static void _slurm_rpc_layout_show(slurm_msg_t * msg)
{
	layout_info_request_msg_t *layout_req_msg = (layout_info_request_msg_t *)
		msg->data;
	DEF_TIMERS;
	slurm_msg_t response_msg;
	char *dump;
	int dump_size;
	static int high_buffer_size = (1024 * 1024);
	Buf buffer = init_buf(high_buffer_size);

	START_TIMER;
	debug2("Processing RPC: REQUEST_LAYOUT_INFO");
	if (layout_req_msg->layout_type == NULL) {
		dump = slurm_get_layouts();
		pack32((uint32_t) 2, buffer);	/* 1 record + trailing \n */
		packstr(dump, buffer);
		packstr("\n", buffer); /* to be consistent with
					* layouts internal prints */
		xfree(dump);
	} else {
		if ( layouts_pack_layout(layout_req_msg->layout_type,
					 layout_req_msg->entities,
					 layout_req_msg->type,
					 layout_req_msg->flags,
					 buffer) != SLURM_SUCCESS) {
			debug2("%s: unable to get layout[%s]",
			       __func__, layout_req_msg->layout_type);
			slurm_send_rc_msg(msg, SLURM_NO_CHANGE_IN_DATA);
			free_buf(buffer);
			return;
		}
	}

	dump_size = get_buf_offset(buffer);
	high_buffer_size = MAX(high_buffer_size, dump_size);
	dump = xfer_buf_data(buffer);
	END_TIMER2("_slurm_rpc_resv_show");

	/* init response_msg structure */
	slurm_msg_t_init(&response_msg);
	response_msg.flags = msg->flags;
	response_msg.protocol_version = msg->protocol_version;
	response_msg.address = msg->address;
	response_msg.conn = msg->conn;
	response_msg.msg_type = RESPONSE_LAYOUT_INFO;
	response_msg.data = dump;
	response_msg.data_size = dump_size;

	/* send message */
	slurm_send_node_msg(msg->conn_fd, &response_msg);
	xfree(dump);
}


static int _wait_for_starting_step(uint32_t job_id, uint32_t step_id)
{
	static pthread_mutex_t dummy_lock = PTHREAD_MUTEX_INITIALIZER;
	struct timespec ts = {0, 0};
	struct timeval now;

	starting_step_t starting_step;
	int num_passes = 0;

	starting_step.job_id  = job_id;
	starting_step.step_id = step_id;

	while (list_find_first( conf->starting_steps,
				&_compare_starting_steps,
				&starting_step )) {
		if (num_passes == 0) {
			if (step_id != NO_VAL)
				debug( "Blocked waiting for step %d.%d",
					job_id, step_id);
			else
				debug( "Blocked waiting for job %d, all steps",
					job_id);
		}
		num_passes++;

		gettimeofday(&now, NULL);
		ts.tv_sec = now.tv_sec+1;
		ts.tv_nsec = now.tv_usec * 1000;

		slurm_mutex_lock(&dummy_lock);
		slurm_cond_timedwait(&conf->starting_steps_cond,
				     &dummy_lock, &ts);
		slurm_mutex_unlock(&dummy_lock);
	}
	if (num_passes > 0) {
		if (step_id != NO_VAL)
			debug( "Finished wait for step %d.%d",
				job_id, step_id);
		else
			debug( "Finished wait for job %d, all steps",
				job_id);
	}

	return SLURM_SUCCESS;
}


_rpc_batch_job(slurm_msg_t *msg, bool new_msg)
{
	batch_job_launch_msg_t *req = (batch_job_launch_msg_t *)msg->data;
	bool     first_job_run;
	int      rc = SLURM_SUCCESS;
	bool	 replied = false, revoked;
	slurm_addr_t *cli = &msg->orig_addr;

	if (new_msg) {
		uid_t req_uid = g_slurm_auth_get_uid(msg->auth_cred,
						     conf->auth_info);
		if (!_slurm_authorized_user(req_uid)) {
			error("Security violation, batch launch RPC from uid %d",
			      req_uid);
			rc = ESLURM_USER_ID_MISSING;  /* or bad in this case */
			goto done;
		}
	}

	if (_launch_job_test(req->job_id)) {
		error("Job %u already running, do not launch second copy",
		      req->job_id);
		rc = ESLURM_DUPLICATE_JOB_ID;	/* job already running */
		_launch_job_fail(req->job_id, rc);
		goto done;
	}

	slurm_cred_handle_reissue(conf->vctx, req->cred);
	if (slurm_cred_revoked(conf->vctx, req->cred)) {
		error("Job %u already killed, do not launch batch job",
		      req->job_id);
		rc = ESLURMD_CREDENTIAL_REVOKED;	/* job already ran */
		goto done;
	}

	task_g_slurmd_batch_request(req->job_id, req);	/* determine task affinity */

	if (!req->user_name)
		req->user_name = uid_to_string(req->uid);

	slurm_mutex_lock(&prolog_mutex);
	first_job_run = !slurm_cred_jobid_cached(conf->vctx, req->job_id);

	/* BlueGene prolog waits for partition boot and is very slow.
	 * On any system we might need to load environment variables
	 * for Moab (see --get-user-env), which could also be slow.
	 * Just reply now and send a separate kill job request if the
	 * prolog or launch fail. */
	replied = true;
	if (new_msg && (slurm_send_rc_msg(msg, rc) < 1)) {
		/* The slurmctld is no longer waiting for a reply.
		 * This typically indicates that the slurmd was
		 * blocked from memory and/or CPUs and the slurmctld
		 * has requeued the batch job request. */
		error("Could not confirm batch launch for job %u, "
		      "aborting request", req->job_id);
		rc = SLURM_COMMUNICATIONS_SEND_ERROR;
		slurm_mutex_unlock(&prolog_mutex);
		goto done;
	}

	/*
	 * Insert jobid into credential context to denote that
	 * we've now "seen" an instance of the job
	 */
	if (first_job_run) {
		job_env_t job_env;
		slurm_cred_insert_jobid(conf->vctx, req->job_id);
		_add_job_running_prolog(req->job_id);
		slurm_mutex_unlock(&prolog_mutex);

		memset(&job_env, 0, sizeof(job_env_t));

		job_env.jobid = req->job_id;
		job_env.step_id = req->step_id;
		job_env.node_list = req->nodes;
		job_env.partition = req->partition;
		job_env.spank_job_env = req->spank_job_env;
		job_env.spank_job_env_size = req->spank_job_env_size;
		job_env.uid = req->uid;
		job_env.user_name = req->user_name;
		/*
	 	 * Run job prolog on this node
	 	 */
#if defined(HAVE_BG)
		select_g_select_jobinfo_get(req->select_jobinfo,
					    SELECT_JOBDATA_BLOCK_ID,
					    &job_env.resv_id);
#elif defined(HAVE_ALPS_CRAY)
		job_env.resv_id = select_g_select_jobinfo_xstrdup(
			req->select_jobinfo, SELECT_PRINT_RESV_ID);
#endif
		if (container_g_create(req->job_id))
			error("container_g_create(%u): %m", req->job_id);
		rc = _run_prolog(&job_env, req->cred);
		xfree(job_env.resv_id);
		if (rc) {
			int term_sig, exit_status;
			if (WIFSIGNALED(rc)) {
				exit_status = 0;
				term_sig    = WTERMSIG(rc);
			} else {
				exit_status = WEXITSTATUS(rc);
				term_sig    = 0;
			}
			error("[job %u] prolog failed status=%d:%d",
			      req->job_id, exit_status, term_sig);
			_prolog_error(req, rc);
			rc = ESLURMD_PROLOG_FAILED;
			goto done;
		}
	} else {
		slurm_mutex_unlock(&prolog_mutex);
		_wait_for_job_running_prolog(req->job_id);
	}

	if (_get_user_env(req) < 0) {
		bool requeue = _requeue_setup_env_fail();
		if (requeue) {
			rc = ESLURMD_SETUP_ENVIRONMENT_ERROR;
			goto done;
		}
	}
	_set_batch_job_limits(msg);

	/* Since job could have been killed while the prolog was
	 * running (especially on BlueGene, which can take minutes
	 * for partition booting). Test if the credential has since
	 * been revoked and exit as needed. */
	if (slurm_cred_revoked(conf->vctx, req->cred)) {
		info("Job %u already killed, do not launch batch job",
		     req->job_id);
		rc = ESLURMD_CREDENTIAL_REVOKED;     /* job already ran */
		goto done;
	}

	slurm_mutex_lock(&launch_mutex);
	if (req->step_id == SLURM_BATCH_SCRIPT)
		info("Launching batch job %u for UID %d",
		     req->job_id, req->uid);
	else
		info("Launching batch job %u.%u for UID %d",
		     req->job_id, req->step_id, req->uid);

	debug3("_rpc_batch_job: call to _forkexec_slurmstepd");
	rc = _forkexec_slurmstepd(LAUNCH_BATCH_JOB, (void *)req, cli, NULL,
				  (hostset_t)NULL, SLURM_PROTOCOL_VERSION);
	debug3("_rpc_batch_job: return from _forkexec_slurmstepd: %d", rc);

	slurm_mutex_unlock(&launch_mutex);
	_launch_complete_add(req->job_id);

	/* On a busy system, slurmstepd may take a while to respond,
	 * if the job was cancelled in the interim, run through the
	 * abort logic below. */
	revoked = slurm_cred_revoked(conf->vctx, req->cred);
	if (revoked)
		_launch_complete_rm(req->job_id);
	if (revoked && _is_batch_job_finished(req->job_id)) {
		/* If configured with select/serial and the batch job already
		 * completed, consider the job sucessfully launched and do
		 * not repeat termination logic below, which in the worst case
		 * just slows things down with another message. */
		revoked = false;
	}
	if (revoked) {
		info("Job %u killed while launch was in progress",
		     req->job_id);
		sleep(1);	/* give slurmstepd time to create
				 * the communication socket */
		_terminate_all_steps(req->job_id, true);
		rc = ESLURMD_CREDENTIAL_REVOKED;
		goto done;
	}

done:
	if (!replied) {
		if (new_msg && (slurm_send_rc_msg(msg, rc) < 1)) {
			/* The slurmctld is no longer waiting for a reply.
			 * This typically indicates that the slurmd was
			 * blocked from memory and/or CPUs and the slurmctld
			 * has requeued the batch job request. */
			error("Could not confirm batch launch for job %u, "
			      "aborting request", req->job_id);
			rc = SLURM_COMMUNICATIONS_SEND_ERROR;
		} else {
			/* No need to initiate separate reply below */
			rc = SLURM_SUCCESS;
		}
	}
	if (rc != SLURM_SUCCESS) {
		/* prolog or job launch failure,
		 * tell slurmctld that the job failed */
		if (req->step_id == SLURM_BATCH_SCRIPT)
			_launch_job_fail(req->job_id, rc);
		else
			_abort_step(req->job_id, req->step_id);
	}

	/*
	 *  If job prolog failed or we could not reply,
	 *  initiate message to slurmctld with current state
	 */
	if ((rc == ESLURMD_PROLOG_FAILED)
	    || (rc == SLURM_COMMUNICATIONS_SEND_ERROR)
	    || (rc == ESLURMD_SETUP_ENVIRONMENT_ERROR)) {
		send_registration_msg(rc, false);
	}
}


static int _get_node_inx(char *hostlist)
{
	char *host;
	int node_inx = -1;
	hostset_t hset;

	if (!conf->node_name)
		return node_inx;

	if ((hset = hostset_create(hostlist))) {
		int inx = 0;
		while ((host = hostset_shift(hset))) {
			if (!strcmp(host, conf->node_name)) {
				node_inx = inx;
				free(host);
				break;
			}
			inx++;
			free(host);
		}
		hostset_destroy(hset);
	}
	return node_inx;
}


_check_job_credential(launch_tasks_request_msg_t *req, uid_t uid,
		      int node_id, hostset_t *step_hset,
		      uint16_t protocol_version)
{
	slurm_cred_arg_t arg;
	hostset_t	s_hset = NULL;
	bool		user_ok = _slurm_authorized_user(uid);
	bool		verified = true;
	int		host_index = -1;
	int		rc;
	slurm_cred_t    *cred = req->cred;
	uint32_t	jobid = req->job_id;
	uint32_t	stepid = req->job_step_id;
	int		tasks_to_launch = req->tasks_to_launch[node_id];
	uint32_t	job_cpus = 0, step_cpus = 0;

	/*
	 * First call slurm_cred_verify() so that all valid
	 * credentials are checked
	 */
	rc = slurm_cred_verify(conf->vctx, cred, &arg, protocol_version);
	if (rc < 0) {
		verified = false;
		if ((!user_ok) || (errno != ESLURMD_INVALID_JOB_CREDENTIAL))
			return SLURM_ERROR;
		else {
			debug("_check_job_credential slurm_cred_verify failed:"
			      " %m, but continuing anyway.");
		}
	}

	/* If uid is the SlurmUser or root and the credential is bad,
	 * then do not attempt validating the credential */
	if (!verified) {
		*step_hset = NULL;
		if (rc >= 0) {
			if ((s_hset = hostset_create(arg.step_hostlist)))
				*step_hset = s_hset;
			slurm_cred_free_args(&arg);
		}
		return SLURM_SUCCESS;
	}

	if ((arg.jobid != jobid) || (arg.stepid != stepid)) {
		error("job credential for %u.%u, expected %u.%u",
		      arg.jobid, arg.stepid, jobid, stepid);
		goto fail;
	}

	if (arg.uid != uid) {
		error("job credential created for uid %ld, expected %ld",
		      (long) arg.uid, (long) uid);
		goto fail;
	}

	/*
	 * Check that credential is valid for this host
	 */
	if (!(s_hset = hostset_create(arg.step_hostlist))) {
		error("Unable to parse credential hostlist: `%s'",
		      arg.step_hostlist);
		goto fail;
	}

	if (!hostset_within(s_hset, conf->node_name)) {
		error("Invalid job %u.%u credential for user %u: "
		      "host %s not in hostset %s",
		      arg.jobid, arg.stepid, arg.uid,
		      conf->node_name, arg.step_hostlist);
		goto fail;
	}

	if ((arg.job_nhosts > 0) && (tasks_to_launch > 0)) {
		uint32_t hi, i, i_first_bit=0, i_last_bit=0, j;
		bool cpu_log = slurm_get_debug_flags() & DEBUG_FLAG_CPU_BIND;

#ifdef HAVE_FRONT_END
		host_index = 0;	/* It is always 0 for front end systems */
#else
		hostset_t j_hset;
		/* Determine the CPU count based upon this node's index into
		 * the _job's_ allocation (job's hostlist and core_bitmap) */
		if (!(j_hset = hostset_create(arg.job_hostlist))) {
			error("Unable to parse credential hostlist: `%s'",
			      arg.job_hostlist);
			goto fail;
		}
		host_index = hostset_find(j_hset, conf->node_name);
		hostset_destroy(j_hset);

		if ((host_index < 0) || (host_index >= arg.job_nhosts)) {
			error("job cr credential invalid host_index %d for "
			      "job %u", host_index, arg.jobid);
			goto fail;
		}
#endif

		if (cpu_log) {
			char *per_job = "", *per_step = "";
			uint64_t job_mem  = arg.job_mem_limit;
			uint64_t step_mem = arg.step_mem_limit;
			if (job_mem & MEM_PER_CPU) {
				job_mem &= (~MEM_PER_CPU);
				per_job = "_per_CPU";
			}
			if (step_mem & MEM_PER_CPU) {
				step_mem &= (~MEM_PER_CPU);
				per_step = "_per_CPU";
			}
			info("====================");
			info("step_id:%u.%u job_mem:%"PRIu64"MB%s "
			     "step_mem:%"PRIu64"MB%s",
			     arg.jobid, arg.stepid, job_mem, per_job,
			     step_mem, per_step);
		}

		hi = host_index + 1;	/* change from 0-origin to 1-origin */
		for (i=0; hi; i++) {
			if (hi > arg.sock_core_rep_count[i]) {
				i_first_bit += arg.sockets_per_node[i] *
					       arg.cores_per_socket[i] *
					       arg.sock_core_rep_count[i];
				hi -= arg.sock_core_rep_count[i];
			} else {
				i_first_bit += arg.sockets_per_node[i] *
					       arg.cores_per_socket[i] *
					       (hi - 1);
				i_last_bit = i_first_bit +
					     arg.sockets_per_node[i] *
					     arg.cores_per_socket[i];
				break;
			}
		}
		/* Now count the allocated processors */
		for (i=i_first_bit, j=0; i<i_last_bit; i++, j++) {
			char *who_has = NULL;
			if (bit_test(arg.job_core_bitmap, i)) {
				job_cpus++;
				who_has = "Job";
			}
			if (bit_test(arg.step_core_bitmap, i)) {
				step_cpus++;
				who_has = "Step";
			}
			if (cpu_log && who_has) {
				info("JobNode[%u] CPU[%u] %s alloc",
				     host_index, j, who_has);
			}
		}
		if (cpu_log)
			info("====================");
		if (step_cpus == 0) {
			error("cons_res: zero processors allocated to step");
			step_cpus = 1;
		}
		/* NOTE: step_cpus is the count of allocated resources
		 * (typically cores). Convert to CPU count as needed */
		if (i_last_bit <= i_first_bit)
			error("step credential has no CPUs selected");
		else {
			i = conf->cpus / (i_last_bit - i_first_bit);
			if (i > 1) {
				if (cpu_log)
					info("Scaling CPU count by factor of "
					     "%d (%u/(%u-%u))",
					     i, conf->cpus,
					     i_last_bit, i_first_bit);
				step_cpus *= i;
				job_cpus *= i;
			}
		}
		if (tasks_to_launch > step_cpus) {
			/* This is expected with the --overcommit option
			 * or hyperthreads */
			debug("cons_res: More than one tasks per logical "
			      "processor (%d > %u) on host [%u.%u %ld %s] ",
			      tasks_to_launch, step_cpus, arg.jobid,
			      arg.stepid, (long) arg.uid, arg.step_hostlist);
		}
	} else {
		step_cpus = 1;
		job_cpus  = 1;
	}

	/* Overwrite any memory limits in the RPC with contents of the
	 * memory limit within the credential.
	 * Reset the CPU count on this node to correct value. */
	if (arg.step_mem_limit) {
		if (arg.step_mem_limit & MEM_PER_CPU) {
			req->step_mem_lim  = arg.step_mem_limit &
					     (~MEM_PER_CPU);
			req->step_mem_lim *= step_cpus;
		} else
			req->step_mem_lim  = arg.step_mem_limit;
	} else {
		if (arg.job_mem_limit & MEM_PER_CPU) {
			req->step_mem_lim  = arg.job_mem_limit &
					     (~MEM_PER_CPU);
			req->step_mem_lim *= job_cpus;
		} else
			req->step_mem_lim  = arg.job_mem_limit;
	}
	if (arg.job_mem_limit & MEM_PER_CPU) {
		req->job_mem_lim  = arg.job_mem_limit & (~MEM_PER_CPU);
		req->job_mem_lim *= job_cpus;
	} else
		req->job_mem_lim  = arg.job_mem_limit;
	req->job_core_spec = arg.job_core_spec;
	req->node_cpus = step_cpus;
#if 0
	info("%u.%u node_id:%d mem orig:%"PRIu64" cpus:%u limit:%"PRIu64"",
	     jobid, stepid, node_id, arg.job_mem_limit,
	     step_cpus, req->job_mem_lim);
#endif

	*step_hset = s_hset;
	slurm_cred_free_args(&arg);
	return SLURM_SUCCESS;

    fail:
	if (s_hset)
		hostset_destroy(s_hset);
	*step_hset = NULL;
	slurm_cred_free_args(&arg);
	slurm_seterrno_ret(ESLURMD_INVALID_JOB_CREDENTIAL);
}


static int _rpc_file_bcast(slurm_msg_t *msg)
{
	int rc;
	int64_t offset, inx;
	file_bcast_info_t *file_info;
	file_bcast_msg_t *req = msg->data;
	file_bcast_info_t key;

	key.uid = g_slurm_auth_get_uid(msg->auth_cred, conf->auth_info);
	key.gid = g_slurm_auth_get_gid(msg->auth_cred, conf->auth_info);
	key.fname = req->fname;

	rc = _valid_sbcast_cred(req, key.uid, req->block_no, &key.job_id);
	if ((rc != SLURM_SUCCESS) && !_slurm_authorized_user(key.uid))
		return rc;

	/* cannot trust, so discard */
	xfree(req->user_name);

#if 0
	info("last_block=%u force=%u modes=%o",
	     req->last_block, req->force, req->modes);
	info("uid=%u gid=%u atime=%lu mtime=%lu block_len[0]=%u",
	     req->uid, req->gid, req->atime, req->mtime, req->block_len);
#if 0
	/* when the file being transferred is binary, the following line
	 * can break the terminal output for slurmd */
	info("req->block[0]=%s, @ %lu", \
	     req->block[0], (unsigned long) &req->block);
#endif
#endif

	if (req->block_no == 1) {
		info("sbcast req_uid=%u job_id=%u fname=%s block_no=%u",
		     key.uid, key.job_id, key.fname, req->block_no);
	} else {
		debug("sbcast req_uid=%u job_id=%u fname=%s block_no=%u",
		      key.uid, key.job_id, key.fname, req->block_no);
	}

	/* first block must register the file and open fd/mmap */
	if (req->block_no == 1) {
		if ((rc = _file_bcast_register_file(msg, &key)))
			return rc;
	}

	_fb_rdlock();
	if (!(file_info = _bcast_lookup_file(&key))) {
		error("No registered file transfer for uid %u file `%s`.",
		      key.uid, key.fname);
		_fb_rdunlock();
		return SLURM_ERROR;
	}

	/* now decompress file */
	if (bcast_decompress_data(req) < 0) {
		error("sbcast: data decompression error for UID %u, file %s",
		      key.uid, key.fname);
		_fb_rdunlock();
		return SLURM_FAILURE;
	}

	offset = 0;
	while (req->block_len - offset) {
		inx = write(file_info->fd, &req->block[offset],
			    (req->block_len - offset));
		if (inx == -1) {
			if ((errno == EINTR) || (errno == EAGAIN))
				continue;
			error("sbcast: uid:%u can't write `%s`: %m",
			      key.uid, key.fname);
			_fb_rdunlock();
			return SLURM_FAILURE;
		}
		offset += inx;
	}

	file_info->last_update = time(NULL);

	if (req->last_block && fchmod(file_info->fd, (req->modes & 0777))) {
		error("sbcast: uid:%u can't chmod `%s`: %m",
		      key.uid, key.fname);
	}
	if (req->last_block && fchown(file_info->fd, key.uid, key.gid)) {
		error("sbcast: uid:%u gid:%u can't chown `%s`: %m",
		      key.uid, key.gid, key.fname);
	}
	if (req->last_block && req->atime) {
		struct utimbuf time_buf;
		time_buf.actime  = req->atime;
		time_buf.modtime = req->mtime;
		if (utime(key.fname, &time_buf)) {
			error("sbcast: uid:%u can't utime `%s`: %m",
			      key.uid, key.fname);
		}
	}

	_fb_rdunlock();

	if (req->last_block) {
		_file_bcast_close_file(&key);
	}
	return SLURM_SUCCESS;
}


static void shutdown_busid(struct bus_id_priv *busid_priv)
{
	if (busid_priv->sdev && !busid_priv->shutdown_busid) {
		busid_priv->shutdown_busid = 1;
		usbip_event_add(&busid_priv->sdev->ud, SDEV_EVENT_REMOVED);

		/* wait for the stop of the event handler */
		usbip_stop_eh(&busid_priv->sdev->ud);
	}
}


static void stub_device_rebind(void)
{
#if IS_MODULE(CONFIG_USBIP_HOST)
	struct bus_id_priv *busid_priv;
	int i;

	/* update status to STUB_BUSID_OTHER so probe ignores the device */
	spin_lock(&busid_table_lock);
	for (i = 0; i < MAX_BUSID; i++) {
		if (busid_table[i].name[0] &&
		    busid_table[i].shutdown_busid) {
			busid_priv = &(busid_table[i]);
			busid_priv->status = STUB_BUSID_OTHER;
		}
	}
	spin_unlock(&busid_table_lock);

	/* now run rebind - no need to hold locks. driver files are removed */
	for (i = 0; i < MAX_BUSID; i++) {
		if (busid_table[i].name[0] &&
		    busid_table[i].shutdown_busid) {
			busid_priv = &(busid_table[i]);
			do_rebind(busid_table[i].name, busid_priv);
		}
	}
#endif
}


static int stub_resume(struct usb_device *udev, pm_message_t message)
{
	dev_dbg(&udev->dev, "stub_resume\n");

	return 0;
}


static void stub_device_unusable(struct usbip_device *ud)
{
	spin_lock_irq(&ud->lock);
	ud->status = SDEV_ST_ERROR;
	spin_unlock_irq(&ud->lock);
}


hyphenHashFree(HyphenHashTab *hashTab) {
	int i;
	HyphenHashEntry *e, *next;
	for (i = 0; i < HYPHENHASHSIZE; i++)
		for (e = hashTab->entries[i]; e; e = next) {
			next = e->next;
			free(e->key);
			free(e);
		}
	free(hashTab);
}


lou_version(void) {
	static char *version = PACKAGE_VERSION;
	return version;
}


_lou_extParseDots(const char *inString, widechar *outString) {
	/* Parse external dot patterns */
	CharsString wideIn;
	CharsString result;
	int k;
	for (k = 0; inString[k] && k < MAXSTRING - 1; k++) wideIn.chars[k] = inString[k];
	wideIn.chars[k] = 0;
	wideIn.length = k;
	parseDots(NULL, &result, &wideIn);
	if (errorCount) {
		errorCount = 0;
		return 0;
	}
	for (k = 0; k < result.length; k++) outString[k] = result.chars[k];
	outString[k] = 0;
	return result.length;
}


_lou_getCharFromDots(widechar d) {
	CharOrDots *cdPtr = getCharOrDots(d, 1, gTable);
	if (cdPtr) return cdPtr->found;
	return ' ';
}


free_tablefiles(char **tables) {
	char **table;
	if (!tables) return;
	for (table = tables; *table; table++) free(*table);
	free(tables);
}


compile_findCharOrDots(widechar c, int m, TranslationTableHeader *table) {
	/* Look up a character or dot pattern. If m is 0 look up a character,
	 * otherwise look up a dot pattern. Although the algorithms are almost
	 * identical, different tables are needed for characters and dots because
	 * of the possibility of conflicts. */
	TranslationTableCharacter *character;
	TranslationTableOffset bucket;
	unsigned long int makeHash = (unsigned long int)c % HASHNUM;
	if (m == 0)
		bucket = table->characters[makeHash];
	else
		bucket = table->dots[makeHash];
	while (bucket) {
		character = (TranslationTableCharacter *)&table->ruleArea[bucket];
		if (character->realchar == c) return character;
		bucket = character->next;
	}
	return NULL;
}


findCharacterClass(const CharsString *name, CharacterClass *characterClasses) {
	/* Find a character class, whether predefined or user-defined */
	const CharacterClass *class = characterClasses;
	while (class) {
		if ((name->length == class->length) &&
				(memcmp(&name->chars[0], class->name, CHARSIZE * name->length) == 0))
			return class;
		class = class->next;
	}
	return NULL;
}


parseDots(FileInfo *nested, CharsString *cells, const CharsString *token) {
	/* get dot patterns */
	widechar cell = 0; /* assembly place for dots */
	int cellCount = 0;
	int index;
	int start = 0;

	for (index = 0; index < token->length; index++) {
		int started = index != start;
		widechar character = token->chars[index];
		switch (character) { /* or dots to make up Braille cell */
			{
				int dot;
			case '1':
				dot = B1;
				goto haveDot;
			case '2':
				dot = B2;
				goto haveDot;
			case '3':
				dot = B3;
				goto haveDot;
			case '4':
				dot = B4;
				goto haveDot;
			case '5':
				dot = B5;
				goto haveDot;
			case '6':
				dot = B6;
				goto haveDot;
			case '7':
				dot = B7;
				goto haveDot;
			case '8':
				dot = B8;
				goto haveDot;
			case '9':
				dot = B9;
				goto haveDot;
			case 'a':
			case 'A':
				dot = B10;
				goto haveDot;
			case 'b':
			case 'B':
				dot = B11;
				goto haveDot;
			case 'c':
			case 'C':
				dot = B12;
				goto haveDot;
			case 'd':
			case 'D':
				dot = B13;
				goto haveDot;
			case 'e':
			case 'E':
				dot = B14;
				goto haveDot;
			case 'f':
			case 'F':
				dot = B15;
			haveDot:
				if (started && !cell) goto invalid;
				if (cell & dot) {
					compileError(nested, "dot specified more than once.");
					return 0;
				}
				cell |= dot;
				break;
			}
		case '0': /* blank */
			if (started) goto invalid;
			break;
		case '-': /* got all dots for this cell */
			if (!started) {
				compileError(nested, "missing cell specification.");
				return 0;
			}
			cells->chars[cellCount++] = cell | B16;
			cell = 0;
			start = index + 1;
			break;
		default:
		invalid:
			compileError(
					nested, "invalid dot number %s.", _lou_showString(&character, 1));
			return 0;
		}
	}
	if (index == start) {
		compileError(nested, "missing cell specification.");
		return 0;
	}
	cells->chars[cellCount++] = cell | B16; /* last cell */
	cells->length = cellCount;
	return 1;
}


lou_readCharFromFile(const char *fileName, int *mode) {
	/* Read a character from a file, whether big-endian, little-endian or
	 * ASCII8 */
	int ch;
	static FileInfo nested;
	if (fileName == NULL) return 0;
	if (*mode == 1) {
		*mode = 0;
		nested.fileName = fileName;
		nested.encoding = noEncoding;
		nested.status = 0;
		nested.lineNumber = 0;
		if (!(nested.in = fopen(nested.fileName, "r"))) {
			_lou_logMessage(LOG_ERROR, "Cannot open file '%s'", nested.fileName);
			*mode = 1;
			return EOF;
		}
	}
	if (nested.in == NULL) {
		*mode = 1;
		return EOF;
	}
	ch = getAChar(&nested);
	if (ch == EOF) {
		fclose(nested.in);
		nested.in = NULL;
		*mode = 1;
	}
	return ch;
}


compileWarning(FileInfo *nested, const char *format, ...) {
#ifndef __SYMBIAN32__
	char buffer[MAXSTRING];
	va_list arguments;
	va_start(arguments, format);
	vsnprintf(buffer, sizeof(buffer), format, arguments);
	va_end(arguments);
	if (nested)
		_lou_logMessage(LOG_WARN, "%s:%d: warning: %s", nested->fileName,
				nested->lineNumber, buffer);
	else
		_lou_logMessage(LOG_WARN, "warning: %s", buffer);
	warningCount++;
#endif
}


  static int handle_error(Sass_Context* c_ctx) {
    try {
      throw;
    }
    catch (Exception::Base& e) {
      std::stringstream msg_stream;
      std::string cwd(Sass::File::get_cwd());
      std::string msg_prefix(e.errtype());
      bool got_newline = false;
      msg_stream << msg_prefix << ": ";
      const char* msg = e.what();
      while (msg && *msg) {
        if (*msg == '\r') {
          got_newline = true;
        }
        else if (*msg == '\n') {
          got_newline = true;
        }
        else if (got_newline) {
          msg_stream << std::string(msg_prefix.size() + 2, ' ');
          got_newline = false;
        }
        msg_stream << *msg;
        ++msg;
      }
      if (!got_newline) msg_stream << "\n";

      if (e.traces.empty()) {
        // we normally should have some traces, still here as a fallback
        std::string rel_path(Sass::File::abs2rel(e.pstate.path, cwd, cwd));
        msg_stream << std::string(msg_prefix.size() + 2, ' ');
        msg_stream << " on line " << e.pstate.line + 1 << " of " << rel_path << "\n";
      }
      else {
        std::string rel_path(Sass::File::abs2rel(e.pstate.path, cwd, cwd));
        msg_stream << traces_to_string(e.traces, "        ");
      }

      // now create the code trace (ToDo: maybe have util functions?)
      if (e.pstate.line != std::string::npos &&
          e.pstate.column != std::string::npos &&
          e.pstate.src != nullptr) {
        size_t lines = e.pstate.line;
        // scan through src until target line
        // move line_beg pointer to line start
        const char* line_beg;
        for (line_beg = e.pstate.src; *line_beg != '\0'; ++line_beg) {
          if (lines == 0) break;
          if (*line_beg == '\n') --lines;
        }
        // move line_end before next newline character
        const char* line_end;
        for (line_end = line_beg; *line_end != '\0'; ++line_end) {
          if (*line_end == '\n' || *line_end == '\r') break;
        }
        if (*line_end != '\0') ++line_end;
        size_t line_len = line_end - line_beg;
        size_t move_in = 0; size_t shorten = 0;
        size_t left_chars = 42; size_t max_chars = 76;
        // reported excerpt should not exceed `max_chars` chars
        if (e.pstate.column > line_len) left_chars = e.pstate.column;
        if (e.pstate.column > left_chars) move_in = e.pstate.column - left_chars;
        if (line_len > max_chars + move_in) shorten = line_len - move_in - max_chars;
        utf8::advance(line_beg, move_in, line_end);
        utf8::retreat(line_end, shorten, line_beg);
        std::string sanitized; std::string marker(e.pstate.column - move_in, '-');
        utf8::replace_invalid(line_beg, line_end, std::back_inserter(sanitized));
        msg_stream << ">> " << sanitized << "\n";
        msg_stream << "   " << marker << "^\n";
      }

      JsonNode* json_err = json_mkobject();
      json_append_member(json_err, "status", json_mknumber(1));
      json_append_member(json_err, "file", json_mkstring(e.pstate.path));
      json_append_member(json_err, "line", json_mknumber((double)(e.pstate.line + 1)));
      json_append_member(json_err, "column", json_mknumber((double)(e.pstate.column + 1)));
      json_append_member(json_err, "message", json_mkstring(e.what()));
      json_append_member(json_err, "formatted", json_mkstream(msg_stream));
      try { c_ctx->error_json = json_stringify(json_err, "  "); }
      catch (...) {}
      c_ctx->error_message = sass_copy_string(msg_stream.str());
      c_ctx->error_text = sass_copy_c_string(e.what());
      c_ctx->error_status = 1;
      c_ctx->error_file = sass_copy_c_string(e.pstate.path);
      c_ctx->error_line = e.pstate.line + 1;
      c_ctx->error_column = e.pstate.column + 1;
      c_ctx->error_src = e.pstate.src;
      c_ctx->output_string = 0;
      c_ctx->source_map_string = 0;
      json_delete(json_err);
    }
    catch (std::bad_alloc& ba) {
      std::stringstream msg_stream;
      JsonNode* json_err = json_mkobject();
      msg_stream << "Unable to allocate memory: " << ba.what() << std::endl;
      json_append_member(json_err, "status", json_mknumber(2));
      json_append_member(json_err, "message", json_mkstring(ba.what()));
      json_append_member(json_err, "formatted", json_mkstream(msg_stream));
      try { c_ctx->error_json = json_stringify(json_err, "  "); }
      catch (...) {}
      c_ctx->error_message = sass_copy_string(msg_stream.str());
      c_ctx->error_text = sass_copy_c_string(ba.what());
      c_ctx->error_status = 2;
      c_ctx->output_string = 0;
      c_ctx->source_map_string = 0;
      json_delete(json_err);
    }
    catch (std::exception& e) {
      std::stringstream msg_stream;
      JsonNode* json_err = json_mkobject();
      msg_stream << "Internal Error: " << e.what() << std::endl;
      json_append_member(json_err, "status", json_mknumber(3));
      json_append_member(json_err, "message", json_mkstring(e.what()));
      json_append_member(json_err, "formatted", json_mkstream(msg_stream));
      try { c_ctx->error_json = json_stringify(json_err, "  "); }
      catch (...) {}
      c_ctx->error_message = sass_copy_string(msg_stream.str());
      c_ctx->error_text = sass_copy_c_string(e.what());
      c_ctx->error_status = 3;
      c_ctx->output_string = 0;
      c_ctx->source_map_string = 0;
      json_delete(json_err);
    }
    catch (std::string& e) {
      std::stringstream msg_stream;
      JsonNode* json_err = json_mkobject();
      msg_stream << "Internal Error: " << e << std::endl;
      json_append_member(json_err, "status", json_mknumber(4));
      json_append_member(json_err, "message", json_mkstring(e.c_str()));
      json_append_member(json_err, "formatted", json_mkstream(msg_stream));
      try { c_ctx->error_json = json_stringify(json_err, "  "); }
      catch (...) {}
      c_ctx->error_message = sass_copy_string(msg_stream.str());
      c_ctx->error_text = sass_copy_c_string(e.c_str());
      c_ctx->error_status = 4;
      c_ctx->output_string = 0;
      c_ctx->source_map_string = 0;
      json_delete(json_err);
    }
    catch (const char* e) {
      std::stringstream msg_stream;
      JsonNode* json_err = json_mkobject();
      msg_stream << "Internal Error: " << e << std::endl;
      json_append_member(json_err, "status", json_mknumber(4));
      json_append_member(json_err, "message", json_mkstring(e));
      json_append_member(json_err, "formatted", json_mkstream(msg_stream));
      try { c_ctx->error_json = json_stringify(json_err, "  "); }
      catch (...) {}
      c_ctx->error_message = sass_copy_string(msg_stream.str());
      c_ctx->error_text = sass_copy_c_string(e);
      c_ctx->error_status = 4;
      c_ctx->output_string = 0;
      c_ctx->source_map_string = 0;
      json_delete(json_err);
    }
    catch (...) {
      std::stringstream msg_stream;
      JsonNode* json_err = json_mkobject();
      msg_stream << "Unknown error occurred" << std::endl;
      json_append_member(json_err, "status", json_mknumber(5));
      json_append_member(json_err, "message", json_mkstring("unknown"));
      try { c_ctx->error_json = json_stringify(json_err, "  "); }
      catch (...) {}
      c_ctx->error_message = sass_copy_string(msg_stream.str());
      c_ctx->error_text = sass_copy_c_string("unknown");
      c_ctx->error_status = 5;
      c_ctx->output_string = 0;
      c_ctx->source_map_string = 0;
      json_delete(json_err);
    }
    return c_ctx->error_status;
  }


Sass_Callee_Entry ADDCALL sass_compiler_get_callee_entry(struct Sass_Compiler* compiler, size_t idx) { return &compiler->cpp_ctx->callee_stack[idx]; }


struct Sass_Context* ADDCALL sass_compiler_get_context(struct Sass_Compiler* compiler) { return compiler->c_ctx; }


  Supports_Condition_Obj Parser::parse_supports_condition_in_parens()
  {
    Supports_Condition_Obj interp = parse_supports_interpolation();
    if (interp != 0) return interp;

    if (!lex < exactly <'('> >()) return {};
    lex < css_whitespace >();

    Supports_Condition_Obj cond = parse_supports_condition();
    if (cond != 0) {
      if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration");
    } else {
      cond = parse_supports_declaration();
      if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration");
    }
    lex < css_whitespace >();
    return cond;
  }


  Simple_Selector_Obj Parser::parse_pseudo_selector() {
    if (lex< sequence<
          optional < pseudo_prefix >,
          // we keep the space within the name, strange enough
          // ToDo: refactor output to schedule the space for it
          // or do we really want to keep the real white-space?
          sequence< identifier, optional < block_comment >, exactly<'('> >
        > >())
    {

      std::string name(lexed);
      name.erase(name.size() - 1);
      ParserState p = pstate;

      // specially parse static stuff
      // ToDo: really everything static?
      if (peek_css <
            sequence <
              alternatives <
                static_value,
                binomial
              >,
              optional_css_whitespace,
              exactly<')'>
            >
          >()
      ) {
        lex_css< alternatives < static_value, binomial > >();
        String_Constant_Obj expr = SASS_MEMORY_NEW(String_Constant, pstate, lexed);
        if (lex_css< exactly<')'> >()) {
          expr->can_compress_whitespace(true);
          return SASS_MEMORY_NEW(Pseudo_Selector, p, name, expr);
        }
      }
      else if (Selector_List_Obj wrapped = parse_selector_list(true)) {
        if (wrapped && lex_css< exactly<')'> >()) {
          return SASS_MEMORY_NEW(Wrapped_Selector, p, name, wrapped);
        }
      }

    }
    // EO if pseudo selector

    else if (lex < sequence< optional < pseudo_prefix >, identifier > >()) {
      return SASS_MEMORY_NEW(Pseudo_Selector, pstate, lexed);
    }
    else if(lex < pseudo_prefix >()) {
      css_error("Invalid CSS", " after ", ": expected pseudoclass or pseudoelement, was ");
    }

    css_error("Invalid CSS", " after ", ": expected \")\", was ");

    // unreachable statement
    return {};
  }


  Expression_Obj Parser::lex_interpolation()
  {
    if (lex < interpolant >(true) != NULL) {
      return parse_interpolated_chunk(lexed, true);
    }
    return {};
  }


  void Inspect::operator()(Block_Ptr block)
  {
    if (!block->is_root()) {
      add_open_mapping(block);
      append_scope_opener();
    }
    if (output_style() == NESTED) indentation += block->tabs();
    for (size_t i = 0, L = block->length(); i < L; ++i) {
      (*block)[i]->perform(this);
    }
    if (output_style() == NESTED) indentation -= block->tabs();
    if (!block->is_root()) {
      append_scope_closer();
      add_close_mapping(block);
    }

  }


  void Inspect::operator()(Element_Selector_Ptr s)
  {
    append_token(s->ns_name(), s);
  }


  void Inspect::operator()(Null_Ptr n)
  {
    // output the final token
    append_token("null", n);
  }


  void Inspect::operator()(Id_Selector_Ptr s)
  {
    append_token(s->ns_name(), s);
    if (s->has_line_break()) append_optional_linefeed();
    if (s->has_line_break()) append_indentation();
  }


  Statement_Ptr Expand::operator()(Directive_Ptr a)
  {
    LOCAL_FLAG(in_keyframes, a->is_keyframes());
    Block_Ptr ab = a->block();
    Selector_List_Ptr as = a->selector();
    Expression_Ptr av = a->value();
    selector_stack.push_back(0);
    if (av) av = av->perform(&eval);
    if (as) as = eval(as);
    selector_stack.pop_back();
    Block_Ptr bb = ab ? operator()(ab) : NULL;
    Directive_Ptr aa = SASS_MEMORY_NEW(Directive,
                                  a->pstate(),
                                  a->keyword(),
                                  as,
                                  bb,
                                  av);
    return aa;
  }


  Statement_Ptr Expand::operator()(If_Ptr i)
  {
    Env env(environment(), true);
    env_stack.push_back(&env);
    call_stack.push_back(i);
    Expression_Obj rv = i->predicate()->perform(&eval);
    if (*rv) {
      append_block(i->block());
    }
    else {
      Block_Ptr alt = i->alternative();
      if (alt) append_block(alt);
    }
    call_stack.pop_back();
    env_stack.pop_back();
    return 0;
  }


  Statement_Ptr Expand::operator()(Supports_Block_Ptr f)
  {
    Expression_Obj condition = f->condition()->perform(&eval);
    Supports_Block_Obj ff = SASS_MEMORY_NEW(Supports_Block,
                                       f->pstate(),
                                       Cast<Supports_Condition>(condition),
                                       operator()(f->block()));
    return ff.detach();
  }


  Statement_Ptr Expand::operator()(Mixin_Call_Ptr c)
  {
    if (recursions > maxRecursion) {
      throw Exception::StackError(traces, *c);
    }

    recursions ++;

    Env* env = environment();
    std::string full_name(c->name() + "[m]");
    if (!env->has(full_name)) {
      error("no mixin named " + c->name(), c->pstate(), traces);
    }
    Definition_Obj def = Cast<Definition>((*env)[full_name]);
    Block_Obj body = def->block();
    Parameters_Obj params = def->parameters();

    if (c->block() && c->name() != "@content" && !body->has_content()) {
      error("Mixin \"" + c->name() + "\" does not accept a content block.", c->pstate(), traces);
    }
    Expression_Obj rv = c->arguments()->perform(&eval);
    Arguments_Obj args = Cast<Arguments>(rv);
    std::string msg(", in mixin `" + c->name() + "`");
    traces.push_back(Backtrace(c->pstate(), msg));
    ctx.callee_stack.push_back({
      c->name().c_str(),
      c->pstate().path,
      c->pstate().line + 1,
      c->pstate().column + 1,
      SASS_CALLEE_MIXIN,
      { env }
    });

    Env new_env(def->environment());
    env_stack.push_back(&new_env);
    if (c->block()) {
      // represent mixin content blocks as thunks/closures
      Definition_Obj thunk = SASS_MEMORY_NEW(Definition,
                                          c->pstate(),
                                          "@content",
                                          SASS_MEMORY_NEW(Parameters, c->pstate()),
                                          c->block(),
                                          Definition::MIXIN);
      thunk->environment(env);
      new_env.local_frame()["@content[m]"] = thunk;
    }

    bind(std::string("Mixin"), c->name(), params, args, &new_env, &eval, traces);

    Block_Obj trace_block = SASS_MEMORY_NEW(Block, c->pstate());
    Trace_Obj trace = SASS_MEMORY_NEW(Trace, c->pstate(), c->name(), trace_block);

    env->set_global("is_in_mixin", bool_true);
    if (Block_Ptr pr = block_stack.back()) {
      trace_block->is_root(pr->is_root());
    }
    block_stack.push_back(trace_block);
    for (auto bb : body->elements()) {
      if (Ruleset_Ptr r = Cast<Ruleset>(bb)) {
        r->is_root(trace_block->is_root());
      }
      Statement_Obj ith = bb->perform(this);
      if (ith) trace->block()->append(ith);
    }
    block_stack.pop_back();
    env->del_global("is_in_mixin");

    ctx.callee_stack.pop_back();
    env_stack.pop_back();
    traces.pop_back();

    recursions --;
    return trace.detach();
  }


  void Expand::expand_selector_list(Selector_Obj s, Selector_List_Obj extender) {

    if (Selector_List_Obj sl = Cast<Selector_List>(s)) {
      for (Complex_Selector_Obj complex_selector : sl->elements()) {
        Complex_Selector_Obj tail = complex_selector;
        while (tail) {
          if (tail->head()) for (Simple_Selector_Obj header : tail->head()->elements()) {
            if (Cast<Parent_Selector>(header) == NULL) continue; // skip all others
            std::string sel_str(complex_selector->to_string(ctx.c_options));
            error("Can't extend " + sel_str + ": can't extend parent selectors", header->pstate(), traces);
          }
          tail = tail->tail();
        }
      }
    }

    Selector_List_Obj contextualized = Cast<Selector_List>(s->perform(&eval));
    if (contextualized == false) return;
    for (auto complex_sel : contextualized->elements()) {
      Complex_Selector_Obj c = complex_sel;
      if (!c->head() || c->tail()) {
        std::string sel_str(contextualized->to_string(ctx.c_options));
        error("Can't extend " + sel_str + ": can't extend nested selectors", c->pstate(), traces);
      }
      Compound_Selector_Obj target = c->head();
      if (contextualized->is_optional()) target->is_optional(true);
      for (size_t i = 0, L = extender->length(); i < L; ++i) {
        Complex_Selector_Obj sel = (*extender)[i];
        if (!(sel->head() && sel->head()->length() > 0 &&
            Cast<Parent_Selector>((*sel->head())[0])))
        {
          Compound_Selector_Obj hh = SASS_MEMORY_NEW(Compound_Selector, (*extender)[i]->pstate());
          hh->media_block((*extender)[i]->media_block());
          Complex_Selector_Obj ssel = SASS_MEMORY_NEW(Complex_Selector, (*extender)[i]->pstate());
          ssel->media_block((*extender)[i]->media_block());
          if (sel->has_line_feed()) ssel->has_line_feed(true);
          Parent_Selector_Obj ps = SASS_MEMORY_NEW(Parent_Selector, (*extender)[i]->pstate());
          ps->media_block((*extender)[i]->media_block());
          hh->append(ps);
          ssel->tail(sel);
          ssel->head(hh);
          sel = ssel;
        }
        // if (c->has_line_feed()) sel->has_line_feed(true);
        ctx.subset_map.put(target, std::make_pair(sel, target));
      }
    }

  }


get_16bit_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
/* This version is for reading 16-bit pixels */
{
  tga_source_ptr source = (tga_source_ptr)sinfo;
  register int t;
  register JSAMPROW ptr;
  register JDIMENSION col;

  ptr = source->pub.buffer[0];
  for (col = cinfo->image_width; col > 0; col--) {
    (*source->read_pixel) (source); /* Load next pixel into tga_pixel */
    t = UCH(source->tga_pixel[0]);
    t += UCH(source->tga_pixel[1]) << 8;
    /* We expand 5 bit data to 8 bit sample width.
     * The format of the 16-bit (LSB first) input word is
     *     xRRRRRGGGGGBBBBB
     */
    ptr[2] = (JSAMPLE)c5to8bits[t & 0x1F];
    t >>= 5;
    ptr[1] = (JSAMPLE)c5to8bits[t & 0x1F];
    t >>= 5;
    ptr[0] = (JSAMPLE)c5to8bits[t & 0x1F];
    ptr += 3;
  }
  return 1;
}


get_24bit_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
/* This version is for reading 24-bit pixels */
{
  tga_source_ptr source = (tga_source_ptr)sinfo;
  register JSAMPROW ptr;
  register JDIMENSION col;

  ptr = source->pub.buffer[0];
  for (col = cinfo->image_width; col > 0; col--) {
    (*source->read_pixel) (source); /* Load next pixel into tga_pixel */
    *ptr++ = (JSAMPLE)UCH(source->tga_pixel[2]); /* change BGR to RGB order */
    *ptr++ = (JSAMPLE)UCH(source->tga_pixel[1]);
    *ptr++ = (JSAMPLE)UCH(source->tga_pixel[0]);
  }
  return 1;
}


int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info)
{
	int ret;

	mutex_lock(&register_mutex);
	ret = __snd_rawmidi_info_select(card, info);
	mutex_unlock(&register_mutex);
	return ret;
}


static __poll_t snd_rawmidi_poll(struct file *file, poll_table * wait)
{
	struct snd_rawmidi_file *rfile;
	struct snd_rawmidi_runtime *runtime;
	__poll_t mask;

	rfile = file->private_data;
	if (rfile->input != NULL) {
		runtime = rfile->input->runtime;
		snd_rawmidi_input_trigger(rfile->input, 1);
		poll_wait(file, &runtime->sleep, wait);
	}
	if (rfile->output != NULL) {
		runtime = rfile->output->runtime;
		poll_wait(file, &runtime->sleep, wait);
	}
	mask = 0;
	if (rfile->input != NULL) {
		if (snd_rawmidi_ready(rfile->input))
			mask |= EPOLLIN | EPOLLRDNORM;
	}
	if (rfile->output != NULL) {
		if (snd_rawmidi_ready(rfile->output))
			mask |= EPOLLOUT | EPOLLWRNORM;
	}
	return mask;
}


int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream)
{
	struct snd_rawmidi_runtime *runtime = substream->runtime;
	int result;
	unsigned long flags;

	if (runtime->buffer == NULL) {
		rmidi_dbg(substream->rmidi,
			  "snd_rawmidi_transmit_empty: output is not active!!!\n");
		return 1;
	}
	spin_lock_irqsave(&runtime->lock, flags);
	result = runtime->avail >= runtime->buffer_size;
	spin_unlock_irqrestore(&runtime->lock, flags);
	return result;
}


int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream,
			     struct snd_rawmidi_params * params)
{
	char *newbuf, *oldbuf;
	struct snd_rawmidi_runtime *runtime = substream->runtime;

	snd_rawmidi_drain_input(substream);
	if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) {
		return -EINVAL;
	}
	if (params->avail_min < 1 || params->avail_min > params->buffer_size) {
		return -EINVAL;
	}
	if (params->buffer_size != runtime->buffer_size) {
		newbuf = kmalloc(params->buffer_size, GFP_KERNEL);
		if (!newbuf)
			return -ENOMEM;
		spin_lock_irq(&runtime->lock);
		oldbuf = runtime->buffer;
		runtime->buffer = newbuf;
		runtime->buffer_size = params->buffer_size;
		runtime->appl_ptr = runtime->hw_ptr = 0;
		spin_unlock_irq(&runtime->lock);
		kfree(oldbuf);
	}
	runtime->avail_min = params->avail_min;
	return 0;
}


int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
			      unsigned char *buffer, int count)
{
	struct snd_rawmidi_runtime *runtime = substream->runtime;
	int result;
	unsigned long flags;

	spin_lock_irqsave(&runtime->lock, flags);
	result = __snd_rawmidi_transmit_peek(substream, buffer, count);
	spin_unlock_irqrestore(&runtime->lock, flags);
	return result;
}


static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
				       struct snd_info_buffer *buffer)
{
	struct snd_rawmidi *rmidi;
	struct snd_rawmidi_substream *substream;
	struct snd_rawmidi_runtime *runtime;

	rmidi = entry->private_data;
	snd_iprintf(buffer, "%s\n\n", rmidi->name);
	mutex_lock(&rmidi->open_mutex);
	if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT) {
		list_for_each_entry(substream,
				    &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams,
				    list) {
			snd_iprintf(buffer,
				    "Output %d\n"
				    "  Tx bytes     : %lu\n",
				    substream->number,
				    (unsigned long) substream->bytes);
			if (substream->opened) {
				snd_iprintf(buffer,
				    "  Owner PID    : %d\n",
				    pid_vnr(substream->pid));
				runtime = substream->runtime;
				snd_iprintf(buffer,
				    "  Mode         : %s\n"
				    "  Buffer size  : %lu\n"
				    "  Avail        : %lu\n",
				    runtime->oss ? "OSS compatible" : "native",
				    (unsigned long) runtime->buffer_size,
				    (unsigned long) runtime->avail);
			}
		}
	}
	if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT) {
		list_for_each_entry(substream,
				    &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams,
				    list) {
			snd_iprintf(buffer,
				    "Input %d\n"
				    "  Rx bytes     : %lu\n",
				    substream->number,
				    (unsigned long) substream->bytes);
			if (substream->opened) {
				snd_iprintf(buffer,
					    "  Owner PID    : %d\n",
					    pid_vnr(substream->pid));
				runtime = substream->runtime;
				snd_iprintf(buffer,
					    "  Buffer size  : %lu\n"
					    "  Avail        : %lu\n"
					    "  Overruns     : %lu\n",
					    (unsigned long) runtime->buffer_size,
					    (unsigned long) runtime->avail,
					    (unsigned long) runtime->xruns);
			}
		}
	}
	mutex_unlock(&rmidi->open_mutex);
}


int snd_rawmidi_new(struct snd_card *card, char *id, int device,
		    int output_count, int input_count,
		    struct snd_rawmidi ** rrawmidi)
{
	struct snd_rawmidi *rmidi;
	int err;
	static struct snd_device_ops ops = {
		.dev_free = snd_rawmidi_dev_free,
		.dev_register = snd_rawmidi_dev_register,
		.dev_disconnect = snd_rawmidi_dev_disconnect,
	};

	if (snd_BUG_ON(!card))
		return -ENXIO;
	if (rrawmidi)
		*rrawmidi = NULL;
	rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL);
	if (!rmidi)
		return -ENOMEM;
	rmidi->card = card;
	rmidi->device = device;
	mutex_init(&rmidi->open_mutex);
	init_waitqueue_head(&rmidi->open_wait);
	INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams);
	INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams);

	if (id != NULL)
		strlcpy(rmidi->id, id, sizeof(rmidi->id));

	snd_device_initialize(&rmidi->dev, card);
	rmidi->dev.release = release_rawmidi_device;
	dev_set_name(&rmidi->dev, "midiC%iD%i", card->number, device);

	if ((err = snd_rawmidi_alloc_substreams(rmidi,
						&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT],
						SNDRV_RAWMIDI_STREAM_INPUT,
						input_count)) < 0) {
		snd_rawmidi_free(rmidi);
		return err;
	}
	if ((err = snd_rawmidi_alloc_substreams(rmidi,
						&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT],
						SNDRV_RAWMIDI_STREAM_OUTPUT,
						output_count)) < 0) {
		snd_rawmidi_free(rmidi);
		return err;
	}
	if ((err = snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops)) < 0) {
		snd_rawmidi_free(rmidi);
		return err;
	}
	if (rrawmidi)
		*rrawmidi = rmidi;
	return 0;
}


long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream,
			     unsigned char *buf, long count)
{
	snd_rawmidi_input_trigger(substream, 1);
	return snd_rawmidi_kernel_read1(substream, NULL/*userbuf*/, buf, count);
}


int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream,
			      struct snd_rawmidi_params * params)
{
	char *newbuf, *oldbuf;
	struct snd_rawmidi_runtime *runtime = substream->runtime;

	if (substream->append && substream->use_count > 1)
		return -EBUSY;
	snd_rawmidi_drain_output(substream);
	if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) {
		return -EINVAL;
	}
	if (params->avail_min < 1 || params->avail_min > params->buffer_size) {
		return -EINVAL;
	}
	if (params->buffer_size != runtime->buffer_size) {
		newbuf = kmalloc(params->buffer_size, GFP_KERNEL);
		if (!newbuf)
			return -ENOMEM;
		spin_lock_irq(&runtime->lock);
		oldbuf = runtime->buffer;
		runtime->buffer = newbuf;
		runtime->buffer_size = params->buffer_size;
		runtime->avail = runtime->buffer_size;
		runtime->appl_ptr = runtime->hw_ptr = 0;
		spin_unlock_irq(&runtime->lock);
		kfree(oldbuf);
	}
	runtime->avail_min = params->avail_min;
	substream->active_sensing = !params->no_active_sensing;
	return 0;
}


int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
{
	int rc;

	mutex_lock(&wil->wmi_mutex);
	rc = __wmi_send(wil, cmdid, buf, len);
	mutex_unlock(&wil->wmi_mutex);

	return rc;
}


int wmi_resume(struct wil6210_priv *wil)
{
	int rc;
	struct {
		struct wmi_cmd_hdr wmi;
		struct wmi_traffic_resume_event evt;
	} __packed reply;

	reply.evt.status = WMI_TRAFFIC_RESUME_FAILED;

	rc = wmi_call(wil, WMI_TRAFFIC_RESUME_CMDID, NULL, 0,
		      WMI_TRAFFIC_RESUME_EVENTID, &reply, sizeof(reply),
		      WIL_WAIT_FOR_SUSPEND_RESUME_COMP);
	if (rc)
		return rc;

	return reply.evt.status;
}


int wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short)
{
	int rc;
	struct {
		struct wmi_cmd_hdr wmi;
		struct wmi_get_mgmt_retry_limit_event evt;
	} __packed reply;

	wil_dbg_wmi(wil, "getting mgmt retry short\n");

	if (!test_bit(WMI_FW_CAPABILITY_MGMT_RETRY_LIMIT, wil->fw_capabilities))
		return -ENOTSUPP;

	reply.evt.mgmt_retry_limit = 0;
	rc = wmi_call(wil, WMI_GET_MGMT_RETRY_LIMIT_CMDID, NULL, 0,
		      WMI_GET_MGMT_RETRY_LIMIT_EVENTID, &reply, sizeof(reply),
		      100);
	if (rc)
		return rc;

	if (retry_short)
		*retry_short = reply.evt.mgmt_retry_limit;

	return 0;
}


psg_json_value_set_ngx_flag(PsgJsonValue *doc, const char *name, ngx_flag_t value) {
    if (value != NGX_CONF_UNSET) {
        return psg_json_value_set_bool(doc, name, value);
    } else {
        return NULL;
    }
}


save_master_process_pid(ngx_cycle_t *cycle) {
    u_char filename[NGX_MAX_PATH];
    u_char *last;
    FILE *f;

    last = ngx_snprintf(filename, sizeof(filename) - 1, "%s/web_server_info/control_process.pid",
                        psg_watchdog_launcher_get_instance_dir(psg_watchdog_launcher, NULL));
    *last = (u_char) '\0';

    f = fopen((const char *) filename, "w");
    if (f != NULL) {
        fprintf(f, "%ld", (long) getppid());
        fclose(f);
    } else {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "could not create %s", filename);
    }

    return NGX_OK;
}


get_contact_handler (LDAPOp *op,
                     LDAPMessage *res)
{
	EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (op->backend);
	gint msg_type;
	GTimeVal start, end;
	gulong diff;

	if (enable_debug) {
		printf ("get_contact_handler ... \n");
		g_get_current_time (&start);
	}

	g_rec_mutex_lock (&eds_ldap_handler_lock);
	if (!bl->priv->ldap) {
		g_rec_mutex_unlock (&eds_ldap_handler_lock);
		e_data_book_respond_get_contact (op->book, op->opid, EDB_ERROR_NOT_CONNECTED (), NULL);
		ldap_op_finished (op);
		if (enable_debug)
			printf ("get_contact_handler... ldap handler is NULL \n");
		return;
	}
	g_rec_mutex_unlock (&eds_ldap_handler_lock);

	/* the msg_type will be either SEARCH_ENTRY (if we're
	 * successful) or SEARCH_RESULT (if we're not), so we finish
	 * the op after either */
	msg_type = ldap_msgtype (res);
	if (msg_type == LDAP_RES_SEARCH_ENTRY) {
		LDAPMessage *e;
		EContact *contact;
		gchar *vcard;

		g_rec_mutex_lock (&eds_ldap_handler_lock);
		if (bl->priv->ldap)
			e = ldap_first_entry (bl->priv->ldap, res);
		else
			e = NULL;
		g_rec_mutex_unlock (&eds_ldap_handler_lock);

		if (!e) {
			e_data_book_respond_get_contact (
				op->book, op->opid,
				e_data_book_create_error_fmt (
					E_DATA_BOOK_STATUS_OTHER_ERROR,
					_("%s: NULL returned from ldap_first_entry"),
					G_STRFUNC),
				NULL);
			ldap_op_finished (op);
			return;
		}

		contact = build_contact_from_entry (bl, e, NULL, NULL);
		if (!contact) {
			e_data_book_respond_get_contact (
				op->book, op->opid,
				e_data_book_create_error_fmt (
					E_DATA_BOOK_STATUS_OTHER_ERROR,
					_("%s: NULL returned from ldap_first_entry"),
					G_STRFUNC),
				NULL);
			ldap_op_finished (op);
			return;
		}

		vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
		e_data_book_respond_get_contact (
			op->book,
			op->opid,
			EDB_ERROR (SUCCESS),
			vcard);
		g_free (vcard);
		g_object_unref (contact);
		ldap_op_finished (op);

		if (enable_debug) {
			g_get_current_time (&end);
			diff = end.tv_sec * 1000 + end.tv_usec / 1000;
			diff -= start.tv_sec * 1000 + start.tv_usec / 1000;
			printf (
				"get_contact_handler took %ld.%03ld seconds \n",
				diff / 1000, diff % 1000);
		}
	} else if (msg_type == LDAP_RES_SEARCH_REFERENCE) {
		/* ignore references */
	} else if (msg_type == LDAP_RES_SEARCH_RESULT) {
		gchar *ldap_error_msg = NULL;
		gint ldap_error;

		g_rec_mutex_lock (&eds_ldap_handler_lock);
		if (bl->priv->ldap) {
			ldap_parse_result (
				bl->priv->ldap, res, &ldap_error,
				NULL, &ldap_error_msg, NULL, NULL, 0);
		} else {
			ldap_error = LDAP_SERVER_DOWN;
		}
		g_rec_mutex_unlock (&eds_ldap_handler_lock);
		if (ldap_error != LDAP_SUCCESS) {
			g_warning (
				"get_contact_handler: %02X (%s), additional info: %s",
				ldap_error,
				ldap_err2string (ldap_error), ldap_error_msg);
		}
		if (ldap_error_msg)
			ldap_memfree (ldap_error_msg);

		e_data_book_respond_get_contact (
			op->book,
			op->opid,
			ldap_error_to_response (ldap_error),
			NULL);
		ldap_op_finished (op);
	}
	else {
		e_data_book_respond_get_contact (
			op->book,
			op->opid,
			e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR,
							_("%s: Unhandled result type %d returned"), G_STRFUNC, msg_type),
			NULL);
		ldap_op_finished (op);
	}

}


email_populate (EContact *contact,
                gchar **values)
{
	gint i;
	for (i = 0; values[i] && i < 4; i++)
		e_contact_set (contact, email_ids[i], values[i]);
}


ldap_search_dtor (LDAPOp *op)
{
	EBookBackendLDAP *bl;
	LDAPSearchOp *search_op = (LDAPSearchOp *) op;

	d (printf ("ldap_search_dtor (%p)\n", search_op->view));

	bl = E_BOOK_BACKEND_LDAP (e_data_book_view_get_backend (op->view));

	/* unhook us from our EDataBookView */
	g_mutex_lock (&bl->priv->view_mutex);
	g_object_set_data (G_OBJECT (search_op->view), LDAP_SEARCH_OP_IDENT, NULL);
	g_mutex_unlock (&bl->priv->view_mutex);

	g_object_unref (search_op->view);

	if (!search_op->aborted)
		g_free (search_op);
}


func_not (struct _ESExp *f,
          gint argc,
          struct _ESExpResult **argv,
          gpointer data)
{
	EBookBackendLDAPSExpData *ldap_data = data;
	ESExpResult *r;

	/* just replace the head of the list with the NOT of it. */
	if (argc > 0) {
		gchar *term = ldap_data->list->data;
		ldap_data->list->data = g_strdup_printf ("(!%s)", term);
		g_free (term);
	}

	r = e_sexp_result_new (f, ESEXP_RES_BOOL);
	r->value.boolean = FALSE;

	return r;
}


ldap_op_change_id (LDAPOp *op,
                   gint msg_id)
{
	EBookBackend *backend = op->backend;
	EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (backend);

	g_rec_mutex_lock (&eds_ldap_handler_lock);
	g_rec_mutex_lock (&bl->priv->op_hash_mutex);
	g_hash_table_remove (bl->priv->id_to_op, &op->id);

	op->id = msg_id;

	g_hash_table_insert (bl->priv->id_to_op, &op->id, op);
	g_rec_mutex_unlock (&bl->priv->op_hash_mutex);
	g_rec_mutex_unlock (&eds_ldap_handler_lock);
}


birthday_ber (EContact *contact)
{
	EContactDate *dt;
	struct berval ** result = NULL;

	dt = e_contact_get (contact, E_CONTACT_BIRTH_DATE);
	if (dt) {
		gchar *birthday;

		birthday = e_contact_date_to_string (dt);

		result = g_new (struct berval *, 2);
		result[0] = g_new (struct berval, 1);
		result[0]->bv_val = birthday;
		result[0]->bv_len = strlen (birthday);

		result[1] = NULL;

		e_contact_date_free (dt);
	}

	return result;
}


book_backend_ldap_refresh_sync (EBookBackend *backend,
				GCancellable *cancellable,
				GError **error)
{
	EBookBackendLDAP *ldap_backend = E_BOOK_BACKEND_LDAP (backend);

	g_return_val_if_fail (ldap_backend != NULL, FALSE);
	g_return_val_if_fail (ldap_backend->priv != NULL, FALSE);

	if (!ldap_backend->priv->cache || !ldap_backend->priv->marked_for_offline ||
	    ldap_backend->priv->generate_cache_in_progress)
		return TRUE;

	e_book_backend_cache_set_time (ldap_backend->priv->cache, "");
	generate_cache (ldap_backend);

	return TRUE;
}


GuestUserList *qmp_guest_get_users(Error **err)
{
    GHashTable *cache = NULL;
    GuestUserList *head = NULL, *cur_item = NULL;
    struct utmpx *user_info = NULL;
    gpointer value = NULL;
    GuestUser *user = NULL;
    GuestUserList *item = NULL;
    double login_time = 0;

    cache = g_hash_table_new(g_str_hash, g_str_equal);
    setutxent();

    for (;;) {
        user_info = getutxent();
        if (user_info == NULL) {
            break;
        } else if (user_info->ut_type != USER_PROCESS) {
            continue;
        } else if (g_hash_table_contains(cache, user_info->ut_user)) {
            value = g_hash_table_lookup(cache, user_info->ut_user);
            user = (GuestUser *)value;
            login_time = ga_get_login_time(user_info);
            /* We're ensuring the earliest login time to be sent */
            if (login_time < user->login_time) {
                user->login_time = login_time;
            }
            continue;
        }

        item = g_new0(GuestUserList, 1);
        item->value = g_new0(GuestUser, 1);
        item->value->user = g_strdup(user_info->ut_user);
        item->value->login_time = ga_get_login_time(user_info);

        g_hash_table_insert(cache, item->value->user, item->value);

        if (!cur_item) {
            head = cur_item = item;
        } else {
            cur_item->next = item;
            cur_item = item;
        }
    }
    endutxent();
    g_hash_table_destroy(cache);
    return head;
}


GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
                                     bool has_count, int64_t count,
                                     Error **errp)
{
    GuestFileWrite *write_data = NULL;
    guchar *buf;
    gsize buf_len;
    bool is_ok;
    DWORD write_count;
    GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
    HANDLE fh;

    if (!gfh) {
        return NULL;
    }
    fh = gfh->fh;
    buf = qbase64_decode(buf_b64, -1, &buf_len, errp);
    if (!buf) {
        return NULL;
    }

    if (!has_count) {
        count = buf_len;
    } else if (count < 0 || count > buf_len) {
        error_setg(errp, "value '%" PRId64
                   "' is invalid for argument count", count);
        goto done;
    }

    is_ok = WriteFile(fh, buf, count, &write_count, NULL);
    if (!is_ok) {
        error_setg_win32(errp, GetLastError(), "failed to write to file");
        slog("guest-file-write-failed, handle: %" PRId64, handle);
    } else {
        write_data = g_new0(GuestFileWrite, 1);
        write_data->count = (size_t) write_count;
    }

done:
    g_free(buf);
    return write_data;
}


GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
{
    IP_ADAPTER_ADDRESSES *adptr_addrs, *addr;
    IP_ADAPTER_UNICAST_ADDRESS *ip_addr = NULL;
    GuestNetworkInterfaceList *head = NULL, *cur_item = NULL;
    GuestIpAddressList *head_addr, *cur_addr;
    GuestNetworkInterfaceList *info;
    GuestNetworkInterfaceStat *interface_stat = NULL;
    GuestIpAddressList *address_item = NULL;
    unsigned char *mac_addr;
    char *addr_str;
    WORD wsa_version;
    WSADATA wsa_data;
    int ret;

    adptr_addrs = guest_get_adapters_addresses(errp);
    if (adptr_addrs == NULL) {
        return NULL;
    }

    /* Make WSA APIs available. */
    wsa_version = MAKEWORD(2, 2);
    ret = WSAStartup(wsa_version, &wsa_data);
    if (ret != 0) {
        error_setg_win32(errp, ret, "failed socket startup");
        goto out;
    }

    for (addr = adptr_addrs; addr; addr = addr->Next) {
        info = g_malloc0(sizeof(*info));

        if (cur_item == NULL) {
            head = cur_item = info;
        } else {
            cur_item->next = info;
            cur_item = info;
        }

        info->value = g_malloc0(sizeof(*info->value));
        info->value->name = guest_wctomb_dup(addr->FriendlyName);

        if (addr->PhysicalAddressLength != 0) {
            mac_addr = addr->PhysicalAddress;

            info->value->hardware_address =
                g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x",
                                (int) mac_addr[0], (int) mac_addr[1],
                                (int) mac_addr[2], (int) mac_addr[3],
                                (int) mac_addr[4], (int) mac_addr[5]);

            info->value->has_hardware_address = true;
        }

        head_addr = NULL;
        cur_addr = NULL;
        for (ip_addr = addr->FirstUnicastAddress;
                ip_addr;
                ip_addr = ip_addr->Next) {
            addr_str = guest_addr_to_str(ip_addr, errp);
            if (addr_str == NULL) {
                continue;
            }

            address_item = g_malloc0(sizeof(*address_item));

            if (!cur_addr) {
                head_addr = cur_addr = address_item;
            } else {
                cur_addr->next = address_item;
                cur_addr = address_item;
            }

            address_item->value = g_malloc0(sizeof(*address_item->value));
            address_item->value->ip_address = addr_str;
            address_item->value->prefix = guest_ip_prefix(ip_addr);
            if (ip_addr->Address.lpSockaddr->sa_family == AF_INET) {
                address_item->value->ip_address_type =
                    GUEST_IP_ADDRESS_TYPE_IPV4;
            } else if (ip_addr->Address.lpSockaddr->sa_family == AF_INET6) {
                address_item->value->ip_address_type =
                    GUEST_IP_ADDRESS_TYPE_IPV6;
            }
        }
        if (head_addr) {
            info->value->has_ip_addresses = true;
            info->value->ip_addresses = head_addr;
        }
        if (!info->value->has_statistics) {
            interface_stat = g_malloc0(sizeof(*interface_stat));
            if (guest_get_network_stats(addr->AdapterName,
                interface_stat) == -1) {
                info->value->has_statistics = false;
                g_free(interface_stat);
            } else {
                info->value->statistics = interface_stat;
                info->value->has_statistics = true;
            }
        }
    }
    WSACleanup();
out:
    g_free(adptr_addrs);
    return head;
}


static int64_t guest_ip_prefix(IP_ADAPTER_UNICAST_ADDRESS *ip_addr)
{
    /* For Windows Vista/2008 and newer, use the OnLinkPrefixLength
     * field to obtain the prefix.
     */
    return ip_addr->OnLinkPrefixLength;
}


void qmp_guest_set_user_password(const char *username,
                                 const char *password,
                                 bool crypted,
                                 Error **errp)
{
    error_setg(errp, QERR_UNSUPPORTED);
}


qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
{
    error_setg(errp, QERR_UNSUPPORTED);
    return NULL;
}


void qmp_guest_suspend_ram(Error **errp)
{
    Error *local_err = NULL;
    GuestSuspendMode *mode = g_new(GuestSuspendMode, 1);

    *mode = GUEST_SUSPEND_MODE_RAM;
    check_suspend_mode(*mode, &local_err);
    acquire_privilege(SE_SHUTDOWN_NAME, &local_err);
    execute_async(do_suspend, mode, &local_err);

    if (local_err) {
        error_propagate(errp, local_err);
        g_free(mode);
    }
}


int64_t qmp_guest_fsfreeze_freeze(Error **errp)
{
    return qmp_guest_fsfreeze_freeze_list(false, NULL, errp);
}


static int dev_major_minor(const char *devpath,
                           unsigned int *devmajor, unsigned int *devminor)
{
    struct stat st;

    *devmajor = 0;
    *devminor = 0;

    if (stat(devpath, &st) < 0) {
        slog("failed to stat device file '%s': %s", devpath, strerror(errno));
        return -1;
    }
    if (S_ISDIR(st.st_mode)) {
        /* It is bind mount */
        return -2;
    }
    if (S_ISBLK(st.st_mode)) {
        *devmajor = major(st.st_rdev);
        *devminor = minor(st.st_rdev);
        return 0;
    }
    return -1;
}


GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
                                   GuestFileWhence *whence_code,
                                   Error **errp)
{
    GuestFileHandle *gfh;
    GuestFileSeek *seek_data;
    HANDLE fh;
    LARGE_INTEGER new_pos, off_pos;
    off_pos.QuadPart = offset;
    BOOL res;
    int whence;
    Error *err = NULL;

    gfh = guest_file_handle_find(handle, errp);
    if (!gfh) {
        return NULL;
    }

    /* We stupidly exposed 'whence':'int' in our qapi */
    whence = ga_parse_whence(whence_code, &err);
    if (err) {
        error_propagate(errp, err);
        return NULL;
    }

    fh = gfh->fh;
    res = SetFilePointerEx(fh, off_pos, &new_pos, whence);
    if (!res) {
        error_setg_win32(errp, GetLastError(), "failed to seek file");
        return NULL;
    }
    seek_data = g_new0(GuestFileSeek, 1);
    seek_data->position = new_pos.QuadPart;
    return seek_data;
}


static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg,
                               const char *sysfile_str, Error **errp)
{
    Error *local_err = NULL;
    char *pmutils_path;
    pid_t pid;
    int status;

    pmutils_path = g_find_program_in_path(pmutils_bin);

    pid = fork();
    if (!pid) {
        char buf[32]; /* hopefully big enough */
        ssize_t ret;
        int fd;

        setsid();
        reopen_fd_to_null(0);
        reopen_fd_to_null(1);
        reopen_fd_to_null(2);

        if (pmutils_path) {
            execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ);
        }

        /*
         * If we get here either pm-utils is not installed or execle() has
         * failed. Let's try the manual method if the caller wants it.
         */

        if (!sysfile_str) {
            _exit(SUSPEND_NOT_SUPPORTED);
        }

        fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
        if (fd < 0) {
            _exit(SUSPEND_NOT_SUPPORTED);
        }

        ret = read(fd, buf, sizeof(buf)-1);
        if (ret <= 0) {
            _exit(SUSPEND_NOT_SUPPORTED);
        }
        buf[ret] = '\0';

        if (strstr(buf, sysfile_str)) {
            _exit(SUSPEND_SUPPORTED);
        }

        _exit(SUSPEND_NOT_SUPPORTED);
    } else if (pid < 0) {
        error_setg_errno(errp, errno, "failed to create child process");
        goto out;
    }

    ga_wait_child(pid, &status, &local_err);
    if (local_err) {
        error_propagate(errp, local_err);
        goto out;
    }

    if (!WIFEXITED(status)) {
        error_setg(errp, "child process has terminated abnormally");
        goto out;
    }

    switch (WEXITSTATUS(status)) {
    case SUSPEND_SUPPORTED:
        goto out;
    case SUSPEND_NOT_SUPPORTED:
        error_setg(errp,
                   "the requested suspend mode is not supported by the guest");
        goto out;
    default:
        error_setg(errp,
                   "the helper program '%s' returned an unexpected exit status"
                   " code (%d)", pmutils_path, WEXITSTATUS(status));
        goto out;
    }

out:
    g_free(pmutils_path);
}


static void cliRefreshPrompt(void) {
    if (config.eval_ldb) return;

    sds prompt = sdsempty();
    if (config.hostsocket != NULL) {
        prompt = sdscatfmt(prompt,"redis %s",config.hostsocket);
    } else {
        char addr[256];
        anetFormatAddr(addr, sizeof(addr), config.hostip, config.hostport);
        prompt = sdscatlen(prompt,addr,strlen(addr));
    }

    /* Add [dbnum] if needed */
    if (config.dbnum != 0)
        prompt = sdscatfmt(prompt,"[%i]",config.dbnum);

    /* Copy the prompt in the static buffer. */
    prompt = sdscatlen(prompt,"> ",2);
    snprintf(config.prompt,sizeof(config.prompt),"%s",prompt);
    sdsfree(prompt);
}


static void cliInitHelp(void) {
    int commandslen = sizeof(commandHelp)/sizeof(struct commandHelp);
    int groupslen = sizeof(commandGroups)/sizeof(char*);
    int i, len, pos = 0;
    helpEntry tmp;

    helpEntriesLen = len = commandslen+groupslen;
    helpEntries = zmalloc(sizeof(helpEntry)*len);

    for (i = 0; i < groupslen; i++) {
        tmp.argc = 1;
        tmp.argv = zmalloc(sizeof(sds));
        tmp.argv[0] = sdscatprintf(sdsempty(),"@%s",commandGroups[i]);
        tmp.full = tmp.argv[0];
        tmp.type = CLI_HELP_GROUP;
        tmp.org = NULL;
        helpEntries[pos++] = tmp;
    }

    for (i = 0; i < commandslen; i++) {
        tmp.argv = sdssplitargs(commandHelp[i].name,&tmp.argc);
        tmp.full = sdsnew(commandHelp[i].name);
        tmp.type = CLI_HELP_COMMAND;
        tmp.org = &commandHelp[i];
        helpEntries[pos++] = tmp;
    }
}


static void intrinsicLatencyModeStop(int s) {
    UNUSED(s);
    force_cancel_loop = 1;
}


static void statMode(void) {
    redisReply *reply;
    long aux, requests = 0;
    int i = 0;

    while(1) {
        char buf[64];
        int j;

        reply = reconnectingRedisCommand(context,"INFO");
        if (reply->type == REDIS_REPLY_ERROR) {
            printf("ERROR: %s\n", reply->str);
            exit(1);
        }

        if ((i++ % 20) == 0) {
            printf(
"------- data ------ --------------------- load -------------------- - child -\n"
"keys       mem      clients blocked requests            connections          \n");
        }

        /* Keys */
        aux = 0;
        for (j = 0; j < 20; j++) {
            long k;

            sprintf(buf,"db%d:keys",j);
            k = getLongInfoField(reply->str,buf);
            if (k == LONG_MIN) continue;
            aux += k;
        }
        sprintf(buf,"%ld",aux);
        printf("%-11s",buf);

        /* Used memory */
        aux = getLongInfoField(reply->str,"used_memory");
        bytesToHuman(buf,aux);
        printf("%-8s",buf);

        /* Clients */
        aux = getLongInfoField(reply->str,"connected_clients");
        sprintf(buf,"%ld",aux);
        printf(" %-8s",buf);

        /* Blocked (BLPOPPING) Clients */
        aux = getLongInfoField(reply->str,"blocked_clients");
        sprintf(buf,"%ld",aux);
        printf("%-8s",buf);

        /* Requests */
        aux = getLongInfoField(reply->str,"total_commands_processed");
        sprintf(buf,"%ld (+%ld)",aux,requests == 0 ? 0 : aux-requests);
        printf("%-19s",buf);
        requests = aux;

        /* Connections */
        aux = getLongInfoField(reply->str,"total_connections_received");
        sprintf(buf,"%ld",aux);
        printf(" %-12s",buf);

        /* Children */
        aux = getLongInfoField(reply->str,"bgsave_in_progress");
        aux |= getLongInfoField(reply->str,"aof_rewrite_in_progress") << 1;
        aux |= getLongInfoField(reply->str,"loading") << 2;
        switch(aux) {
        case 0: break;
        case 1:
            printf("SAVE");
            break;
        case 2:
            printf("AOF");
            break;
        case 3:
            printf("SAVE+AOF");
            break;
        case 4:
            printf("LOAD");
            break;
        }

        printf("\n");
        freeReplyObject(reply);
        usleep(config.interval);
    }
}


static void LRUTestMode(void) {
    redisReply *reply;
    char key[128];
    long long start_cycle;
    int j;

    srand(time(NULL)^getpid());
    while(1) {
        /* Perform cycles of 1 second with 50% writes and 50% reads.
         * We use pipelining batching writes / reads N times per cycle in order
         * to fill the target instance easily. */
        start_cycle = mstime();
        long long hits = 0, misses = 0;
        while(mstime() - start_cycle < 1000) {
            /* Write cycle. */
            for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++) {
                char val[6];
                val[5] = '\0';
                for (int i = 0; i < 5; i++) val[i] = 'A'+rand()%('z'-'A');
                LRUTestGenKey(key,sizeof(key));
                redisAppendCommand(context, "SET %s %s",key,val);
            }
            for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++)
                redisGetReply(context, (void**)&reply);

            /* Read cycle. */
            for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++) {
                LRUTestGenKey(key,sizeof(key));
                redisAppendCommand(context, "GET %s",key);
            }
            for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++) {
                if (redisGetReply(context, (void**)&reply) == REDIS_OK) {
                    switch(reply->type) {
                        case REDIS_REPLY_ERROR:
                            printf("%s\n", reply->str);
                            break;
                        case REDIS_REPLY_NIL:
                            misses++;
                            break;
                        default:
                            hits++;
                            break;
                    }
                }
            }

            if (context->err) {
                fprintf(stderr,"I/O error during LRU test\n");
                exit(1);
            }
        }
        /* Print stats. */
        printf(
            "%lld Gets/sec | Hits: %lld (%.2f%%) | Misses: %lld (%.2f%%)\n",
            hits+misses,
            hits, (double)hits/(hits+misses)*100,
            misses, (double)misses/(hits+misses)*100);
    }
    exit(0);
}


static int issueCommand(int argc, char **argv) {
    return issueCommandRepeat(argc, argv, config.repeat);
}


int main(int argc, char **argv) {
    int firstarg;

    config.hostip = sdsnew("127.0.0.1");
    config.hostport = 6379;
    config.hostsocket = NULL;
    config.repeat = 1;
    config.interval = 0;
    config.dbnum = 0;
    config.interactive = 0;
    config.shutdown = 0;
    config.monitor_mode = 0;
    config.pubsub_mode = 0;
    config.latency_mode = 0;
    config.latency_dist_mode = 0;
    config.latency_history = 0;
    config.lru_test_mode = 0;
    config.lru_test_sample_size = 0;
    config.cluster_mode = 0;
    config.slave_mode = 0;
    config.getrdb_mode = 0;
    config.stat_mode = 0;
    config.scan_mode = 0;
    config.intrinsic_latency_mode = 0;
    config.pattern = NULL;
    config.rdb_filename = NULL;
    config.pipe_mode = 0;
    config.pipe_timeout = REDIS_CLI_DEFAULT_PIPE_TIMEOUT;
    config.bigkeys = 0;
    config.hotkeys = 0;
    config.stdinarg = 0;
    config.auth = NULL;
    config.eval = NULL;
    config.eval_ldb = 0;
    config.eval_ldb_end = 0;
    config.eval_ldb_sync = 0;
    config.enable_ldb_on_eval = 0;
    config.last_cmd_type = -1;

    pref.hints = 1;

    spectrum_palette = spectrum_palette_color;
    spectrum_palette_size = spectrum_palette_color_size;

    if (!isatty(fileno(stdout)) && (getenv("FAKETTY") == NULL))
        config.output = OUTPUT_RAW;
    else
        config.output = OUTPUT_STANDARD;
    config.mb_delim = sdsnew("\n");

    firstarg = parseOptions(argc,argv);
    argc -= firstarg;
    argv += firstarg;

    /* Latency mode */
    if (config.latency_mode) {
        if (cliConnect(0) == REDIS_ERR) exit(1);
        latencyMode();
    }

    /* Latency distribution mode */
    if (config.latency_dist_mode) {
        if (cliConnect(0) == REDIS_ERR) exit(1);
        latencyDistMode();
    }

    /* Slave mode */
    if (config.slave_mode) {
        if (cliConnect(0) == REDIS_ERR) exit(1);
        slaveMode();
    }

    /* Get RDB mode. */
    if (config.getrdb_mode) {
        if (cliConnect(0) == REDIS_ERR) exit(1);
        getRDB();
    }

    /* Pipe mode */
    if (config.pipe_mode) {
        if (cliConnect(0) == REDIS_ERR) exit(1);
        pipeMode();
    }

    /* Find big keys */
    if (config.bigkeys) {
        if (cliConnect(0) == REDIS_ERR) exit(1);
        findBigKeys();
    }

    /* Find hot keys */
    if (config.hotkeys) {
        if (cliConnect(0) == REDIS_ERR) exit(1);
        findHotKeys();
    }

    /* Stat mode */
    if (config.stat_mode) {
        if (cliConnect(0) == REDIS_ERR) exit(1);
        if (config.interval == 0) config.interval = 1000000;
        statMode();
    }

    /* Scan mode */
    if (config.scan_mode) {
        if (cliConnect(0) == REDIS_ERR) exit(1);
        scanMode();
    }

    /* LRU test mode */
    if (config.lru_test_mode) {
        if (cliConnect(0) == REDIS_ERR) exit(1);
        LRUTestMode();
    }

    /* Intrinsic latency mode */
    if (config.intrinsic_latency_mode) intrinsicLatencyMode();

    /* Start interactive mode when no command is provided */
    if (argc == 0 && !config.eval) {
        /* Ignore SIGPIPE in interactive mode to force a reconnect */
        signal(SIGPIPE, SIG_IGN);

        /* Note that in repl mode we don't abort on connection error.
         * A new attempt will be performed for every command send. */
        cliConnect(0);
        repl();
    }

    /* Otherwise, we have some arguments to execute */
    if (cliConnect(0) != REDIS_OK) exit(1);
    if (config.eval) {
        return evalMode(argc,argv);
    } else {
        return noninteractive(argc,convertToSds(argc,argv));
    }
}


ModuleExport void UnregisterDIBImage(void)
{
  (void) UnregisterMagickInfo("ICODIB");
  (void) UnregisterMagickInfo("DIB");
}


static Image *ReadDIBImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
  DIBInfo
    dib_info;

  Image
    *image;

  IndexPacket
    index;

  ssize_t
    bit,
    y;

  MagickBooleanType
    status;

  MemoryInfo
    *pixel_info;

  register IndexPacket
    *indexes;

  register ssize_t
    x;

  register PixelPacket
    *q;

  register ssize_t
    i;

  register unsigned char
    *p;

  size_t
    bytes_per_line,
    length;

  ssize_t
    count;

  unsigned char
    *pixels;

  /*
    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);
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
  if (status == MagickFalse)
    {
      image=DestroyImageList(image);
      return((Image *) NULL);
    }
  /*
    Determine if this a DIB file.
  */
  (void) memset(&dib_info,0,sizeof(dib_info));
  dib_info.size=ReadBlobLSBLong(image);
  if (dib_info.size != 40)
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  /*
    Microsoft Windows 3.X DIB image file.
  */
  dib_info.width=ReadBlobLSBSignedLong(image);
  dib_info.height=ReadBlobLSBSignedLong(image);
  dib_info.planes=ReadBlobLSBShort(image);
  dib_info.bits_per_pixel=ReadBlobLSBShort(image);
  dib_info.compression=ReadBlobLSBLong(image);
  dib_info.image_size=ReadBlobLSBLong(image);
  dib_info.x_pixels=ReadBlobLSBLong(image);
  dib_info.y_pixels=ReadBlobLSBLong(image);
  dib_info.number_colors=ReadBlobLSBLong(image);
  dib_info.colors_important=ReadBlobLSBLong(image);
  if ((dib_info.bits_per_pixel != 1) && (dib_info.bits_per_pixel != 4) &&
      (dib_info.bits_per_pixel != 8) && (dib_info.bits_per_pixel != 16) &&
      (dib_info.bits_per_pixel != 24) && (dib_info.bits_per_pixel != 32))
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  if ((dib_info.compression == BI_BITFIELDS) &&
      ((dib_info.bits_per_pixel == 16) || (dib_info.bits_per_pixel == 32)))
    {
      dib_info.red_mask=(unsigned short) ReadBlobLSBLong(image);
      dib_info.green_mask=(unsigned short) ReadBlobLSBLong(image);
      dib_info.blue_mask=(unsigned short) ReadBlobLSBLong(image);
    }
  if (EOFBlob(image) != MagickFalse)
    ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
  if (dib_info.width <= 0)
    ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
  if (dib_info.height == 0)
    ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
  if (dib_info.planes != 1)
    ThrowReaderException(CorruptImageError,"StaticPlanesValueNotEqualToOne");
  if ((dib_info.bits_per_pixel != 1) && (dib_info.bits_per_pixel != 4) &&
      (dib_info.bits_per_pixel != 8) && (dib_info.bits_per_pixel != 16) &&
      (dib_info.bits_per_pixel != 24) && (dib_info.bits_per_pixel != 32))
    ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
  if ((dib_info.bits_per_pixel < 16) &&
      (dib_info.number_colors > (unsigned int) (1UL << dib_info.bits_per_pixel)))
    ThrowReaderException(CorruptImageError,"UnrecognizedNumberOfColors");
  if ((dib_info.compression == 1) && (dib_info.bits_per_pixel != 8))
    ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
  if ((dib_info.compression == 2) && (dib_info.bits_per_pixel != 4))
    ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
  if ((dib_info.compression == 3) && (dib_info.bits_per_pixel < 16))
    ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
  switch (dib_info.compression)
  {
    case BI_RGB:
    case BI_RLE8:
    case BI_RLE4:
    case BI_BITFIELDS:
      break;
    case BI_JPEG:
      ThrowReaderException(CoderError,"JPEGCompressNotSupported");
    case BI_PNG:
      ThrowReaderException(CoderError,"PNGCompressNotSupported");
    default:
      ThrowReaderException(CorruptImageError,"UnrecognizedImageCompression");
  }
  image->columns=(size_t) MagickAbsoluteValue((ssize_t) dib_info.width);
  image->rows=(size_t) MagickAbsoluteValue((ssize_t) dib_info.height);
  image->matte=dib_info.bits_per_pixel == 32 ? MagickTrue : MagickFalse;
  image->depth=8;
  if ((dib_info.number_colors > 256) || (dib_info.colors_important > 256))
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  if ((dib_info.number_colors != 0) || (dib_info.bits_per_pixel < 16))
    {
      size_t
        one;

      image->storage_class=PseudoClass;
      image->colors=dib_info.number_colors;
      one=1;
      if (image->colors == 0)
        image->colors=one << dib_info.bits_per_pixel;
    }
  if (image_info->size != (char *) NULL)
    {
      RectangleInfo
        geometry;

      MagickStatusType
        flags;

      flags=ParseAbsoluteGeometry(image_info->size,&geometry);
      if (flags & WidthValue)
        if ((geometry.width != 0) && (geometry.width < image->columns))
          image->columns=geometry.width;
      if (flags & HeightValue)
        if ((geometry.height != 0) && (geometry.height < image->rows))
          image->rows=geometry.height;
    }
  status=SetImageExtent(image,image->columns,image->rows);
  if (status == MagickFalse)
    {
      InheritException(exception,&image->exception);
      return(DestroyImageList(image));
    }
  if (image->storage_class == PseudoClass)
    {
      size_t
        length,
        packet_size;

      unsigned char
        *dib_colormap;

      /*
        Read DIB raster colormap.
      */
      if (AcquireImageColormap(image,image->colors) == MagickFalse)
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      length=(size_t) image->colors;
      dib_colormap=(unsigned char *) AcquireQuantumMemory(length,
        4*sizeof(*dib_colormap));
      if (dib_colormap == (unsigned char *) NULL)
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      packet_size=4;
      count=ReadBlob(image,packet_size*image->colors,dib_colormap);
      if (count != (ssize_t) (packet_size*image->colors))
        {
          dib_colormap=(unsigned char *) RelinquishMagickMemory(dib_colormap);
          ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
        }
      p=dib_colormap;
      for (i=0; i < (ssize_t) image->colors; i++)
      {
        image->colormap[i].blue=ScaleCharToQuantum(*p++);
        image->colormap[i].green=ScaleCharToQuantum(*p++);
        image->colormap[i].red=ScaleCharToQuantum(*p++);
        if (packet_size == 4)
          p++;
      }
      dib_colormap=(unsigned char *) RelinquishMagickMemory(dib_colormap);
    }
  /*
    Read image data.
  */
  if (dib_info.compression == BI_RLE4)
    dib_info.bits_per_pixel<<=1;
  bytes_per_line=4*((image->columns*dib_info.bits_per_pixel+31)/32);
  length=bytes_per_line*image->rows;
  pixel_info=AcquireVirtualMemory(image->rows,MagickMax(bytes_per_line,
    image->columns+256UL)*sizeof(*pixels));
  if (pixel_info == (MemoryInfo *) NULL)
    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
  pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
  if ((dib_info.compression == BI_RGB) ||
      (dib_info.compression == BI_BITFIELDS))
    {
      count=ReadBlob(image,length,pixels);
      if (count != (ssize_t) (length))
        {
          pixel_info=RelinquishVirtualMemory(pixel_info);
          ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
        }
    }
  else
    {
      /*
        Convert run-length encoded raster pixels.
      */
      status=DecodeImage(image,dib_info.compression ? MagickTrue : MagickFalse,
        pixels,image->columns*image->rows);
      if (status == MagickFalse)
        {
          pixel_info=RelinquishVirtualMemory(pixel_info);
          ThrowReaderException(CorruptImageError,
            "UnableToRunlengthDecodeImage");
        }
    }
  /*
    Initialize image structure.
  */
  image->units=PixelsPerCentimeterResolution;
  image->x_resolution=(double) dib_info.x_pixels/100.0;
  image->y_resolution=(double) dib_info.y_pixels/100.0;
  /*
    Convert DIB raster image to pixel packets.
  */
  switch (dib_info.bits_per_pixel)
  {
    case 1:
    {
      /*
        Convert bitmap scanline.
      */
      for (y=(ssize_t) image->rows-1; y >= 0; y--)
      {
        p=pixels+(image->rows-y-1)*bytes_per_line;
        q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
        if (q == (PixelPacket *) NULL)
          break;
        indexes=GetAuthenticIndexQueue(image);
        for (x=0; x < ((ssize_t) image->columns-7); x+=8)
        {
          for (bit=0; bit < 8; bit++)
          {
            index=(IndexPacket) ((*p) & (0x80 >> bit) ? 0x01 : 0x00);
            SetPixelIndex(indexes+x+bit,index);
          }
          p++;
        }
        if ((image->columns % 8) != 0)
          {
            for (bit=0; bit < (ssize_t) (image->columns % 8); bit++)
            {
              index=(IndexPacket) ((*p) & (0x80 >> bit) ? 0x01 : 0x00);
              SetPixelIndex(indexes+x+bit,index);
            }
            p++;
          }
        if (SyncAuthenticPixels(image,exception) == MagickFalse)
          break;
        if (image->previous == (Image *) NULL)
          {
            status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
              image->rows-y-1,image->rows);
            if (status == MagickFalse)
              break;
          }
      }
      (void) SyncImage(image);
      break;
    }
    case 4:
    {
      /*
        Convert PseudoColor scanline.
      */
      for (y=(ssize_t) image->rows-1; y >= 0; y--)
      {
        p=pixels+(image->rows-y-1)*bytes_per_line;
        q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
        if (q == (PixelPacket *) NULL)
          break;
        indexes=GetAuthenticIndexQueue(image);
        for (x=0; x < ((ssize_t) image->columns-1); x+=2)
        {
          index=ConstrainColormapIndex(image,(*p >> 4) & 0xf);
          SetPixelIndex(indexes+x,index);
          index=ConstrainColormapIndex(image,*p & 0xf);
          SetPixelIndex(indexes+x+1,index);
          p++;
        }
        if ((image->columns % 2) != 0)
          {
            index=ConstrainColormapIndex(image,(*p >> 4) & 0xf);
            SetPixelIndex(indexes+x,index);
            p++;
          }
        if (SyncAuthenticPixels(image,exception) == MagickFalse)
          break;
        if (image->previous == (Image *) NULL)
          {
            status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
              image->rows-y-1,image->rows);
            if (status == MagickFalse)
              break;
          }
      }
      (void) SyncImage(image);
      break;
    }
    case 8:
    {
      /*
        Convert PseudoColor scanline.
      */
      if ((dib_info.compression == BI_RLE8) ||
          (dib_info.compression == BI_RLE4))
        bytes_per_line=image->columns;
      for (y=(ssize_t) image->rows-1; y >= 0; y--)
      {
        p=pixels+(image->rows-y-1)*bytes_per_line;
        q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
        if (q == (PixelPacket *) NULL)
          break;
        indexes=GetAuthenticIndexQueue(image);
        for (x=0; x < (ssize_t) image->columns; x++)
        {
          index=ConstrainColormapIndex(image,*p);
          SetPixelIndex(indexes+x,index);
          p++;
          q++;
        }
        if (SyncAuthenticPixels(image,exception) == MagickFalse)
          break;
        if (image->previous == (Image *) NULL)
          {
            status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
              image->rows-y-1,image->rows);
            if (status == MagickFalse)
              break;
          }
      }
      (void) SyncImage(image);
      break;
    }
    case 16:
    {
      unsigned short
        word;

      /*
        Convert PseudoColor scanline.
      */
      image->storage_class=DirectClass;
      if (dib_info.compression == BI_RLE8)
        bytes_per_line=2*image->columns;
      for (y=(ssize_t) image->rows-1; y >= 0; y--)
      {
        p=pixels+(image->rows-y-1)*bytes_per_line;
        q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
        if (q == (PixelPacket *) NULL)
          break;
        for (x=0; x < (ssize_t) image->columns; x++)
        {
          word=(*p++);
          word|=(*p++ << 8);
          if (dib_info.red_mask == 0)
            {
              SetPixelRed(q,ScaleCharToQuantum(ScaleColor5to8(
                (unsigned char) ((word >> 10) & 0x1f))));
              SetPixelGreen(q,ScaleCharToQuantum(ScaleColor5to8(
                (unsigned char) ((word >> 5) & 0x1f))));
              SetPixelBlue(q,ScaleCharToQuantum(ScaleColor5to8(
                (unsigned char) (word & 0x1f))));
            }
          else
            {
              SetPixelRed(q,ScaleCharToQuantum(ScaleColor5to8(
                (unsigned char) ((word >> 11) & 0x1f))));
              SetPixelGreen(q,ScaleCharToQuantum(ScaleColor6to8(
                (unsigned char) ((word >> 5) & 0x3f))));
              SetPixelBlue(q,ScaleCharToQuantum(ScaleColor5to8(
                (unsigned char) (word & 0x1f))));
            }
          q++;
        }
        if (SyncAuthenticPixels(image,exception) == MagickFalse)
          break;
        if (image->previous == (Image *) NULL)
          {
            status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
              image->rows-y-1,image->rows);
            if (status == MagickFalse)
              break;
          }
      }
      break;
    }
    case 24:
    case 32:
    {
      /*
        Convert DirectColor scanline.
      */
      for (y=(ssize_t) image->rows-1; y >= 0; y--)
      {
        p=pixels+(image->rows-y-1)*bytes_per_line;
        q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
        if (q == (PixelPacket *) NULL)
          break;
        for (x=0; x < (ssize_t) image->columns; x++)
        {
          SetPixelBlue(q,ScaleCharToQuantum(*p++));
          SetPixelGreen(q,ScaleCharToQuantum(*p++));
          SetPixelRed(q,ScaleCharToQuantum(*p++));
          if (image->matte != MagickFalse)
            SetPixelOpacity(q,ScaleCharToQuantum(*p++));
          q++;
        }
        if (SyncAuthenticPixels(image,exception) == MagickFalse)
          break;
        if (image->previous == (Image *) NULL)
          {
            status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
              image->rows-y-1,image->rows);
            if (status == MagickFalse)
              break;
          }
      }
      break;
    }
    default:
    {
      pixel_info=RelinquishVirtualMemory(pixel_info);
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    }
  }
  pixel_info=RelinquishVirtualMemory(pixel_info);
  if (strcmp(image_info->magick,"ICODIB") == 0)
    {
      int
        c;

      /*
        Handle ICO mask.
      */
      image->storage_class=DirectClass;
      image->matte=MagickTrue;
      for (y=0; y < (ssize_t) image->rows; y++)
      {
        register ssize_t
          x;

        register PixelPacket
          *magick_restrict q;

        q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
        if (q == (PixelPacket *) NULL)
          break;
        for (x=0; x < ((ssize_t) image->columns-7); x+=8)
        {
          c=ReadBlobByte(image);
          for (bit=0; bit < 8; bit++)
            SetPixelOpacity(q+x+bit,c & (0x80 >> bit) ? TransparentOpacity :
              OpaqueOpacity);
        }
        if ((image->columns % 8) != 0)
          {
            c=ReadBlobByte(image);
            for (bit=0; bit < (ssize_t) (image->columns % 8); bit++)
              SetPixelOpacity(q+x+bit,c & (0x80 >> bit) ? TransparentOpacity :
                OpaqueOpacity);
          }
        if (image->columns % 32)
          for (x=0; x < (ssize_t) ((32-(image->columns % 32))/8); x++)
            c=ReadBlobByte(image);
        if (SyncAuthenticPixels(image,exception) == MagickFalse)
          break;
      }
    }
  if (dib_info.height < 0)
    {
      Image
        *flipped_image;

      /*
        Correct image orientation.
      */
      flipped_image=FlipImage(image,exception);
      if (flipped_image != (Image *) NULL)
        {
          DuplicateBlob(flipped_image,image);
          image=DestroyImage(image);
          image=flipped_image;
        }
    }
  if (EOFBlob(image) != MagickFalse)
    ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
      image->filename);
  (void) CloseBlob(image);
  return(GetFirstImageInList(image));
}


char * exif_dump_data(int *dump_free, int format, int components, int length, int motorola_intel, char *value_ptr) /* {{{ */
{
	char *dump;
	int len;

	*dump_free = 0;
	if (format == TAG_FMT_STRING) {
		return value_ptr ? value_ptr : "<no data>";
	}
	if (format == TAG_FMT_UNDEFINED) {
		return "<undefined>";
	}
	if (format == TAG_FMT_IFD) {
		return "";
	}
	if (format == TAG_FMT_SINGLE || format == TAG_FMT_DOUBLE) {
		return "<not implemented>";
	}
	*dump_free = 1;
	if (components > 1) {
		len = spprintf(&dump, 0, "(%d,%d) {", components, length);
	} else {
		len = spprintf(&dump, 0, "{");
	}
	while(components > 0) {
		switch(format) {
			case TAG_FMT_BYTE:
			case TAG_FMT_UNDEFINED:
			case TAG_FMT_STRING:
			case TAG_FMT_SBYTE:
				dump = erealloc(dump, len + 4 + 1);
				snprintf(dump + len, 4 + 1, "0x%02X", *value_ptr);
				len += 4;
				value_ptr++;
				break;
			case TAG_FMT_USHORT:
			case TAG_FMT_SSHORT:
				dump = erealloc(dump, len + 6 + 1);
				snprintf(dump + len, 6 + 1, "0x%04X", php_ifd_get16s(value_ptr, motorola_intel));
				len += 6;
				value_ptr += 2;
				break;
			case TAG_FMT_ULONG:
			case TAG_FMT_SLONG:
				dump = erealloc(dump, len + 6 + 1);
				snprintf(dump + len, 6 + 1, "0x%04X", php_ifd_get32s(value_ptr, motorola_intel));
				len += 6;
				value_ptr += 4;
				break;
			case TAG_FMT_URATIONAL:
			case TAG_FMT_SRATIONAL:
				dump = erealloc(dump, len + 13 + 1);
				snprintf(dump + len, 13 + 1, "0x%04X/0x%04X", php_ifd_get32s(value_ptr, motorola_intel), php_ifd_get32s(value_ptr+4, motorola_intel));
				len += 13;
				value_ptr += 8;
				break;
		}
		if (components > 0) {
			dump = erealloc(dump, len + 2 + 1);
			snprintf(dump + len, 2 + 1, ", ");
			len += 2;
			components--;
		} else{
			break;
		}
	}
	dump = erealloc(dump, len + 1 + 1);
	snprintf(dump + len, 1 + 1, "}");
	return dump;
}


static char *base64enc(const char *str)
{
	/* paranoia */
	unsigned len = strnlen(str, sizeof(G.wget_buf)/4*3 - 10);
	bb_uuencode(G.wget_buf, str, len, bb_uuenc_tbl_base64);
	return G.wget_buf;
}