poppler_document_set_property (GObject      *object,
			       guint         prop_id,
			       const GValue *value,
			       GParamSpec   *pspec)
{
  PopplerDocument *document = POPPLER_DOCUMENT (object);

  switch (prop_id)
    {
    case PROP_TITLE:
      poppler_document_set_title (document, g_value_get_string (value));
      break;
    case PROP_AUTHOR:
      poppler_document_set_author (document, g_value_get_string (value));
      break;
    case PROP_SUBJECT:
      poppler_document_set_subject (document, g_value_get_string (value));
      break;
    case PROP_KEYWORDS:
      poppler_document_set_keywords (document, g_value_get_string (value));
      break;
    case PROP_CREATOR:
      poppler_document_set_creator (document, g_value_get_string (value));
      break;
    case PROP_PRODUCER:
      poppler_document_set_producer (document, g_value_get_string (value));
      break;
    case PROP_CREATION_DATE:
      poppler_document_set_creation_date (document, g_value_get_int (value));
      break;
    case PROP_MOD_DATE:
      poppler_document_set_modification_date (document, g_value_get_int (value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}


poppler_attachment_save (PopplerAttachment  *attachment,
			 const char         *filename,
			 GError            **error)
{
  gboolean result;
  FILE *f;

  g_return_val_if_fail (POPPLER_IS_ATTACHMENT (attachment), FALSE);

  f = g_fopen (filename, "wb");

  if (f == nullptr)
    {
      gchar *display_name = g_filename_display_name (filename);
      g_set_error (error,
		   G_FILE_ERROR,
		   g_file_error_from_errno (errno),
		   _("Failed to open '%s' for writing: %s"),
		   display_name,
		   g_strerror (errno));
      g_free (display_name);
      return FALSE;
    }

  result = poppler_attachment_save_to_callback (attachment, save_helper, f, error);

  if (fclose (f) < 0)
    {
      gchar *display_name = g_filename_display_name (filename);
      g_set_error (error,
		   G_FILE_ERROR,
		   g_file_error_from_errno (errno),
		   _("Failed to close '%s', all data may not have been saved: %s"),
		   display_name,
		   g_strerror (errno));
      g_free (display_name);
      return FALSE;
    }

  return result;
}


poppler_ps_file_new (PopplerDocument *document, const char *filename,
		     int first_page, int n_pages)
{
	PopplerPSFile *ps_file;

	g_return_val_if_fail (POPPLER_IS_DOCUMENT (document), NULL);
	g_return_val_if_fail (filename != nullptr, NULL);
	g_return_val_if_fail (n_pages > 0, NULL);

        ps_file = (PopplerPSFile *) g_object_new (POPPLER_TYPE_PS_FILE, nullptr);
	ps_file->document = (PopplerDocument *) g_object_ref (document);
        ps_file->filename = g_strdup (filename);
        ps_file->first_page = first_page + 1;
        ps_file->last_page = first_page + 1 + n_pages - 1;

	return ps_file;
}


poppler_fonts_iter_is_embedded (PopplerFontsIter *iter)
{
	FontInfo *info;

	info = (FontInfo *)iter->items->get (iter->index);

	return info->getEmbedded();
}


poppler_fonts_iter_next (PopplerFontsIter *iter)
{
	g_return_val_if_fail (iter != nullptr, FALSE);

	iter->index++;
	if (iter->index >= iter->items->getLength())
		return FALSE;

	return TRUE;
}


get_optional_content_items (OCGs *ocg)
{
  Array *order;
  GList *items = nullptr;

  order = ocg->getOrderArray ();

  if (order) {
    items = get_optional_content_items_sorted (ocg, nullptr, order);
  } else {
    const auto &ocgs = ocg->getOCGs ();

    for (const auto &oc : ocgs) {
      Layer *layer = layer_new (oc.second.get());

      items = g_list_prepend (items, layer);
    }

    items = g_list_reverse (items);
  }

  return items;
}


poppler_index_iter_is_open (PopplerIndexIter *iter)
{
	OutlineItem *item;

	item = (OutlineItem *)iter->items->get (iter->index);

	return item->isOpen();
}


poppler_document_get_modification_date (PopplerDocument *document)
{
  g_return_val_if_fail (POPPLER_IS_DOCUMENT (document), (time_t)-1);

  GooString *str = document->doc->getDocInfoModDate();
  if (str == nullptr) {
    return (time_t)-1;
  }

  time_t date;
  gboolean success = _poppler_convert_pdf_date_to_gtime (str, &date);
  delete str;

  return (success) ? date : (time_t)-1;
}


poppler_document_class_init (PopplerDocumentClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

  gobject_class->finalize = poppler_document_finalize;
  gobject_class->get_property = poppler_document_get_property;
  gobject_class->set_property = poppler_document_set_property;

  /**
   * PopplerDocument:title:
   *
   * The document's title or %NULL
   */
  g_object_class_install_property (G_OBJECT_CLASS (klass),
				   PROP_TITLE,
				   g_param_spec_string ("title",
							"Document Title",
							"The title of the document",
							nullptr,
							G_PARAM_READWRITE));

  /**
   * PopplerDocument:format:
   *
   * The PDF version as string. See also poppler_document_get_pdf_version_string()
   */
  g_object_class_install_property (G_OBJECT_CLASS (klass),
				   PROP_FORMAT,
				   g_param_spec_string ("format",
							"PDF Format",
							"The PDF version of the document",
							nullptr,
							G_PARAM_READABLE));

  /**
   * PopplerDocument:format-major:
   *
   * The PDF major version number. See also poppler_document_get_pdf_version()
   */
  g_object_class_install_property (G_OBJECT_CLASS (klass),
				   PROP_FORMAT_MAJOR,
				   g_param_spec_uint ("format-major",
						      "PDF Format Major",
						      "The PDF major version number of the document",
						      0, G_MAXUINT, 1,
						      G_PARAM_READABLE));

  /**
   * PopplerDocument:format-minor:
   *
   * The PDF minor version number. See also poppler_document_get_pdf_version()
   */
  g_object_class_install_property (G_OBJECT_CLASS (klass),
				   PROP_FORMAT_MINOR,
				   g_param_spec_uint ("format-minor",
						      "PDF Format Minor",
						      "The PDF minor version number of the document",
						      0, G_MAXUINT, 0,
						      G_PARAM_READABLE));

  /**
   * PopplerDocument:author:
   *
   * The author of the document
   */
  g_object_class_install_property (G_OBJECT_CLASS (klass),
				   PROP_AUTHOR,
				   g_param_spec_string ("author",
							"Author",
							"The author of the document",
							nullptr,
							G_PARAM_READWRITE));

  /**
   * PopplerDocument:subject:
   *
   * The subject of the document
   */
  g_object_class_install_property (G_OBJECT_CLASS (klass),
				   PROP_SUBJECT,
				   g_param_spec_string ("subject",
							"Subject",
							"Subjects the document touches",
							nullptr,
							G_PARAM_READWRITE));

  /**
   * PopplerDocument:keywords:
   *
   * The keywords associated to the document
   */
  g_object_class_install_property (G_OBJECT_CLASS (klass),
				   PROP_KEYWORDS,
				   g_param_spec_string ("keywords",
							"Keywords",
							"Keywords",
							nullptr,
							G_PARAM_READWRITE));

  /**
   * PopplerDocument:creator:
   *
   * The creator of the document. See also poppler_document_get_creator()
   */
  g_object_class_install_property (G_OBJECT_CLASS (klass),
				   PROP_CREATOR,
				   g_param_spec_string ("creator",
							"Creator",
							"The software that created the document",
							nullptr,
							G_PARAM_READWRITE));

  /**
   * PopplerDocument:producer:
   *
   * The producer of the document. See also poppler_document_get_producer()
   */
  g_object_class_install_property (G_OBJECT_CLASS (klass),
				   PROP_PRODUCER,
				   g_param_spec_string ("producer",
							"Producer",
							"The software that converted the document",
							nullptr,
							G_PARAM_READWRITE));

  /**
   * PopplerDocument:creation-date:
   *
   * The date the document was created as seconds since the Epoch, or -1
   */
  g_object_class_install_property (G_OBJECT_CLASS (klass),
				   PROP_CREATION_DATE,
				   g_param_spec_int ("creation-date",
						     "Creation Date",
						     "The date and time the document was created",
						     -1, G_MAXINT, -1,
						     G_PARAM_READWRITE));

  /**
   * PopplerDocument:mod-date:
   *
   * The date the document was most recently modified as seconds since the Epoch, or -1
   */
  g_object_class_install_property (G_OBJECT_CLASS (klass),
				   PROP_MOD_DATE,
				   g_param_spec_int ("mod-date",
						     "Modification Date",
						     "The date and time the document was modified",
						     -1, G_MAXINT, -1,
						     G_PARAM_READWRITE));

  /**
   * PopplerDocument:linearized:
   *
   * Whether document is linearized. See also poppler_document_is_linearized()
   */
  g_object_class_install_property (G_OBJECT_CLASS (klass),
				   PROP_LINEARIZED,
				   g_param_spec_boolean ("linearized",
							 "Fast Web View Enabled",
							 "Is the document optimized for web viewing?",
							 FALSE,
							 G_PARAM_READABLE));

  /**
   * PopplerDocument:page-layout:
   *
   * The page layout that should be used when the document is opened
   */
  g_object_class_install_property (G_OBJECT_CLASS (klass),
				   PROP_PAGE_LAYOUT,
				   g_param_spec_enum ("page-layout",
						      "Page Layout",
						      "Initial Page Layout",
						      POPPLER_TYPE_PAGE_LAYOUT,
						      POPPLER_PAGE_LAYOUT_UNSET,
						      G_PARAM_READABLE));

  /**
   * PopplerDocument:page-mode:
   *
   * The mode that should be used when the document is opened
   */
  g_object_class_install_property (G_OBJECT_CLASS (klass),
				   PROP_PAGE_MODE,
				   g_param_spec_enum ("page-mode",
						      "Page Mode",
						      "Page Mode",
						      POPPLER_TYPE_PAGE_MODE,
						      POPPLER_PAGE_MODE_UNSET,
						      G_PARAM_READABLE));

  /**
   * PopplerDocument:viewer-preferences:
   */
  g_object_class_install_property (G_OBJECT_CLASS (klass),
				   PROP_VIEWER_PREFERENCES,
				   g_param_spec_flags ("viewer-preferences",
						       "Viewer Preferences",
						       "Viewer Preferences",
						       POPPLER_TYPE_VIEWER_PREFERENCES,
						       POPPLER_VIEWER_PREFERENCES_UNSET,
						       G_PARAM_READABLE));

  /**
   * PopplerDocument:permissions:
   *
   * Flags specifying which operations are permitted when the document is opened
   */
  g_object_class_install_property (G_OBJECT_CLASS (klass),
				   PROP_PERMISSIONS,
				   g_param_spec_flags ("permissions",
						       "Permissions",
						       "Permissions",
						       POPPLER_TYPE_PERMISSIONS,
						       POPPLER_PERMISSIONS_FULL,
						       G_PARAM_READABLE));

  /**
   * PopplerDocument:metadata:
   *
   * Document metadata in XML format, or %NULL
   */
  g_object_class_install_property (G_OBJECT_CLASS (klass),
				   PROP_METADATA,
				   g_param_spec_string ("metadata",
							"XML Metadata",
							"Embedded XML metadata",
							nullptr,
							G_PARAM_READABLE));
}


poppler_index_iter_new (PopplerDocument *document)
{
	PopplerIndexIter *iter;
	Outline *outline;
	const GooList *items;

	outline = document->doc->getOutline();
	if (outline == nullptr)
		return nullptr;

	items = outline->getItems();
	if (items == nullptr)
		return nullptr;

	iter = g_slice_new (PopplerIndexIter);
	iter->document = (PopplerDocument *) g_object_ref (document);
	iter->items = items;
	iter->index = 0;

	return iter;
}


save_helper (const gchar  *buf,
	     gsize         count,
	     gpointer      data,
	     GError      **error)
{
  FILE *f = (FILE *) data;
  gsize n;

  n = fwrite (buf, 1, count, f);
  if (n != count)
    {
      g_set_error (error,
		   G_FILE_ERROR,
		   g_file_error_from_errno (errno),
		   _("Error writing to image file: %s"),
		   g_strerror (errno));
      return FALSE;
    }

  return TRUE;
}


poppler_layers_iter_get_child (PopplerLayersIter *parent)
{
  PopplerLayersIter *child;
  Layer *layer;

  g_return_val_if_fail (parent != nullptr, NULL);

  layer = (Layer *) g_list_nth_data (parent->items, parent->index);
  if (!layer || !layer->kids)
    return nullptr;

  child = g_slice_new0 (PopplerLayersIter);
  child->document = (PopplerDocument *)g_object_ref (parent->document);
  child->items = layer->kids;

  g_assert (child->items);

  return child;
}


ZEND_METHOD(Generator, current)
{
	zend_generator *generator, *root;

	if (zend_parse_parameters_none() == FAILURE) {
		return;
	}

	generator = (zend_generator *) Z_OBJ_P(getThis());

	zend_generator_ensure_initialized(generator);

	root = zend_generator_get_current(generator);
	if (EXPECTED(generator->execute_data != NULL && Z_TYPE(root->value) != IS_UNDEF)) {
		zval *value = &root->value;

		ZVAL_DEREF(value);
		ZVAL_COPY(return_value, value);
	}
}


ZEND_METHOD(Generator, send)
{
	zval *value;
	zend_generator *generator, *root;

	ZEND_PARSE_PARAMETERS_START(1, 1)
		Z_PARAM_ZVAL(value)
	ZEND_PARSE_PARAMETERS_END();

	generator = (zend_generator *) Z_OBJ_P(getThis());

	zend_generator_ensure_initialized(generator);

	/* The generator is already closed, thus can't send anything */
	if (UNEXPECTED(!generator->execute_data)) {
		return;
	}

	root = zend_generator_get_current(generator);
	/* Put sent value in the target VAR slot, if it is used */
	if (root->send_target) {
		ZVAL_COPY(root->send_target, value);
	}

	zend_generator_resume(generator);

	root = zend_generator_get_current(generator);
	if (EXPECTED(generator->execute_data)) {
		zval *value = &root->value;

		ZVAL_DEREF(value);
		ZVAL_COPY(return_value, value);
	}
}


static void zend_generator_throw_exception(zend_generator *generator, zval *exception)
{
	zend_execute_data *original_execute_data = EG(current_execute_data);

	/* if we don't stop an array/iterator yield from, the exception will only reach the generator after the values were all iterated over */
	if (UNEXPECTED(Z_TYPE(generator->values) != IS_UNDEF)) {
		zval_ptr_dtor(&generator->values);
		ZVAL_UNDEF(&generator->values);
	}

	/* Throw the exception in the context of the generator. Decrementing the opline
	 * to pretend the exception happened during the YIELD opcode. */
	EG(current_execute_data) = generator->execute_data;
	generator->execute_data->opline--;
	if (exception) {
		zend_throw_exception_object(exception);
	} else {
		zend_throw_exception_internal(NULL);
	}
	generator->execute_data->opline++;
	EG(current_execute_data) = original_execute_data;
}


ipvs_cmd(int cmd, virtual_server_t *vs, real_server_t *rs)
{
	ipvs_service_t srule;
	ipvs_dest_t drule;

	/* Allocate the room */
	ipvs_set_srule(cmd, &srule, vs);
	if (rs) {
		ipvs_set_drule(cmd, &drule, rs);

		/* Does the service use inhibit flag ? */
		if (cmd == IP_VS_SO_SET_DELDEST && rs->inhibit) {
			drule.user.weight = 0;
			cmd = IP_VS_SO_SET_EDITDEST;
		}
		else if (cmd == IP_VS_SO_SET_ADDDEST && rs->inhibit && rs->set)
			cmd = IP_VS_SO_SET_EDITDEST;

		/* Set flag */
		else if (cmd == IP_VS_SO_SET_ADDDEST && !rs->set) {
			rs->set = true;
			if (rs->inhibit && rs->num_failed_checkers)
				drule.user.weight = 0;
		}
		else if (cmd == IP_VS_SO_SET_DELDEST && rs->set)
			rs->set = false;
	}

	/* Set vs rule and send to kernel */
	if (vs->vsg)
		return ipvs_group_cmd(cmd, &srule, &drule, vs, rs);

	if (vs->vfwmark) {
		srule.user.fwmark = vs->vfwmark;
	} else {
		if (vs->af == AF_INET6)
			inet_sockaddrip6(&vs->addr, &srule.nf_addr.in6);
		else
			srule.nf_addr.ip = inet_sockaddrip4(&vs->addr);
		srule.user.port = inet_sockaddrport(&vs->addr);
	}

	/* Talk to the IPVS channel */
	return ipvs_talk(cmd, &srule, &drule, NULL, false);
}


open_bfd_pipes(void)
{
#ifdef _WITH_VRRP_
	/* Open BFD VRRP control pipe */
	if (open_pipe(bfd_vrrp_event_pipe) == -1) {
		log_message(LOG_ERR, "Unable to create BFD vrrp event pipe: %m");
		stop_keepalived();
		return;
	}
#endif

#ifdef _WITH_LVS_
	/* Open BFD checker control pipe */
	if (open_pipe(bfd_checker_event_pipe) == -1) {
		log_message(LOG_ERR, "Unable to create BFD checker event pipe: %m");
		stop_keepalived();
		return;
	}
#endif
}


stop_vrrp(int status)
{
	if (__test_bit(CONFIG_TEST_BIT, &debug))
		return;

	/* This runs in the main process, not in the context of a thread */
	vrrp_terminate_phase1(false);

	vrrp_terminate_phase2(status);

	/* unreachable */
	exit(status);
}


start_check(list old_checkers_queue, data_t *old_global_data)
{
	init_checkers_queue();

	/* Parse configuration file */
	if (reload)
		global_data = alloc_global_data();
	check_data = alloc_check_data();
	if (!check_data) {
		stop_check(KEEPALIVED_EXIT_FATAL);
		return;
	}

	init_data(conf_file, check_init_keywords);

	if (reload)
		init_global_data(global_data, old_global_data);

	/* fill 'vsg' members of the virtual_server_t structure.
	 * We must do that after parsing config, because
	 * vs and vsg declarations may appear in any order,
	 * but we must do it before validate_check_config().
	 */
	link_vsg_to_vs();

	/* Post initializations */
	if (!validate_check_config()) {
		stop_check(KEEPALIVED_EXIT_CONFIG);
		return;
	}

#ifdef _MEM_CHECK_
	log_message(LOG_INFO, "Configuration is using : %zu Bytes", mem_allocated);
#endif

	/* If we are just testing the configuration, then we terminate now */
	if (__test_bit(CONFIG_TEST_BIT, &debug))
		return;

	/* Initialize sub-system if any virtual servers are configured */
	if ((!LIST_ISEMPTY(check_data->vs) || (reload && !LIST_ISEMPTY(old_check_data->vs))) &&
	    ipvs_start() != IPVS_SUCCESS) {
		stop_check(KEEPALIVED_EXIT_FATAL);
		return;
	}

	/* Create a notify FIFO if needed, and open it */
	notify_fifo_open(&global_data->notify_fifo, &global_data->lvs_notify_fifo, lvs_notify_fifo_script_exit, "lvs_");

	/* Get current active addresses, and start update process */
	if (using_ha_suspend || __test_bit(LOG_ADDRESS_CHANGES, &debug)) {
		if (reload)
			kernel_netlink_set_recv_bufs();
		else
			kernel_netlink_init();
	}
	else if (reload)
		kernel_netlink_close();

	/* Remove any entries left over from previous invocation */
	if (!reload && global_data->lvs_flush)
		ipvs_flush_cmd();

#ifdef _WITH_SNMP_CHECKER_
	if (!reload && global_data->enable_snmp_checker)
		check_snmp_agent_init(global_data->snmp_socket);
#endif

	/* SSL load static data & initialize common ctx context */
	if (check_data->ssl_required && !init_ssl_ctx())
		stop_check(KEEPALIVED_EXIT_FATAL);

	/* Processing differential configuration parsing */
	if (reload)
		clear_diff_services(old_checkers_queue);

	/* We can send SMTP messages from here so set the time */
	set_time_now();

	/* Initialize IPVS topology */
	if (!init_services())
		stop_check(KEEPALIVED_EXIT_FATAL);

	/* Dump configuration */
	if (__test_bit(DUMP_CONF_BIT, &debug)) {
		dump_global_data(NULL, global_data);
		dump_check_data(NULL, check_data);
	}

	/* Register checkers thread */
	register_checkers_thread();

	/* Set the process priority and non swappable if configured */
	set_process_priorities(
#ifdef _HAVE_SCHED_RT_
			       global_data->checker_realtime_priority,
#if HAVE_DECL_RLIMIT_RTTIME == 1
			       global_data->checker_rlimit_rt,
#endif
#endif
			       global_data->checker_process_priority, global_data->checker_no_swap ? 4096 : 0);
}


ipvs_change_needed(int cmd, virtual_server_group_entry_t *vsge, virtual_server_t *vs, real_server_t *rs)
{
	unsigned count;

	if (cmd == IP_VS_SO_SET_ADD)
		return !is_vsge_alive(vsge, vs);
	else if (cmd == IP_VS_SO_SET_DEL) {
		count = vsge->is_fwmark ? (vs->af == AF_INET ? vsge->fwm4_alive : vsge->fwm6_alive) :
			vs->service_type == IPPROTO_TCP ? vsge->tcp_alive :
			vs->service_type == IPPROTO_UDP ? vsge->udp_alive : vsge->sctp_alive;

		return (count == 1);
	}
	else if (cmd == IP_VS_SO_SET_ADDDEST)
		return !rs->alive;
	else if (cmd == IP_VS_SO_SET_DELDEST)
		return rs->alive;
	else /* cmd == IP_VS_SO_SET_EDITDEST */
		return true;
}


vrrp_ipvs_needed(void)
{
	return !!(global_data->lvs_syncd.ifname);
}


usage(const char *prog)
{
	fprintf(stderr, "Usage: %s [OPTION...]\n", prog);
	fprintf(stderr, "  -f, --use-file=FILE          Use the specified configuration file\n");
#if defined _WITH_VRRP_ && defined _WITH_LVS_
	fprintf(stderr, "  -P, --vrrp                   Only run with VRRP subsystem\n");
	fprintf(stderr, "  -C, --check                  Only run with Health-checker subsystem\n");
#endif
#ifdef _WITH_BFD_
	fprintf(stderr, "  -B, --no_bfd                 Don't run BFD subsystem\n");
#endif
	fprintf(stderr, "      --all                    Force all child processes to run, even if have no configuration\n");
	fprintf(stderr, "  -l, --log-console            Log messages to local console\n");
	fprintf(stderr, "  -D, --log-detail             Detailed log messages\n");
	fprintf(stderr, "  -S, --log-facility=[0-7]     Set syslog facility to LOG_LOCAL[0-7]\n");
#ifdef ENABLE_LOG_TO_FILE
	fprintf(stderr, "  -g, --log-file=FILE          Also log to FILE (default /tmp/keepalived.log)\n");
	fprintf(stderr, "      --flush-log-file         Flush log file on write\n");
#endif
	fprintf(stderr, "  -G, --no-syslog              Don't log via syslog\n");
	fprintf(stderr, "  -u, --umask=MASK             umask for file creation (in numeric form)\n");
#ifdef _WITH_VRRP_
	fprintf(stderr, "  -X, --release-vips           Drop VIP on transition from signal.\n");
	fprintf(stderr, "  -V, --dont-release-vrrp      Don't remove VRRP VIPs and VROUTEs on daemon stop\n");
#endif
#ifdef _WITH_LVS_
	fprintf(stderr, "  -I, --dont-release-ipvs      Don't remove IPVS topology on daemon stop\n");
#endif
	fprintf(stderr, "  -R, --dont-respawn           Don't respawn child processes\n");
	fprintf(stderr, "  -n, --dont-fork              Don't fork the daemon process\n");
	fprintf(stderr, "  -d, --dump-conf              Dump the configuration data\n");
	fprintf(stderr, "  -p, --pid=FILE               Use specified pidfile for parent process\n");
#ifdef _WITH_VRRP_
	fprintf(stderr, "  -r, --vrrp_pid=FILE          Use specified pidfile for VRRP child process\n");
#endif
#ifdef _WITH_LVS_
	fprintf(stderr, "  -c, --checkers_pid=FILE      Use specified pidfile for checkers child process\n");
	fprintf(stderr, "  -a, --address-monitoring     Report all address additions/deletions notified via netlink\n");
#endif
#ifdef _WITH_BFD_
	fprintf(stderr, "  -b, --bfd_pid=FILE           Use specified pidfile for BFD child process\n");
#endif
#ifdef _WITH_SNMP_
	fprintf(stderr, "  -x, --snmp                   Enable SNMP subsystem\n");
	fprintf(stderr, "  -A, --snmp-agent-socket=FILE Use the specified socket for master agent\n");
#endif
#if HAVE_DECL_CLONE_NEWNET
	fprintf(stderr, "  -s, --namespace=NAME         Run in network namespace NAME (overrides config)\n");
#endif
	fprintf(stderr, "  -m, --core-dump              Produce core dump if terminate abnormally\n");
	fprintf(stderr, "  -M, --core-dump-pattern=PATN Also set /proc/sys/kernel/core_pattern to PATN (default 'core')\n");
#ifdef _MEM_CHECK_LOG_
	fprintf(stderr, "  -L, --mem-check-log          Log malloc/frees to syslog\n");
#endif
	fprintf(stderr, "  -i, --config-id id           Skip any configuration lines beginning '@' that don't match id\n"
			"                                or any lines beginning @^ that do match.\n"
			"                                The config-id defaults to the node name if option not used\n");
	fprintf(stderr, "      --signum=SIGFUNC         Return signal number for STOP, RELOAD, DATA, STATS"
#ifdef _WITH_JSON_
								", JSON"
#endif
								"\n");
	fprintf(stderr, "  -t, --config-test[=LOG_FILE] Check the configuration for obvious errors, output to\n"
			"                                stderr by default\n");
#ifdef _WITH_PERF_
	fprintf(stderr, "      --perf[=PERF_TYPE]       Collect perf data, PERF_TYPE=all, run(default) or end\n");
#endif
#ifdef WITH_DEBUG_OPTIONS
	fprintf(stderr, "      --debug[=...]            Enable debug options. p, b, c, v specify parent, bfd, checker and vrrp processes\n");
#ifdef _TIMER_CHECK_
	fprintf(stderr, "                                   T - timer debug\n");
#endif
#ifdef _SMTP_ALERT_DEBUG_
	fprintf(stderr, "                                   M - email alert debug\n");
#endif
#ifdef _EPOLL_DEBUG_
	fprintf(stderr, "                                   E - epoll debug\n");
#endif
#ifdef _EPOLL_THREAD_DUMP_
	fprintf(stderr, "                                   D - epoll thread dump debug\n");
#endif
#ifdef _VRRP_FD_DEBUG
	fprintf(stderr, "                                   F - vrrp fd dump debug\n");
#endif
#ifdef _REGEX_DEBUG_
	fprintf(stderr, "                                   R - regex debug\n");
#endif
#ifdef _WITH_REGEX_TIMERS_
	fprintf(stderr, "                                   X - regex timers\n");
#endif
#ifdef _TSM_DEBUG_
	fprintf(stderr, "                                   S - TSM debug\n");
#endif
#ifdef _NETLINK_TIMERS_
	fprintf(stderr, "                                   N - netlink timer debug\n");
#endif
	fprintf(stderr, "                                 Example --debug=TpMEvcp\n");
#endif
	fprintf(stderr, "  -v, --version                Display the version number\n");
	fprintf(stderr, "  -h, --help                   Display this help message\n");
}


ipvs_get_group_by_name(char *gname, list l)
{
	element e;
	virtual_server_group_t *vsg;

	for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) {
		vsg = ELEMENT_DATA(e);
		if (!strcmp(vsg->gname, gname))
			return vsg;
	}
	return NULL;
}


tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx)
{
	struct window_pane	*wp = ctx->wp;
	struct screen		*s = wp->screen;
	u_int			 i;

	/*
	 * If region is large, schedule a window redraw. In most cases this is
	 * likely to be followed by some more scrolling.
	 */
	if (tty_large_region(tty, ctx)) {
		wp->flags |= PANE_REDRAW;
		return;
	}

	if (ctx->ocy < ctx->orupper || ctx->ocy > ctx->orlower) {
		for (i = ctx->ocy; i < screen_size_y(s); i++)
			tty_draw_pane(tty, ctx, i);
	} else {
		for (i = ctx->orupper; i <= ctx->orlower; i++)
			tty_draw_pane(tty, ctx, i);
	}
}


format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb)
{
	struct timeval	 tv;
	size_t		 size;
	char		*s;

	timerclear(&tv);
	tv.tv_sec = paste_buffer_created(pb);
	paste_buffer_data(pb, &size);

	format_add(ft, "buffer_size", "%zu", size);
	format_add(ft, "buffer_name", "%s", paste_buffer_name(pb));
	format_add_tv(ft, "buffer_created", &tv);

	s = paste_make_sample(pb);
	format_add(ft, "buffer_sample", "%s", s);
	free(s);
}


tty_attributes(struct tty *tty, const struct grid_cell *gc,
    const struct window_pane *wp)
{
	struct grid_cell	*tc = &tty->cell, gc2;
	int			 changed;

	/* Ignore cell if it is the same as the last one. */
	if (wp != NULL &&
	    (int)wp->id == tty->last_wp &&
	    ~(wp->window->flags & WINDOW_STYLECHANGED) &&
	    gc->attr == tty->last_cell.attr &&
	    gc->fg == tty->last_cell.fg &&
	    gc->bg == tty->last_cell.bg)
		return;
	tty->last_wp = (wp != NULL ? (int)wp->id : -1);
	memcpy(&tty->last_cell, gc, sizeof tty->last_cell);

	/* Copy cell and update default colours. */
	memcpy(&gc2, gc, sizeof gc2);
	if (wp != NULL)
		tty_default_colours(&gc2, wp);

	/*
	 * If no setab, try to use the reverse attribute as a best-effort for a
	 * non-default background. This is a bit of a hack but it doesn't do
	 * any serious harm and makes a couple of applications happier.
	 */
	if (!tty_term_has(tty->term, TTYC_SETAB)) {
		if (gc2.attr & GRID_ATTR_REVERSE) {
			if (gc2.fg != 7 && !COLOUR_DEFAULT(gc2.fg))
				gc2.attr &= ~GRID_ATTR_REVERSE;
		} else {
			if (gc2.bg != 0 && !COLOUR_DEFAULT(gc2.bg))
				gc2.attr |= GRID_ATTR_REVERSE;
		}
	}

	/* Fix up the colours if necessary. */
	tty_check_fg(tty, wp, &gc2);
	tty_check_bg(tty, wp, &gc2);

	/* If any bits are being cleared, reset everything. */
	if (tc->attr & ~gc2.attr)
		tty_reset(tty);

	/*
	 * Set the colours. This may call tty_reset() (so it comes next) and
	 * may add to (NOT remove) the desired attributes by changing new_attr.
	 */
	tty_colours(tty, &gc2);

	/* Filter out attribute bits already set. */
	changed = gc2.attr & ~tc->attr;
	tc->attr = gc2.attr;

	/* Set the attributes. */
	if (changed & GRID_ATTR_BRIGHT)
		tty_putcode(tty, TTYC_BOLD);
	if (changed & GRID_ATTR_DIM)
		tty_putcode(tty, TTYC_DIM);
	if (changed & GRID_ATTR_ITALICS)
		tty_set_italics(tty);
	if (changed & GRID_ATTR_ALL_UNDERSCORE) {
		if ((changed & GRID_ATTR_UNDERSCORE) ||
		    !tty_term_has(tty->term, TTYC_SMULX))
			tty_putcode(tty, TTYC_SMUL);
		else if (changed & GRID_ATTR_UNDERSCORE_2)
			tty_putcode1(tty, TTYC_SMULX, 2);
		else if (changed & GRID_ATTR_UNDERSCORE_3)
			tty_putcode1(tty, TTYC_SMULX, 3);
		else if (changed & GRID_ATTR_UNDERSCORE_4)
			tty_putcode1(tty, TTYC_SMULX, 4);
		else if (changed & GRID_ATTR_UNDERSCORE_5)
			tty_putcode1(tty, TTYC_SMULX, 5);
	}
	if (changed & GRID_ATTR_BLINK)
		tty_putcode(tty, TTYC_BLINK);
	if (changed & GRID_ATTR_REVERSE) {
		if (tty_term_has(tty->term, TTYC_REV))
			tty_putcode(tty, TTYC_REV);
		else if (tty_term_has(tty->term, TTYC_SMSO))
			tty_putcode(tty, TTYC_SMSO);
	}
	if (changed & GRID_ATTR_HIDDEN)
		tty_putcode(tty, TTYC_INVIS);
	if (changed & GRID_ATTR_STRIKETHROUGH)
		tty_putcode(tty, TTYC_SMXX);
	if ((changed & GRID_ATTR_CHARSET) && tty_acs_needed(tty))
		tty_putcode(tty, TTYC_SMACS);
}


format_entry_cmp(struct format_entry *fe1, struct format_entry *fe2)
{
	return (strcmp(fe1->key, fe2->key));
}


server_client_set_title(struct client *c)
{
	struct session		*s = c->session;
	const char		*template;
	char			*title;
	struct format_tree	*ft;

	template = options_get_string(s->options, "set-titles-string");

	ft = format_create(c, NULL, FORMAT_NONE, 0);
	format_defaults(ft, c, NULL, NULL, NULL);

	title = format_expand_time(ft, template, time(NULL));
	if (c->title == NULL || strcmp(title, c->title) != 0) {
		free(c->title);
		c->title = xstrdup(title);
		tty_set_title(&c->tty, c->title);
	}
	free(title);

	format_free(ft);
}


tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
{
	struct window_pane	*wp = ctx->wp;

	if (ctx->ocy != ctx->orupper)
		return;

	if (ctx->bigger ||
	    !tty_pane_full_width(tty, ctx) ||
	    tty_fake_bce(tty, wp, 8) ||
	    !tty_term_has(tty->term, TTYC_CSR) ||
	    !tty_term_has(tty->term, TTYC_RI) ||
	    ctx->wp->sx == 1 ||
	    ctx->wp->sy == 1) {
		tty_redraw_region(tty, ctx);
		return;
	}

	tty_default_attributes(tty, wp, ctx->bg);

	tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
	tty_margin_off(tty);
	tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper);

	tty_putcode(tty, TTYC_RI);
}


server_client_stdout_cb(__unused int fd, __unused short events, void *arg)
{
	struct client	*c = arg;

	if (~c->flags & CLIENT_DEAD)
		server_client_push_stdout(c);
	server_client_unref(c);
}


window_create(u_int sx, u_int sy)
{
	struct window	*w;

	w = xcalloc(1, sizeof *w);
	w->name = NULL;
	w->flags = WINDOW_STYLECHANGED;

	TAILQ_INIT(&w->panes);
	w->active = NULL;

	w->lastlayout = -1;
	w->layout_root = NULL;

	w->sx = sx;
	w->sy = sy;

	w->options = options_create(global_w_options);

	w->references = 0;
	TAILQ_INIT(&w->winlinks);

	w->id = next_window_id++;
	RB_INSERT(windows, &windows, w);

	window_update_activity(w);

	return (w);
}


server_client_how_many(void)
{
	struct client  	*c;
	u_int		 n;

	n = 0;
	TAILQ_FOREACH(c, &clients, entry) {
		if (c->session != NULL && (~c->flags & CLIENT_DETACHING))
			n++;
	}
	return (n);
}


input_csi_dispatch_sm(struct input_ctx *ictx)
{
	u_int	i;

	for (i = 0; i < ictx->param_list_len; i++) {
		switch (input_get(ictx, i, 0, -1)) {
		case -1:
			break;
		case 4:		/* IRM */
			screen_write_mode_set(&ictx->ctx, MODE_INSERT);
			break;
		case 34:
			screen_write_mode_clear(&ictx->ctx, MODE_BLINKING);
			break;
		default:
			log_debug("%s: unknown '%c'", __func__, ictx->ch);
			break;
		}
	}
}


input_osc_104(struct input_ctx *ictx, const char *p)
{
	struct window_pane	*wp = ictx->wp;
	char			*copy, *s;
	long			idx;

	if (*p == '\0') {
		window_pane_reset_palette(wp);
		return;
	}

	copy = s = xstrdup(p);
	while (*s != '\0') {
		idx = strtol(s, &s, 10);
		if (*s != '\0' && *s != ';')
			goto bad;
		if (idx < 0 || idx >= 0x100)
			goto bad;

		window_pane_unset_palette(wp, idx);
		if (*s == ';')
			s++;
	}
	free(copy);
	return;

bad:
	log_debug("bad OSC 104: %s", p);
	free(copy);
}


format_add(struct format_tree *ft, const char *key, const char *fmt, ...)
{
	struct format_entry	*fe;
	struct format_entry	*fe_now;
	va_list			 ap;

	fe = xmalloc(sizeof *fe);
	fe->key = xstrdup(key);

	fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe);
	if (fe_now != NULL) {
		free(fe->key);
		free(fe);
		free(fe_now->value);
		fe = fe_now;
	}

	fe->cb = NULL;
	fe->t = 0;

	va_start(ap, fmt);
	xvasprintf(&fe->value, fmt, ap);
	va_end(ap);
}


tty_set_italics(struct tty *tty)
{
	const char	*s;

	if (tty_term_has(tty->term, TTYC_SITM)) {
		s = options_get_string(global_options, "default-terminal");
		if (strcmp(s, "screen") != 0 && strncmp(s, "screen-", 7) != 0) {
			tty_putcode(tty, TTYC_SITM);
			return;
		}
	}
	tty_putcode(tty, TTYC_SMSO);
}


tty_putn(struct tty *tty, const void *buf, size_t len, u_int width)
{
	tty_add(tty, buf, len);
	if (tty->cx + width > tty->sx) {
		tty->cx = (tty->cx + width) - tty->sx;
		if (tty->cx <= tty->sx)
			tty->cy++;
		else
			tty->cx = tty->cy = UINT_MAX;
	} else
		tty->cx += width;
}


tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
{
	struct window_pane	*wp = ctx->wp;

	if (ctx->ocy != ctx->orlower)
		return;

	if (ctx->bigger ||
	    (!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) ||
	    tty_fake_bce(tty, wp, 8) ||
	    !tty_term_has(tty->term, TTYC_CSR) ||
	    wp->sx == 1 ||
	    wp->sy == 1) {
		tty_redraw_region(tty, ctx);
		return;
	}

	tty_default_attributes(tty, wp, ctx->bg);

	tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
	tty_margin_pane(tty, ctx);

	/*
	 * If we want to wrap a pane while using margins, the cursor needs to
	 * be exactly on the right of the region. If the cursor is entirely off
	 * the edge - move it back to the right. Some terminals are funny about
	 * this and insert extra spaces, so only use the right if margins are
	 * enabled.
	 */
	if (ctx->xoff + ctx->ocx > tty->rright) {
		if (!tty_use_margin(tty))
			tty_cursor(tty, 0, ctx->yoff + ctx->ocy);
		else
			tty_cursor(tty, tty->rright, ctx->yoff + ctx->ocy);
	} else
		tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);

	tty_putc(tty, '\n');
}


input_parse(struct window_pane *wp)
{
	struct input_ctx		*ictx = wp->ictx;
	const struct input_transition	*itr;
	struct evbuffer			*evb = wp->event->input;
	u_char				*buf;
	size_t				 len, off;

	if (EVBUFFER_LENGTH(evb) == 0)
		return;

	window_update_activity(wp->window);
	wp->flags |= PANE_CHANGED;

	/*
	 * Open the screen. Use NULL wp if there is a mode set as don't want to
	 * update the tty.
	 */
	if (wp->mode == NULL)
		screen_write_start(&ictx->ctx, wp, &wp->base);
	else
		screen_write_start(&ictx->ctx, NULL, &wp->base);
	ictx->wp = wp;

	buf = EVBUFFER_DATA(evb);
	len = EVBUFFER_LENGTH(evb);
	off = 0;

	notify_input(wp, evb);

	log_debug("%s: %%%u %s, %zu bytes: %.*s", __func__, wp->id,
	    ictx->state->name, len, (int)len, buf);

	/* Parse the input. */
	while (off < len) {
		ictx->ch = buf[off++];

		/* Find the transition. */
		itr = ictx->state->transitions;
		while (itr->first != -1 && itr->last != -1) {
			if (ictx->ch >= itr->first && ictx->ch <= itr->last)
				break;
			itr++;
		}
		if (itr->first == -1 || itr->last == -1) {
			/* No transition? Eh? */
			fatalx("no transition from state");
		}

		/*
		 * Any state except print stops the current collection. This is
		 * an optimization to avoid checking if the attributes have
		 * changed for every character. It will stop unnecessarily for
		 * sequences that don't make a terminal change, but they should
		 * be the minority.
		 */
		if (itr->handler != input_print)
			screen_write_collect_end(&ictx->ctx);

		/*
		 * Execute the handler, if any. Don't switch state if it
		 * returns non-zero.
		 */
		if (itr->handler != NULL && itr->handler(ictx) != 0)
			continue;

		/* And switch state, if necessary. */
		if (itr->state != NULL)
			input_set_state(wp, itr);

		/* If not in ground state, save input. */
		if (ictx->state != &input_state_ground)
			evbuffer_add(ictx->since_ground, &ictx->ch, 1);
	}

	/* Close the screen. */
	screen_write_stop(&ictx->ctx);

	evbuffer_drain(evb, len);
}


format_add_cb(struct format_tree *ft, const char *key, format_cb cb)
{
	struct format_entry	*fe;
	struct format_entry	*fe_now;

	fe = xmalloc(sizeof *fe);
	fe->key = xstrdup(key);

	fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe);
	if (fe_now != NULL) {
		free(fe->key);
		free(fe);
		free(fe_now->value);
		fe = fe_now;
	}

	fe->cb = cb;
	fe->t = 0;

	fe->value = NULL;
}


window_pane_next_by_number(struct window *w, struct window_pane *wp, u_int n)
{
	for (; n > 0; n--) {
		if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
			wp = TAILQ_FIRST(&w->panes);
	}

	return (wp);
}


tty_check_bg(struct tty *tty, const struct window_pane *wp,
    struct grid_cell *gc)
{
	u_char	r, g, b;
	u_int	colours;
	int	c;

	/* Perform substitution if this pane has a palette. */
	if (~gc->flags & GRID_FLAG_NOPALETTE) {
		if ((c = window_pane_get_palette(wp, gc->bg)) != -1)
			gc->bg = c;
	}

	/* Is this a 24-bit colour? */
	if (gc->bg & COLOUR_FLAG_RGB) {
		/* Not a 24-bit terminal? Translate to 256-colour palette. */
		if (!tty_term_has(tty->term, TTYC_SETRGBB)) {
			colour_split_rgb(gc->bg, &r, &g, &b);
			gc->bg = colour_find_rgb(r, g, b);
		} else
			return;
	}

	/* How many colours does this terminal have? */
	if ((tty->term->flags|tty->term_flags) & TERM_256COLOURS)
		colours = 256;
	else
		colours = tty_term_number(tty->term, TTYC_COLORS);

	/* Is this a 256-colour colour? */
	if (gc->bg & COLOUR_FLAG_256) {
		/*
		 * And not a 256 colour mode? Translate to 16-colour
		 * palette. Bold background doesn't exist portably, so just
		 * discard the bold bit if set.
		 */
		if (colours != 256) {
			gc->bg = colour_256to16(gc->bg);
			if (gc->bg & 8) {
				gc->bg &= 7;
				if (colours >= 16)
					gc->fg += 90;
			}
		}
		return;
	}

	/* Is this an aixterm colour? */
	if (gc->bg >= 90 && gc->bg <= 97 && colours < 16)
		gc->bg -= 90;
}


server_client_check_focus(struct window_pane *wp)
{
	struct client	*c;
	int		 push;

	/* Do we need to push the focus state? */
	push = wp->flags & PANE_FOCUSPUSH;
	wp->flags &= ~PANE_FOCUSPUSH;

	/* If we're not the active pane in our window, we're not focused. */
	if (wp->window->active != wp)
		goto not_focused;

	/* If we're in a mode, we're not focused. */
	if (wp->screen != &wp->base)
		goto not_focused;

	/*
	 * If our window is the current window in any focused clients with an
	 * attached session, we're focused.
	 */
	TAILQ_FOREACH(c, &clients, entry) {
		if (c->session == NULL || !(c->flags & CLIENT_FOCUSED))
			continue;
		if (c->session->attached == 0)
			continue;

		if (c->session->curw->window == wp->window)
			goto focused;
	}

not_focused:
	if (push || (wp->flags & PANE_FOCUSED)) {
		if (wp->base.mode & MODE_FOCUSON)
			bufferevent_write(wp->event, "\033[O", 3);
		notify_pane("pane-focus-out", wp);
	}
	wp->flags &= ~PANE_FOCUSED;
	return;

focused:
	if (push || !(wp->flags & PANE_FOCUSED)) {
		if (wp->base.mode & MODE_FOCUSON)
			bufferevent_write(wp->event, "\033[I", 3);
		notify_pane("pane-focus-in", wp);
	}
	wp->flags |= PANE_FOCUSED;
}


tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx)
{
	struct window_pane	*wp = ctx->wp;
	u_int			 px, py, nx, ny;

	tty_default_attributes(tty, wp, ctx->bg);

	tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1);
	tty_margin_off(tty);

	px = 0;
	nx = screen_size_x(wp->screen);
	py = ctx->ocy + 1;
	ny = screen_size_y(wp->screen) - ctx->ocy - 1;

	tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg);

	px = ctx->ocx;
	nx = screen_size_x(wp->screen) - ctx->ocx;
	py = ctx->ocy;

	tty_clear_pane_line(tty, ctx, py, px, nx, ctx->bg);
}


tty_margin_off(struct tty *tty)
{
	tty_margin(tty, 0, tty->sx - 1);
}


format_replace(struct format_tree *ft, const char *key, size_t keylen,
    char **buf, size_t *len, size_t *off)
{
	struct window_pane	*wp = ft->wp;
	char			*copy, *copy0, *endptr, *ptr, *found, *new, sep;
	char			*value, *from = NULL, *to = NULL, *left, *right;
	size_t			 valuelen, newlen, fromlen, tolen, used;
	long			 limit = 0;
	int			 modifiers = 0, compare = 0, search = 0;
	int			 literal = 0;

	/* Make a copy of the key. */
	copy0 = copy = xmalloc(keylen + 1);
	memcpy(copy, key, keylen);
	copy[keylen] = '\0';

	/* Is there a length limit or whatnot? */
	switch (copy[0]) {
	case 'l':
		if (copy[1] != ':')
			break;
		literal = 1;
		copy += 2;
		break;
	case 'm':
		if (copy[1] != ':')
			break;
		compare = -2;
		copy += 2;
		break;
	case 'C':
		if (copy[1] != ':')
			break;
		search = 1;
		copy += 2;
		break;
	case '|':
		if (copy[1] != '|' || copy[2] != ':')
			break;
		compare = -3;
		copy += 3;
		break;
	case '&':
		if (copy[1] != '&' || copy[2] != ':')
			break;
		compare = -4;
		copy += 3;
		break;
	case '!':
		if (copy[1] == '=' && copy[2] == ':') {
			compare = -1;
			copy += 3;
			break;
		}
		break;
	case '=':
		if (copy[1] == '=' && copy[2] == ':') {
			compare = 1;
			copy += 3;
			break;
		}
		errno = 0;
		limit = strtol(copy + 1, &endptr, 10);
		if (errno == ERANGE && (limit == LONG_MIN || limit == LONG_MAX))
			break;
		if (*endptr != ':')
			break;
		copy = endptr + 1;
		break;
	case 'b':
		if (copy[1] != ':')
			break;
		modifiers |= FORMAT_BASENAME;
		copy += 2;
		break;
	case 'd':
		if (copy[1] != ':')
			break;
		modifiers |= FORMAT_DIRNAME;
		copy += 2;
		break;
	case 't':
		if (copy[1] != ':')
			break;
		modifiers |= FORMAT_TIMESTRING;
		copy += 2;
		break;
	case 'q':
		if (copy[1] != ':')
			break;
		modifiers |= FORMAT_QUOTE;
		copy += 2;
		break;
	case 's':
		sep = copy[1];
		if (sep == ':' || !ispunct((u_char)sep))
			break;
		from = copy + 2;
		for (copy = from; *copy != '\0' && *copy != sep; copy++)
			/* nothing */;
		if (copy[0] != sep || copy == from) {
			copy = copy0;
			break;
		}
		copy[0] = '\0';
		to = copy + 1;
		for (copy = to; *copy != '\0' && *copy != sep; copy++)
			/* nothing */;
		if (copy[0] != sep || copy[1] != ':') {
			copy = copy0;
			break;
		}
		copy[0] = '\0';

		modifiers |= FORMAT_SUBSTITUTE;
		copy += 2;
		break;
	}

	/* Is this a literal string? */
	if (literal) {
		value = xstrdup(copy);
		goto done;
	}

	/* Is this a comparison or a conditional? */
	if (search) {
		/* Search in pane. */
		if (wp == NULL)
			value = xstrdup("0");
		else
			xasprintf(&value, "%u", window_pane_search(wp, copy));
	} else if (compare != 0) {
		/* Comparison: compare comma-separated left and right. */
		if (format_choose(copy, &left, &right) != 0)
			goto fail;
		left = format_expand(ft, left);
		right = format_expand(ft, right);
		if (compare == -3 &&
		    (format_true(left) || format_true(right)))
			value = xstrdup("1");
		else if (compare == -4 &&
		    (format_true(left) && format_true(right)))
			value = xstrdup("1");
		else if (compare == 1 && strcmp(left, right) == 0)
			value = xstrdup("1");
		else if (compare == -1 && strcmp(left, right) != 0)
			value = xstrdup("1");
		else if (compare == -2 && fnmatch(left, right, 0) == 0)
			value = xstrdup("1");
		else
			value = xstrdup("0");
		free(right);
		free(left);
	} else if (*copy == '?') {
		/* Conditional: check first and choose second or third. */
		ptr = (char *)format_skip(copy, ',');
		if (ptr == NULL)
			goto fail;
		*ptr = '\0';

		found = format_find(ft, copy + 1, modifiers);
		if (found == NULL) {
			/*
			 * If the conditional not found, try to expand it. If
			 * the expansion doesn't have any effect, then assume
			 * false.
			 */
			found = format_expand(ft, copy + 1);
			if (strcmp(found, copy + 1) == 0) {
				free(found);
				found = xstrdup("");
			}
		}
		if (format_choose(ptr + 1, &left, &right) != 0) {
			free(found);
			goto fail;
		}

		if (format_true(found))
			value = format_expand(ft, left);
		else
			value = format_expand(ft, right);
		free(found);
	} else {
		/* Neither: look up directly. */
		value = format_find(ft, copy, modifiers);
		if (value == NULL)
			value = xstrdup("");
	}

	/* Perform substitution if any. */
	if (modifiers & FORMAT_SUBSTITUTE) {
		fromlen = strlen(from);
		tolen = strlen(to);

		newlen = strlen(value) + 1;
		copy = new = xmalloc(newlen);
		for (ptr = value; *ptr != '\0'; /* nothing */) {
			if (strncmp(ptr, from, fromlen) != 0) {
				*new++ = *ptr++;
				continue;
			}
			used = new - copy;

			newlen += tolen;
			copy = xrealloc(copy, newlen);

			new = copy + used;
			memcpy(new, to, tolen);

			new += tolen;
			ptr += fromlen;
		}
		*new = '\0';
		free(value);
		value = copy;
	}

	/* Truncate the value if needed. */
	if (limit > 0) {
		new = utf8_trimcstr(value, limit);
		free(value);
		value = new;
	} else if (limit < 0) {
		new = utf8_rtrimcstr(value, -limit);
		free(value);
		value = new;
	}

done:
	/* Expand the buffer and copy in the value. */
	valuelen = strlen(value);
	while (*len - *off < valuelen + 1) {
		*buf = xreallocarray(*buf, 2, *len);
		*len *= 2;
	}
	memcpy(*buf + *off, value, valuelen);
	*off += valuelen;

	free(value);
	free(copy0);
	return (0);

fail:
	free(copy0);
	return (-1);
}


winlink_next_index(struct winlinks *wwl, int idx)
{
	int	i;

	i = idx;
	do {
		if (winlink_find_by_index(wwl, i) == NULL)
			return (i);
		if (i == INT_MAX)
			i = 0;
		else
			i++;
	} while (i != idx);
	return (-1);
}


tty_colours_fg(struct tty *tty, const struct grid_cell *gc)
{
	struct grid_cell	*tc = &tty->cell;
	char			 s[32];

	/* Is this a 24-bit or 256-colour colour? */
	if (gc->fg & COLOUR_FLAG_RGB || gc->fg & COLOUR_FLAG_256) {
		if (tty_try_colour(tty, gc->fg, "38") == 0)
			goto save_fg;
		/* Should not get here, already converted in tty_check_fg. */
		return;
	}

	/* Is this an aixterm bright colour? */
	if (gc->fg >= 90 && gc->fg <= 97) {
		xsnprintf(s, sizeof s, "\033[%dm", gc->fg);
		tty_puts(tty, s);
		goto save_fg;
	}

	/* Otherwise set the foreground colour. */
	tty_putcode1(tty, TTYC_SETAF, gc->fg);

save_fg:
	/* Save the new values in the terminal current cell. */
	tc->fg = gc->fg;
}


input_exit_apc(struct input_ctx *ictx)
{
	if (ictx->flags & INPUT_DISCARD)
		return;
	log_debug("%s: \"%s\"", __func__, ictx->input_buf);

	if (!utf8_isvalid(ictx->input_buf))
		return;
	screen_set_title(ictx->ctx.s, ictx->input_buf);
	server_status_window(ictx->wp->window);
}


input_exit_rename(struct input_ctx *ictx)
{
	if (ictx->flags & INPUT_DISCARD)
		return;
	if (!options_get_number(ictx->wp->window->options, "allow-rename"))
		return;
	log_debug("%s: \"%s\"", __func__, ictx->input_buf);

	if (!utf8_isvalid(ictx->input_buf))
		return;
	window_set_name(ictx->wp->window, ictx->input_buf);
	options_set_number(ictx->wp->window->options, "automatic-rename", 0);
	server_status_window(ictx->wp->window);
}


server_client_set_identify(struct client *c, u_int delay)
{
	struct timeval	tv;

	tv.tv_sec = delay / 1000;
	tv.tv_usec = (delay % 1000) * 1000L;

	if (event_initialized(&c->identify_timer))
		evtimer_del(&c->identify_timer);
	evtimer_set(&c->identify_timer, server_client_callback_identify, c);
	if (delay != 0)
		evtimer_add(&c->identify_timer, &tv);

	c->flags |= CLIENT_IDENTIFY;
	c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR);
	server_redraw_client(c);
}


window_pane_find_by_id_str(const char *s)
{
	const char	*errstr;
	u_int		 id;

	if (*s != '%')
		return (NULL);

	id = strtonum(s + 1, 0, UINT_MAX, &errstr);
	if (errstr != NULL)
		return (NULL);
	return (window_pane_find_by_id(id));
}


window_pane_destroy(struct window_pane *wp)
{
	window_pane_reset_mode(wp);
	free(wp->searchstr);

	if (wp->fd != -1) {
		bufferevent_free(wp->event);
		close(wp->fd);
	}

	input_free(wp);

	screen_free(&wp->base);
	if (wp->saved_grid != NULL)
		grid_destroy(wp->saved_grid);

	if (wp->pipe_fd != -1) {
		bufferevent_free(wp->pipe_event);
		close(wp->pipe_fd);
	}

	if (event_initialized(&wp->resize_timer))
		event_del(&wp->resize_timer);

	RB_REMOVE(window_pane_tree, &all_window_panes, wp);

	free((void *)wp->cwd);
	free(wp->shell);
	cmd_free_argv(wp->argc, wp->argv);
	free(wp->palette);
	free(wp);
}


tty_fake_bce(const struct tty *tty, const struct window_pane *wp, u_int bg)
{
	struct grid_cell	gc;

	if (tty_term_flag(tty->term, TTYC_BCE))
		return (0);

	memcpy(&gc, &grid_default_cell, sizeof gc);
	if (wp != NULL)
		tty_default_colours(&gc, wp);

	if (!COLOUR_DEFAULT(bg) || !COLOUR_DEFAULT(gc.bg))
		return (1);
	return (0);
}


input_c0_dispatch(struct input_ctx *ictx)
{
	struct screen_write_ctx	*sctx = &ictx->ctx;
	struct window_pane	*wp = ictx->wp;
	struct screen		*s = sctx->s;

	ictx->utf8started = 0; /* can't be valid UTF-8 */

	log_debug("%s: '%c'", __func__, ictx->ch);

	switch (ictx->ch) {
	case '\000':	/* NUL */
		break;
	case '\007':	/* BEL */
		alerts_queue(wp->window, WINDOW_BELL);
		break;
	case '\010':	/* BS */
		screen_write_backspace(sctx);
		break;
	case '\011':	/* HT */
		/* Don't tab beyond the end of the line. */
		if (s->cx >= screen_size_x(s) - 1)
			break;

		/* Find the next tab point, or use the last column if none. */
		do {
			s->cx++;
			if (bit_test(s->tabs, s->cx))
				break;
		} while (s->cx < screen_size_x(s) - 1);
		break;
	case '\012':	/* LF */
	case '\013':	/* VT */
	case '\014':	/* FF */
		screen_write_linefeed(sctx, 0, ictx->cell.cell.bg);
		break;
	case '\015':	/* CR */
		screen_write_carriagereturn(sctx);
		break;
	case '\016':	/* SO */
		ictx->cell.set = 1;
		break;
	case '\017':	/* SI */
		ictx->cell.set = 0;
		break;
	default:
		log_debug("%s: unknown '%c'", __func__, ictx->ch);
		break;
	}

	ictx->last = -1;
	return (0);
}


tty_putcode_ptr2(struct tty *tty, enum tty_code_code code, const void *a,
    const void *b)
{
	if (a != NULL && b != NULL)
		tty_puts(tty, tty_term_ptr2(tty->term, code, a, b));
}


tty_putcode_ptr1(struct tty *tty, enum tty_code_code code, const void *a)
{
	if (a != NULL)
		tty_puts(tty, tty_term_ptr1(tty->term, code, a));
}


server_client_callback_identify(__unused int fd, __unused short events,
    void *data)
{
	server_client_clear_identify(data, NULL);
}


winlink_add(struct winlinks *wwl, int idx)
{
	struct winlink	*wl;

	if (idx < 0) {
		if ((idx = winlink_next_index(wwl, -idx - 1)) == -1)
			return (NULL);
	} else if (winlink_find_by_index(wwl, idx) != NULL)
		return (NULL);

	wl = xcalloc(1, sizeof *wl);
	wl->idx = idx;
	RB_INSERT(winlinks, wwl, wl);

	return (wl);
}


tty_clamp_line(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py,
    u_int nx, u_int *i, u_int *x, u_int *rx, u_int *ry)
{
	struct window_pane	*wp = ctx->wp;
	u_int			 xoff = wp->xoff + px;

	if (!tty_is_visible(tty, ctx, px, py, nx, 1))
		return (0);
	*ry = ctx->yoff + py - ctx->oy;

	if (xoff >= ctx->ox && xoff + nx <= ctx->ox + ctx->sx) {
		/* All visible. */
		*i = 0;
		*x = ctx->xoff + px - ctx->ox;
		*rx = nx;
	} else if (xoff < ctx->ox && xoff + nx > ctx->ox + ctx->sx) {
		/* Both left and right not visible. */
		*i = ctx->ox;
		*x = 0;
		*rx = ctx->sx;
	} else if (xoff < ctx->ox) {
		/* Left not visible. */
		*i = ctx->ox - (ctx->xoff + px);
		*x = 0;
		*rx = nx - *i;
	} else {
		/* Right not visible. */
		*i = 0;
		*x = (ctx->xoff + px) - ctx->ox;
		*rx = ctx->sx - *x;
	}
	if (*rx > nx)
		fatalx("%s: x too big, %u > %u", __func__, *rx, nx);

	return (1);
}


aiff_read_header (SF_PRIVATE *psf, COMM_CHUNK *comm_fmt)
{	SSND_CHUNK	ssnd_fmt ;
	AIFF_PRIVATE *paiff ;
	BUF_UNION	ubuf ;
	uint32_t	chunk_size = 0, FORMsize, SSNDsize, bytesread, mark_count = 0 ;
	int			k, found_chunk = 0, done = 0, error = 0 ;
	char		*cptr ;
	int			instr_found = 0, mark_found = 0 ;

	if (psf->filelength > SF_PLATFORM_S64 (0xffffffff))
		psf_log_printf (psf, "Warning : filelength > 0xffffffff. This is bad!!!!\n") ;

	if ((paiff = psf->container_data) == NULL)
		return SFE_INTERNAL ;

	paiff->comm_offset = 0 ;
	paiff->ssnd_offset = 0 ;

	/* Set position to start of file to begin reading header. */
	psf_binheader_readf (psf, "p", 0) ;

	memset (comm_fmt, 0, sizeof (COMM_CHUNK)) ;

	/* Until recently AIF* file were all BIG endian. */
	psf->endian = SF_ENDIAN_BIG ;

	/*	AIFF files can apparently have their chunks in any order. However, they
	**	must have a FORM chunk. Approach here is to read all the chunks one by
	**	one and then check for the mandatory chunks at the end.
	*/
	while (! done)
	{	unsigned	marker ;
		size_t jump = chunk_size & 1 ;

		marker = chunk_size = 0 ;
		psf_binheader_readf (psf, "Ejm4", jump, &marker, &chunk_size) ;
		if (marker == 0)
		{	sf_count_t pos = psf_ftell (psf) ;
			psf_log_printf (psf, "Have 0 marker at position %D (0x%x).\n", pos, pos) ;
			break ;
			} ;

		if (psf->file.mode == SFM_RDWR && (found_chunk & HAVE_SSND))
			return SFE_AIFF_RW_SSND_NOT_LAST ;

		psf_store_read_chunk_u32 (&psf->rchunks, marker, psf_ftell (psf), chunk_size) ;

		switch (marker)
		{	case FORM_MARKER :
					if (found_chunk)
						return SFE_AIFF_NO_FORM ;

					FORMsize = chunk_size ;

					found_chunk |= HAVE_FORM ;
					psf_binheader_readf (psf, "m", &marker) ;
					switch (marker)
					{	case AIFC_MARKER :
						case AIFF_MARKER :
							found_chunk |= (marker == AIFC_MARKER) ? (HAVE_AIFC | HAVE_AIFF) : HAVE_AIFF ;
							break ;
						default :
							break ;
						} ;

					if (psf->fileoffset > 0 && psf->filelength > FORMsize + 8)
					{	/* Set file length. */
						psf->filelength = FORMsize + 8 ;
						psf_log_printf (psf, "FORM : %u\n %M\n", FORMsize, marker) ;
						}
					else if (FORMsize != psf->filelength - 2 * SIGNED_SIZEOF (chunk_size))
					{	chunk_size = psf->filelength - 2 * sizeof (chunk_size) ;
						psf_log_printf (psf, "FORM : %u (should be %u)\n %M\n", FORMsize, chunk_size, marker) ;
						FORMsize = chunk_size ;
						}
					else
						psf_log_printf (psf, "FORM : %u\n %M\n", FORMsize, marker) ;
					/* Set this to 0, so we don't jump a byte when parsing the next marker. */
					chunk_size = 0 ;
					break ;

			case COMM_MARKER :
					paiff->comm_offset = psf_ftell (psf) - 8 ;
					chunk_size += chunk_size & 1 ;
					comm_fmt->size = chunk_size ;
					if ((error = aiff_read_comm_chunk (psf, comm_fmt)) != 0)
						return error ;

					found_chunk |= HAVE_COMM ;
					break ;

			case PEAK_MARKER :
					/* Must have COMM chunk before PEAK chunk. */
					if ((found_chunk & (HAVE_FORM | HAVE_AIFF | HAVE_COMM)) != (HAVE_FORM | HAVE_AIFF | HAVE_COMM))
						return SFE_AIFF_PEAK_B4_COMM ;

					psf_log_printf (psf, "%M : %d\n", marker, chunk_size) ;
					if (chunk_size != AIFF_PEAK_CHUNK_SIZE (psf->sf.channels))
					{	psf_binheader_readf (psf, "j", chunk_size) ;
						psf_log_printf (psf, "*** File PEAK chunk too big.\n") ;
						return SFE_WAV_BAD_PEAK ;
						} ;

					if ((psf->peak_info = peak_info_calloc (psf->sf.channels)) == NULL)
						return SFE_MALLOC_FAILED ;

					/* read in rest of PEAK chunk. */
					psf_binheader_readf (psf, "E44", &(psf->peak_info->version), &(psf->peak_info->timestamp)) ;

					if (psf->peak_info->version != 1)
						psf_log_printf (psf, "  version    : %d *** (should be version 1)\n", psf->peak_info->version) ;
					else
						psf_log_printf (psf, "  version    : %d\n", psf->peak_info->version) ;

					psf_log_printf (psf, "  time stamp : %d\n", psf->peak_info->timestamp) ;
					psf_log_printf (psf, "    Ch   Position       Value\n") ;

					cptr = ubuf.cbuf ;
					for (k = 0 ; k < psf->sf.channels ; k++)
					{	float value ;
						uint32_t position ;

						psf_binheader_readf (psf, "Ef4", &value, &position) ;
						psf->peak_info->peaks [k].value = value ;
						psf->peak_info->peaks [k].position = position ;

						snprintf (cptr, sizeof (ubuf.scbuf), "    %2d   %-12" PRId64 "   %g\n",
								k, psf->peak_info->peaks [k].position, psf->peak_info->peaks [k].value) ;
						cptr [sizeof (ubuf.scbuf) - 1] = 0 ;
						psf_log_printf (psf, "%s", cptr) ;
						} ;

					psf->peak_info->peak_loc = ((found_chunk & HAVE_SSND) == 0) ? SF_PEAK_START : SF_PEAK_END ;
					break ;

			case SSND_MARKER :
					if ((found_chunk & HAVE_AIFC) && (found_chunk & HAVE_FVER) == 0)
						psf_log_printf (psf, "*** Valid AIFC files should have an FVER chunk.\n") ;

					paiff->ssnd_offset = psf_ftell (psf) - 8 ;
					SSNDsize = chunk_size ;
					psf_binheader_readf (psf, "E44", &(ssnd_fmt.offset), &(ssnd_fmt.blocksize)) ;

					psf->datalength = SSNDsize - sizeof (ssnd_fmt) ;
					psf->dataoffset = psf_ftell (psf) ;

					if (psf->datalength > psf->filelength - psf->dataoffset || psf->datalength < 0)
					{	psf_log_printf (psf, " SSND : %u (should be %D)\n", SSNDsize, psf->filelength - psf->dataoffset + sizeof (SSND_CHUNK)) ;
						psf->datalength = psf->filelength - psf->dataoffset ;
						}
					else
						psf_log_printf (psf, " SSND : %u\n", SSNDsize) ;

					if (ssnd_fmt.offset == 0 || psf->dataoffset + ssnd_fmt.offset == ssnd_fmt.blocksize)
					{	psf_log_printf (psf, "  Offset     : %u\n", ssnd_fmt.offset) ;
						psf_log_printf (psf, "  Block Size : %u\n", ssnd_fmt.blocksize) ;

						psf->dataoffset += ssnd_fmt.offset ;
						psf->datalength -= ssnd_fmt.offset ;
						}
					else
					{	psf_log_printf (psf, "  Offset     : %u\n", ssnd_fmt.offset) ;
						psf_log_printf (psf, "  Block Size : %u ???\n", ssnd_fmt.blocksize) ;
						psf->dataoffset += ssnd_fmt.offset ;
						psf->datalength -= ssnd_fmt.offset ;
						} ;

					/* Only set dataend if there really is data at the end. */
					if (psf->datalength + psf->dataoffset < psf->filelength)
						psf->dataend = psf->datalength + psf->dataoffset ;

					found_chunk |= HAVE_SSND ;

					if (! psf->sf.seekable)
						break ;

					/* Seek to end of SSND chunk. */
					psf_fseek (psf, psf->dataoffset + psf->datalength, SEEK_SET) ;
					break ;

			case c_MARKER :
					if (chunk_size == 0)
						break ;
					if (chunk_size >= SIGNED_SIZEOF (ubuf.scbuf))
					{	psf_log_printf (psf, " %M : %d (too big)\n", marker, chunk_size) ;
						return SFE_INTERNAL ;
						} ;

					cptr = ubuf.cbuf ;
					psf_binheader_readf (psf, "b", cptr, chunk_size + (chunk_size & 1)) ;
					cptr [chunk_size] = 0 ;

					psf_sanitize_string (cptr, chunk_size) ;

					psf_log_printf (psf, " %M : %s\n", marker, cptr) ;
					psf_store_string (psf, SF_STR_COPYRIGHT, cptr) ;
					chunk_size += chunk_size & 1 ;
					break ;

			case AUTH_MARKER :
					if (chunk_size == 0)
						break ;
					if (chunk_size >= SIGNED_SIZEOF (ubuf.scbuf) - 1)
					{	psf_log_printf (psf, " %M : %d (too big)\n", marker, chunk_size) ;
						return SFE_INTERNAL ;
						} ;

					cptr = ubuf.cbuf ;
					psf_binheader_readf (psf, "b", cptr, chunk_size + (chunk_size & 1)) ;
					cptr [chunk_size] = 0 ;
					psf_log_printf (psf, " %M : %s\n", marker, cptr) ;
					psf_store_string (psf, SF_STR_ARTIST, cptr) ;
					chunk_size += chunk_size & 1 ;
					break ;

			case COMT_MARKER :
				{	uint16_t count, id, len ;
					uint32_t timestamp, bytes ;

					if (chunk_size == 0)
						break ;
					bytes = chunk_size ;
					bytes -= psf_binheader_readf (psf, "E2", &count) ;
					psf_log_printf (psf, " %M : %d\n  count  : %d\n", marker, chunk_size, count) ;

					for (k = 0 ; k < count ; k++)
					{	bytes -= psf_binheader_readf (psf, "E422", &timestamp, &id, &len) ;
						psf_log_printf (psf, "   time   : 0x%x\n   marker : %x\n   length : %d\n", timestamp, id, len) ;

						if (len + 1 > SIGNED_SIZEOF (ubuf.scbuf))
						{	psf_log_printf (psf, "\nError : string length (%d) too big.\n", len) ;
							return SFE_INTERNAL ;
							} ;

						cptr = ubuf.cbuf ;
						bytes -= psf_binheader_readf (psf, "b", cptr, len) ;
						cptr [len] = 0 ;
						psf_log_printf (psf, "   string : %s\n", cptr) ;
						} ;

					if (bytes > 0)
						psf_binheader_readf (psf, "j", bytes) ;
					} ;
					break ;

			case APPL_MARKER :
				{	unsigned appl_marker ;

					if (chunk_size == 0)
						break ;
					if (chunk_size >= SIGNED_SIZEOF (ubuf.scbuf) - 1)
					{	psf_log_printf (psf, " %M : %u (too big, skipping)\n", marker, chunk_size) ;
						psf_binheader_readf (psf, "j", chunk_size + (chunk_size & 1)) ;
						break ;
						} ;

					if (chunk_size < 4)
					{	psf_log_printf (psf, " %M : %d (too small, skipping)\n", marker, chunk_size) ;
						psf_binheader_readf (psf, "j", chunk_size + (chunk_size & 1)) ;
						break ;
						} ;

					cptr = ubuf.cbuf ;
					psf_binheader_readf (psf, "mb", &appl_marker, cptr, chunk_size + (chunk_size & 1) - 4) ;
					cptr [chunk_size] = 0 ;

					for (k = 0 ; k < (int) chunk_size ; k++)
						if (! psf_isprint (cptr [k]))
						{	cptr [k] = 0 ;
							break ;
							} ;

					psf_log_printf (psf, " %M : %d\n  AppSig : %M\n  Name   : %s\n", marker, chunk_size, appl_marker, cptr) ;
					psf_store_string (psf, SF_STR_SOFTWARE, cptr) ;
					chunk_size += chunk_size & 1 ;
					} ;
					break ;

			case NAME_MARKER :
					if (chunk_size == 0)
						break ;
					if (chunk_size >= SIGNED_SIZEOF (ubuf.scbuf) - 2)
					{	psf_log_printf (psf, " %M : %d (too big)\n", marker, chunk_size) ;
						return SFE_INTERNAL ;
						} ;

					cptr = ubuf.cbuf ;
					psf_binheader_readf (psf, "b", cptr, chunk_size + (chunk_size & 1)) ;
					cptr [chunk_size] = 0 ;
					psf_log_printf (psf, " %M : %s\n", marker, cptr) ;
					psf_store_string (psf, SF_STR_TITLE, cptr) ;
					chunk_size += chunk_size & 1 ;
					break ;

			case ANNO_MARKER :
					if (chunk_size == 0)
						break ;
					if (chunk_size >= SIGNED_SIZEOF (ubuf.scbuf) - 2)
					{	psf_log_printf (psf, " %M : %d (too big)\n", marker, chunk_size) ;
						return SFE_INTERNAL ;
						} ;

					cptr = ubuf.cbuf ;
					psf_binheader_readf (psf, "b", cptr, chunk_size + (chunk_size & 1)) ;
					cptr [chunk_size] = 0 ;
					psf_log_printf (psf, " %M : %s\n", marker, cptr) ;
					psf_store_string (psf, SF_STR_COMMENT, cptr) ;
					chunk_size += chunk_size & 1 ;
					break ;

			case INST_MARKER :
					if (chunk_size != SIZEOF_INST_CHUNK)
					{	psf_log_printf (psf, " %M : %d (should be %d)\n", marker, chunk_size, SIZEOF_INST_CHUNK) ;
						psf_binheader_readf (psf, "j", chunk_size) ;
						break ;
						} ;
					psf_log_printf (psf, " %M : %d\n", marker, chunk_size) ;
					{	uint8_t bytes [6] ;
						int16_t gain ;

						if (psf->instrument == NULL && (psf->instrument = psf_instrument_alloc ()) == NULL)
							return SFE_MALLOC_FAILED ;

						psf_binheader_readf (psf, "b", bytes, 6) ;
						psf_log_printf (psf, "  Base Note : %u\n  Detune    : %u\n"
											"  Low  Note : %u\n  High Note : %u\n"
											"  Low  Vel. : %u\n  High Vel. : %u\n",
											bytes [0], bytes [1], bytes [2], bytes [3], bytes [4], bytes [5]) ;
						psf->instrument->basenote = bytes [0] ;
						psf->instrument->detune = bytes [1] ;
						psf->instrument->key_lo = bytes [2] ;
						psf->instrument->key_hi = bytes [3] ;
						psf->instrument->velocity_lo = bytes [4] ;
						psf->instrument->velocity_hi = bytes [5] ;
						psf_binheader_readf (psf, "E2", &gain) ;
						psf->instrument->gain = gain ;
						psf_log_printf (psf, "  Gain (dB) : %d\n", gain) ;
						} ;
					{	int16_t	mode ; /* 0 - no loop, 1 - forward looping, 2 - backward looping */
						const char	*loop_mode ;
						uint16_t begin, end ;

						psf_binheader_readf (psf, "E222", &mode, &begin, &end) ;
						loop_mode = get_loop_mode_str (mode) ;
						mode = get_loop_mode (mode) ;
						if (mode == SF_LOOP_NONE)
						{	psf->instrument->loop_count = 0 ;
							psf->instrument->loops [0].mode = SF_LOOP_NONE ;
							}
						else
						{	psf->instrument->loop_count = 1 ;
							psf->instrument->loops [0].mode = SF_LOOP_FORWARD ;
							psf->instrument->loops [0].start = begin ;
							psf->instrument->loops [0].end = end ;
							psf->instrument->loops [0].count = 0 ;
							} ;
						psf_log_printf (psf, "  Sustain\n   mode  : %d => %s\n   begin : %u\n   end   : %u\n",
											mode, loop_mode, begin, end) ;
						psf_binheader_readf (psf, "E222", &mode, &begin, &end) ;
						loop_mode = get_loop_mode_str (mode) ;
						mode = get_loop_mode (mode) ;
						if (mode == SF_LOOP_NONE)
							psf->instrument->loops [1].mode = SF_LOOP_NONE ;
						else
						{	psf->instrument->loop_count += 1 ;
							psf->instrument->loops [1].mode = SF_LOOP_FORWARD ;
							psf->instrument->loops [1].start = begin ;
							psf->instrument->loops [1].end = end ;
							psf->instrument->loops [1].count = 0 ;
							} ;
						psf_log_printf (psf, "  Release\n   mode  : %d => %s\n   begin : %u\n   end   : %u\n",
										mode, loop_mode, begin, end) ;
						} ;
					instr_found++ ;
					break ;

			case basc_MARKER :
					psf_log_printf (psf, " basc : %u\n", chunk_size) ;

					if ((error = aiff_read_basc_chunk (psf, chunk_size)))
						return error ;
					break ;

			case MARK_MARKER :
					psf_log_printf (psf, " %M : %d\n", marker, chunk_size) ;
					{	uint16_t mark_id, n = 0 ;
						uint32_t position ;

						bytesread = psf_binheader_readf (psf, "E2", &n) ;
						mark_count = n ;
						psf_log_printf (psf, "  Count : %u\n", mark_count) ;
						if (paiff->markstr != NULL)
						{	psf_log_printf (psf, "*** Second MARK chunk found. Throwing away the first.\n") ;
							free (paiff->markstr) ;
							} ;
						paiff->markstr = calloc (mark_count, sizeof (MARK_ID_POS)) ;
						if (paiff->markstr == NULL)
							return SFE_MALLOC_FAILED ;

						if (mark_count > 1000)
						{	psf_log_printf (psf, "  More than 1000 markers, skipping!\n") ;
							psf_binheader_readf (psf, "j", chunk_size - bytesread) ;
							break ;
						} ;

						if ((psf->cues = psf_cues_alloc (mark_count)) == NULL)
							return SFE_MALLOC_FAILED ;

						for (n = 0 ; n < mark_count && bytesread < chunk_size ; n++)
						{	uint32_t pstr_len ;
							uint8_t ch ;

							bytesread += psf_binheader_readf (psf, "E241", &mark_id, &position, &ch) ;
							psf_log_printf (psf, "   Mark ID  : %u\n   Position : %u\n", mark_id, position) ;

							psf->cues->cue_points [n].indx = mark_id ;
							psf->cues->cue_points [n].position = 0 ;
							psf->cues->cue_points [n].fcc_chunk = MAKE_MARKER ('d', 'a', 't', 'a') ; /* always data */
							psf->cues->cue_points [n].chunk_start = 0 ;
							psf->cues->cue_points [n].block_start = 0 ;
							psf->cues->cue_points [n].sample_offset = position ;

							pstr_len = (ch & 1) ? ch : ch + 1 ;

							if (pstr_len < sizeof (ubuf.scbuf) - 1)
							{	bytesread += psf_binheader_readf (psf, "b", ubuf.scbuf, pstr_len) ;
								ubuf.scbuf [pstr_len] = 0 ;
								}
							else
							{	uint32_t read_len = pstr_len - (sizeof (ubuf.scbuf) - 1) ;
								bytesread += psf_binheader_readf (psf, "bj", ubuf.scbuf, read_len, pstr_len - read_len) ;
								ubuf.scbuf [sizeof (ubuf.scbuf) - 1] = 0 ;
								}

							psf_log_printf (psf, "   Name     : %s\n", ubuf.scbuf) ;

							psf_strlcpy (psf->cues->cue_points [n].name, sizeof (psf->cues->cue_points [n].name), ubuf.cbuf) ;

							paiff->markstr [n].markerID = mark_id ;
							paiff->markstr [n].position = position ;
							/*
							**	TODO if ubuf.scbuf is equal to
							**	either Beg_loop, Beg loop or beg loop and spam
							**	if (psf->instrument == NULL && (psf->instrument = psf_instrument_alloc ()) == NULL)
							**		return SFE_MALLOC_FAILED ;
							*/
							} ;
						} ;
					mark_found++ ;
					psf_binheader_readf (psf, "j", chunk_size - bytesread) ;
					break ;

			case FVER_MARKER :
					found_chunk |= HAVE_FVER ;
					/* Falls through. */

			case SFX_MARKER :
					psf_log_printf (psf, " %M : %d\n", marker, chunk_size) ;
					psf_binheader_readf (psf, "j", chunk_size) ;
					break ;

			case NONE_MARKER :
					/* Fix for broken AIFC files with incorrect COMM chunk length. */
					chunk_size = (chunk_size >> 24) - 3 ;
					psf_log_printf (psf, " %M : %d\n", marker, chunk_size) ;
					psf_binheader_readf (psf, "j", make_size_t (chunk_size)) ;
					break ;

			case CHAN_MARKER :
					if (chunk_size < 12)
					{	psf_log_printf (psf, " %M : %d (should be >= 12)\n", marker, chunk_size) ;
						psf_binheader_readf (psf, "j", chunk_size) ;
						break ;
						}

					psf_log_printf (psf, " %M : %d\n", marker, chunk_size) ;

					if ((error = aiff_read_chanmap (psf, chunk_size)))
						return error ;
					break ;

			default :
					if (chunk_size >= 0xffff0000)
					{	done = SF_TRUE ;
						psf_log_printf (psf, "*** Unknown chunk marker (%X) at position %D with length %u. Exiting parser.\n", marker, psf_ftell (psf) - 8, chunk_size) ;
						break ;
						} ;

					if (psf_isprint ((marker >> 24) & 0xFF) && psf_isprint ((marker >> 16) & 0xFF)
						&& psf_isprint ((marker >> 8) & 0xFF) && psf_isprint (marker & 0xFF))
					{	psf_log_printf (psf, " %M : %u (unknown marker)\n", marker, chunk_size) ;

						psf_binheader_readf (psf, "j", chunk_size) ;
						break ;
						} ;

					if (psf_ftell (psf) & 0x03)
					{	psf_log_printf (psf, "  Unknown chunk marker at position %D. Resynching.\n", psf_ftell (psf) - 8) ;
						psf_binheader_readf (psf, "j", -3) ;
						break ;
						} ;
					psf_log_printf (psf, "*** Unknown chunk marker %X at position %D. Exiting parser.\n", marker, psf_ftell (psf)) ;
					done = SF_TRUE ;
					break ;
			} ;	/* switch (marker) */

		if (chunk_size >= psf->filelength)
		{	psf_log_printf (psf, "*** Chunk size %u > file length %D. Exiting parser.\n", chunk_size, psf->filelength) ;
			break ;
			} ;

		if ((! psf->sf.seekable) && (found_chunk & HAVE_SSND))
			break ;

		if (psf_ftell (psf) >= psf->filelength - (2 * SIGNED_SIZEOF (int32_t)))
			break ;
		} ; /* while (1) */

	if (instr_found && mark_found)
	{	int ji, str_index ;
		/* Next loop will convert markers to loop positions for internal handling */
		for (ji = 0 ; ji < psf->instrument->loop_count ; ji ++)
		{	if (ji < ARRAY_LEN (psf->instrument->loops))
			{	psf->instrument->loops [ji].start = marker_to_position (paiff->markstr, psf->instrument->loops [ji].start, mark_count) ;
				psf->instrument->loops [ji].end = marker_to_position (paiff->markstr, psf->instrument->loops [ji].end, mark_count) ;
				psf->instrument->loops [ji].mode = SF_LOOP_FORWARD ;
				} ;
			} ;

		/* The markers that correspond to loop positions can now be removed from cues struct */
		if (psf->cues->cue_count > (uint32_t) (psf->instrument->loop_count * 2))
		{	uint32_t j ;

			for (j = 0 ; j < psf->cues->cue_count - (uint32_t) (psf->instrument->loop_count * 2) ; j ++)
			{	/* This simply copies the information in cues above loop positions and writes it at current count instead */
				psf->cues->cue_points [j].indx = psf->cues->cue_points [j + psf->instrument->loop_count * 2].indx ;
				psf->cues->cue_points [j].position = psf->cues->cue_points [j + psf->instrument->loop_count * 2].position ;
				psf->cues->cue_points [j].fcc_chunk = psf->cues->cue_points [j + psf->instrument->loop_count * 2].fcc_chunk ;
				psf->cues->cue_points [j].chunk_start = psf->cues->cue_points [j + psf->instrument->loop_count * 2].chunk_start ;
				psf->cues->cue_points [j].block_start = psf->cues->cue_points [j + psf->instrument->loop_count * 2].block_start ;
				psf->cues->cue_points [j].sample_offset = psf->cues->cue_points [j + psf->instrument->loop_count * 2].sample_offset ;
				for (str_index = 0 ; str_index < 256 ; str_index++)
					psf->cues->cue_points [j].name [str_index] = psf->cues->cue_points [j + psf->instrument->loop_count * 2].name [str_index] ;
				} ;
			psf->cues->cue_count -= psf->instrument->loop_count * 2 ;
			} else
			{	/* All the cues were in fact loop positions so we can actually remove the cues altogether */
				free (psf->cues) ;
				psf->cues = NULL ;
				}
		} ;

	if (psf->sf.channels < 1)
		return SFE_CHANNEL_COUNT_ZERO ;

	if (psf->sf.channels > SF_MAX_CHANNELS)
		return SFE_CHANNEL_COUNT ;

	if (! (found_chunk & HAVE_FORM))
		return SFE_AIFF_NO_FORM ;

	if (! (found_chunk & HAVE_AIFF))
		return SFE_AIFF_COMM_NO_FORM ;

	if (! (found_chunk & HAVE_COMM))
		return SFE_AIFF_SSND_NO_COMM ;

	if (! psf->dataoffset)
		return SFE_AIFF_NO_DATA ;

	return 0 ;
} /* aiff_read_header */


rf64_write_tailer (SF_PRIVATE *psf)
{
	/* Reset the current header buffer length to zero. */
	psf->header.ptr [0] = 0 ;
	psf->header.indx = 0 ;

	if (psf->bytewidth > 0 && psf->sf.seekable == SF_TRUE)
	{	psf->datalength = psf->sf.frames * psf->bytewidth * psf->sf.channels ;
		psf->dataend = psf->dataoffset + psf->datalength ;
		} ;

	if (psf->dataend > 0)
		psf_fseek (psf, psf->dataend, SEEK_SET) ;
	else
		psf->dataend = psf_fseek (psf, 0, SEEK_END) ;

	if (psf->dataend & 1)
		psf_binheader_writef (psf, "z", BHWz (1)) ;

	if (psf->strings.flags & SF_STR_LOCATE_END)
		wavlike_write_strings (psf, SF_STR_LOCATE_END) ;

	/* Write the tailer. */
	if (psf->header.indx > 0)
		psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;

	return 0 ;
} /* rf64_write_tailer */


rf64_read_header (SF_PRIVATE *psf, int *blockalign, int *framesperblock)
{	WAVLIKE_PRIVATE	*wpriv ;
	WAV_FMT		*wav_fmt ;
	sf_count_t riff_size = 0, frame_count = 0, ds64_datalength = 0 ;
	uint32_t marks [2], marker, chunk_size, parsestage = 0 ;
	int error, done = 0, format = 0 ;

	if ((wpriv = psf->container_data) == NULL)
		return SFE_INTERNAL ;
	wav_fmt = &wpriv->wav_fmt ;

	/* Set position to start of file to begin reading header. */
	psf_binheader_readf (psf, "pmmm", 0, &marker, marks, marks + 1) ;
	if (marker != RF64_MARKER || marks [1] != WAVE_MARKER)
		return SFE_RF64_NOT_RF64 ;

	if (marks [0] == FFFF_MARKER)
		psf_log_printf (psf, "%M\n  %M\n", RF64_MARKER, WAVE_MARKER) ;
	else
		psf_log_printf (psf, "%M : 0x%x (should be 0xFFFFFFFF)\n  %M\n", RF64_MARKER, WAVE_MARKER) ;

	while (NOT (done))
	{
		marker = chunk_size = 0 ;
		psf_binheader_readf (psf, "em4", &marker, &chunk_size) ;

		if (marker == 0)
		{	sf_count_t pos = psf_ftell (psf) ;
			psf_log_printf (psf, "Have 0 marker at position %D (0x%x).\n", pos, pos) ;
			break ;
			} ;

		psf_store_read_chunk_u32 (&psf->rchunks, marker, psf_ftell (psf), chunk_size) ;

		switch (marker)
		{	case ds64_MARKER :
				if (parsestage & HAVE_ds64)
				{	psf_log_printf (psf, "*** Second 'ds64' chunk?\n") ;
					break ;
					} ;

				{	unsigned int table_len, bytesread ;

					/* Read ds64 sizes (3 8-byte words). */
					bytesread = psf_binheader_readf (psf, "888", &riff_size, &ds64_datalength, &frame_count) ;

					/* Read table length. */
					bytesread += psf_binheader_readf (psf, "4", &table_len) ;
					/* Skip table for now. (this was "table_len + 4", why?) */
					bytesread += psf_binheader_readf (psf, "j", table_len) ;

					if (chunk_size == bytesread)
						psf_log_printf (psf, "%M : %u\n", marker, chunk_size) ;
					else if (chunk_size >= bytesread + 4)
					{	unsigned int next ;
						psf_binheader_readf (psf, "m", &next) ;
						if (next == fmt_MARKER)
						{	psf_log_printf (psf, "%M : %u (should be %u)\n", marker, chunk_size, bytesread) ;
							psf_binheader_readf (psf, "j", -4) ;
							}
						else
						{	psf_log_printf (psf, "%M : %u\n", marker, chunk_size) ;
							psf_binheader_readf (psf, "j", chunk_size - bytesread - 4) ;
							} ;
						} ;

					if (psf->filelength != riff_size + 8)
						psf_log_printf (psf, "  Riff size : %D (should be %D)\n", riff_size, psf->filelength - 8) ;
					else
						psf_log_printf (psf, "  Riff size : %D\n", riff_size) ;

					psf_log_printf (psf, "  Data size : %D\n", ds64_datalength) ;

					psf_log_printf (psf, "  Frames    : %D\n", frame_count) ;
					psf_log_printf (psf, "  Table length : %u\n", table_len) ;

					} ;
				parsestage |= HAVE_ds64 ;
				break ;

			case fmt_MARKER:
					psf_log_printf (psf, "%M : %u\n", marker, chunk_size) ;
					if ((error = wavlike_read_fmt_chunk (psf, chunk_size)) != 0)
						return error ;
					format = wav_fmt->format ;
					parsestage |= HAVE_fmt ;
					break ;

			case bext_MARKER :
					if ((error = wavlike_read_bext_chunk (psf, chunk_size)) != 0)
						return error ;
					parsestage |= HAVE_bext ;
					break ;

			case cart_MARKER :
					if ((error = wavlike_read_cart_chunk (psf, chunk_size)) != 0)
						return error ;
					parsestage |= HAVE_cart ;
					break ;

			case INFO_MARKER :
			case LIST_MARKER :
					if ((error = wavlike_subchunk_parse (psf, marker, chunk_size)) != 0)
						return error ;
					parsestage |= HAVE_other ;
					break ;

			case PEAK_MARKER :
					if ((parsestage & (HAVE_ds64 | HAVE_fmt)) != (HAVE_ds64 | HAVE_fmt))
						return SFE_RF64_PEAK_B4_FMT ;

					parsestage |= HAVE_PEAK ;

					psf_log_printf (psf, "%M : %u\n", marker, chunk_size) ;
					if ((error = wavlike_read_peak_chunk (psf, chunk_size)) != 0)
						return error ;
					psf->peak_info->peak_loc = ((parsestage & HAVE_data) == 0) ? SF_PEAK_START : SF_PEAK_END ;
					break ;

			case data_MARKER :
				/* see wav for more sophisticated parsing -> implement state machine with parsestage */

				if (HAVE_CHUNK (HAVE_ds64))
				{	if (chunk_size == 0xffffffff)
						psf_log_printf (psf, "%M : 0x%x\n", marker, chunk_size) ;
					else
						psf_log_printf (psf, "%M : 0x%x (should be 0xffffffff\n", marker, chunk_size) ;
					psf->datalength = ds64_datalength ;
					}
				else
				{	if (chunk_size == 0xffffffff)
					{	psf_log_printf (psf, "%M : 0x%x\n", marker, chunk_size) ;
						psf_log_printf (psf, "  *** Data length not specified no 'ds64' chunk.\n") ;
						}
					else
					{	psf_log_printf (psf, "%M : 0x%x\n**** Weird, RF64 file without a 'ds64' chunk and no valid 'data' size.\n", marker, chunk_size) ;
						psf->datalength = chunk_size ;
						} ;
					} ;

				psf->dataoffset = psf_ftell (psf) ;

				if (psf->dataoffset > 0)
				{	if (chunk_size == 0 && riff_size == 8 && psf->filelength > 44)
					{	psf_log_printf (psf, "  *** Looks like a WAV file which wasn't closed properly. Fixing it.\n") ;
						psf->datalength = psf->filelength - psf->dataoffset ;
						} ;

					/* Only set dataend if there really is data at the end. */
					if (psf->datalength + psf->dataoffset < psf->filelength)
						psf->dataend = psf->datalength + psf->dataoffset ;

					if (NOT (psf->sf.seekable) || psf->dataoffset < 0)
						break ;

					/* Seek past data and continue reading header. */
					psf_fseek (psf, psf->datalength, SEEK_CUR) ;

					if (psf_ftell (psf) != psf->datalength + psf->dataoffset)
						psf_log_printf (psf, "  *** psf_fseek past end error ***\n") ;
					} ;
				break ;

			case JUNK_MARKER :
			case PAD_MARKER :
				psf_log_printf (psf, "%M : %d\n", marker, chunk_size) ;
				psf_binheader_readf (psf, "j", chunk_size) ;
				break ;

			default :
					if (chunk_size >= 0xffff0000)
					{	psf_log_printf (psf, "*** Unknown chunk marker (%X) at position %D with length %u. Exiting parser.\n", marker, psf_ftell (psf) - 8, chunk_size) ;
						done = SF_TRUE ;
						break ;
						} ;

					if (isprint ((marker >> 24) & 0xFF) && isprint ((marker >> 16) & 0xFF)
						&& isprint ((marker >> 8) & 0xFF) && isprint (marker & 0xFF))
					{	psf_log_printf (psf, "*** %M : %d (unknown marker)\n", marker, chunk_size) ;
						psf_binheader_readf (psf, "j", chunk_size) ;
						break ;
						} ;
					if (psf_ftell (psf) & 0x03)
					{	psf_log_printf (psf, "  Unknown chunk marker at position 0x%x. Resynching.\n", chunk_size - 4) ;
						psf_binheader_readf (psf, "j", -3) ;
						break ;
						} ;
					psf_log_printf (psf, "*** Unknown chunk marker (0x%X) at position 0x%X. Exiting parser.\n", marker, psf_ftell (psf) - 4) ;
					done = SF_TRUE ;
					break ;
			} ;	/* switch (marker) */

		/* The 'data' chunk, a chunk size of 0xffffffff means that the 'data' chunk size
		** is actually given by the ds64_datalength field.
		*/
		if (marker != data_MARKER && chunk_size >= psf->filelength)
		{	psf_log_printf (psf, "*** Chunk size %u > file length %D. Exiting parser.\n", chunk_size, psf->filelength) ;
			break ;
			} ;

		if (psf_ftell (psf) >= psf->filelength - SIGNED_SIZEOF (marker))
		{	psf_log_printf (psf, "End\n") ;
			break ;
			} ;
		} ;

	if (psf->dataoffset <= 0)
		return SFE_RF64_NO_DATA ;

	if (psf->sf.channels < 1)
		return SFE_CHANNEL_COUNT_ZERO ;

	if (psf->sf.channels > SF_MAX_CHANNELS)
		return SFE_CHANNEL_COUNT ;

	/* WAVs can be little or big endian */
	psf->endian = psf->rwf_endian ;

	psf_fseek (psf, psf->dataoffset, SEEK_SET) ;

	if (psf->is_pipe == 0)
	{	/*
		** Check for 'wvpk' at the start of the DATA section. Not able to
		** handle this.
		*/
		psf_binheader_readf (psf, "4", &marker) ;
		if (marker == wvpk_MARKER || marker == OggS_MARKER)
			return SFE_WAV_WVPK_DATA ;
		} ;

	/* Seek to start of DATA section. */
	psf_fseek (psf, psf->dataoffset, SEEK_SET) ;

	if (psf->blockwidth)
	{	if (psf->filelength - psf->dataoffset < psf->datalength)
			psf->sf.frames = (psf->filelength - psf->dataoffset) / psf->blockwidth ;
		else
			psf->sf.frames = psf->datalength / psf->blockwidth ;
		} ;

	if (frame_count != psf->sf.frames)
		psf_log_printf (psf, "*** Calculated frame count %d does not match value from 'ds64' chunk of %d.\n", psf->sf.frames, frame_count) ;

	switch (format)
	{
		case WAVE_FORMAT_EXTENSIBLE :

			/* with WAVE_FORMAT_EXTENSIBLE the psf->sf.format field is already set. We just have to set the major to rf64 */
			psf->sf.format = (psf->sf.format & ~SF_FORMAT_TYPEMASK) | SF_FORMAT_RF64 ;

			if (psf->sf.format == (SF_FORMAT_WAVEX | SF_FORMAT_MS_ADPCM))
			{	*blockalign = wav_fmt->msadpcm.blockalign ;
				*framesperblock = wav_fmt->msadpcm.samplesperblock ;
				} ;
			break ;

		case WAVE_FORMAT_PCM :
					psf->sf.format = SF_FORMAT_RF64 | u_bitwidth_to_subformat (psf->bytewidth * 8) ;
					break ;

		case WAVE_FORMAT_MULAW :
		case IBM_FORMAT_MULAW :
					psf->sf.format = (SF_FORMAT_RF64 | SF_FORMAT_ULAW) ;
					break ;

		case WAVE_FORMAT_ALAW :
		case IBM_FORMAT_ALAW :
					psf->sf.format = (SF_FORMAT_RF64 | SF_FORMAT_ALAW) ;
					break ;

		case WAVE_FORMAT_MS_ADPCM :
					psf->sf.format = (SF_FORMAT_RF64 | SF_FORMAT_MS_ADPCM) ;
					*blockalign = wav_fmt->msadpcm.blockalign ;
					*framesperblock = wav_fmt->msadpcm.samplesperblock ;
					break ;

		case WAVE_FORMAT_IMA_ADPCM :
					psf->sf.format = (SF_FORMAT_RF64 | SF_FORMAT_IMA_ADPCM) ;
					*blockalign = wav_fmt->ima.blockalign ;
					*framesperblock = wav_fmt->ima.samplesperblock ;
					break ;

		case WAVE_FORMAT_GSM610 :
					psf->sf.format = (SF_FORMAT_RF64 | SF_FORMAT_GSM610) ;
					break ;

		case WAVE_FORMAT_IEEE_FLOAT :
					psf->sf.format = SF_FORMAT_RF64 ;
					psf->sf.format |= (psf->bytewidth == 8) ? SF_FORMAT_DOUBLE : SF_FORMAT_FLOAT ;
					break ;

		case WAVE_FORMAT_G721_ADPCM :
					psf->sf.format = SF_FORMAT_RF64 | SF_FORMAT_G721_32 ;
					break ;

		default : return SFE_UNIMPLEMENTED ;
		} ;

	if (wpriv->fmt_is_broken)
		wavlike_analyze (psf) ;

	/* Only set the format endian-ness if its non-standard big-endian. */
	if (psf->endian == SF_ENDIAN_BIG)
		psf->sf.format |= SF_ENDIAN_BIG ;

	return 0 ;
} /* rf64_read_header */


wav_write_fmt_chunk (SF_PRIVATE *psf)
{	int subformat, fmt_size, add_fact_chunk = 0 ;

	subformat = SF_CODEC (psf->sf.format) ;

	switch (subformat)
	{	case SF_FORMAT_PCM_U8 :
		case SF_FORMAT_PCM_16 :
		case SF_FORMAT_PCM_24 :
		case SF_FORMAT_PCM_32 :
					fmt_size = 2 + 2 + 4 + 4 + 2 + 2 ;

					/* fmt : format, channels, samplerate */
					psf_binheader_writef (psf, "4224", BHW4 (fmt_size), BHW2 (WAVE_FORMAT_PCM), BHW2 (psf->sf.channels), BHW4 (psf->sf.samplerate)) ;
					/*  fmt : bytespersec */
					psf_binheader_writef (psf, "4", BHW4 (psf->sf.samplerate * psf->bytewidth * psf->sf.channels)) ;
					/*  fmt : blockalign, bitwidth */
					psf_binheader_writef (psf, "22", BHW2 (psf->bytewidth * psf->sf.channels), BHW2 (psf->bytewidth * 8)) ;
					break ;

		case SF_FORMAT_FLOAT :
		case SF_FORMAT_DOUBLE :
					fmt_size = 2 + 2 + 4 + 4 + 2 + 2 ;

					/* fmt : format, channels, samplerate */
					psf_binheader_writef (psf, "4224", BHW4 (fmt_size), BHW2 (WAVE_FORMAT_IEEE_FLOAT), BHW2 (psf->sf.channels), BHW4 (psf->sf.samplerate)) ;
					/*  fmt : bytespersec */
					psf_binheader_writef (psf, "4", BHW4 (psf->sf.samplerate * psf->bytewidth * psf->sf.channels)) ;
					/*  fmt : blockalign, bitwidth */
					psf_binheader_writef (psf, "22", BHW2 (psf->bytewidth * psf->sf.channels), BHW2 (psf->bytewidth * 8)) ;

					add_fact_chunk = SF_TRUE ;
					break ;

		case SF_FORMAT_ULAW :
					fmt_size = 2 + 2 + 4 + 4 + 2 + 2 + 2 ;

					/* fmt : format, channels, samplerate */
					psf_binheader_writef (psf, "4224", BHW4 (fmt_size), BHW2 (WAVE_FORMAT_MULAW), BHW2 (psf->sf.channels), BHW4 (psf->sf.samplerate)) ;
					/*  fmt : bytespersec */
					psf_binheader_writef (psf, "4", BHW4 (psf->sf.samplerate * psf->bytewidth * psf->sf.channels)) ;
					/*  fmt : blockalign, bitwidth, extrabytes */
					psf_binheader_writef (psf, "222", BHW2 (psf->bytewidth * psf->sf.channels), BHW2 (8), BHW2 (0)) ;

					add_fact_chunk = SF_TRUE ;
					break ;

		case SF_FORMAT_ALAW :
					fmt_size = 2 + 2 + 4 + 4 + 2 + 2 + 2 ;

					/* fmt : format, channels, samplerate */
					psf_binheader_writef (psf, "4224", BHW4 (fmt_size), BHW2 (WAVE_FORMAT_ALAW), BHW2 (psf->sf.channels), BHW4 (psf->sf.samplerate)) ;
					/*  fmt : bytespersec */
					psf_binheader_writef (psf, "4", BHW4 (psf->sf.samplerate * psf->bytewidth * psf->sf.channels)) ;
					/*  fmt : blockalign, bitwidth, extrabytes */
					psf_binheader_writef (psf, "222", BHW2 (psf->bytewidth * psf->sf.channels), BHW2 (8), BHW2 (0)) ;

					add_fact_chunk = SF_TRUE ;
					break ;

		/* Lite remove start */
		case SF_FORMAT_IMA_ADPCM :
					{	int blockalign, framesperblock, bytespersec ;

						blockalign		= wavlike_srate2blocksize (psf->sf.samplerate * psf->sf.channels) ;
						framesperblock	= 2 * (blockalign - 4 * psf->sf.channels) / psf->sf.channels + 1 ;
						bytespersec		= (psf->sf.samplerate * blockalign) / framesperblock ;

						/* fmt chunk. */
						fmt_size = 2 + 2 + 4 + 4 + 2 + 2 + 2 + 2 ;

						/* fmt : size, WAV format type, channels, samplerate, bytespersec */
						psf_binheader_writef (psf, "42244", BHW4 (fmt_size), BHW2 (WAVE_FORMAT_IMA_ADPCM),
									BHW2 (psf->sf.channels), BHW4 (psf->sf.samplerate), BHW4 (bytespersec)) ;

						/* fmt : blockalign, bitwidth, extrabytes, framesperblock. */
						psf_binheader_writef (psf, "2222", BHW2 (blockalign), BHW2 (4), BHW2 (2), BHW2 (framesperblock)) ;
						} ;

					add_fact_chunk = SF_TRUE ;
					break ;

		case SF_FORMAT_MS_ADPCM :
					{	int	blockalign, framesperblock, bytespersec, extrabytes ;

						blockalign		= wavlike_srate2blocksize (psf->sf.samplerate * psf->sf.channels) ;
						framesperblock	= 2 + 2 * (blockalign - 7 * psf->sf.channels) / psf->sf.channels ;
						bytespersec		= (psf->sf.samplerate * blockalign) / framesperblock ;

						/* fmt chunk. */
						extrabytes	= 2 + 2 + WAVLIKE_MSADPCM_ADAPT_COEFF_COUNT * (2 + 2) ;
						fmt_size	= 2 + 2 + 4 + 4 + 2 + 2 + 2 + extrabytes ;

						/* fmt : size, WAV format type, channels. */
						psf_binheader_writef (psf, "422", BHW4 (fmt_size), BHW2 (WAVE_FORMAT_MS_ADPCM), BHW2 (psf->sf.channels)) ;

						/* fmt : samplerate, bytespersec. */
						psf_binheader_writef (psf, "44", BHW4 (psf->sf.samplerate), BHW4 (bytespersec)) ;

						/* fmt : blockalign, bitwidth, extrabytes, framesperblock. */
						psf_binheader_writef (psf, "22222", BHW2 (blockalign), BHW2 (4), BHW2 (extrabytes), BHW2 (framesperblock), BHW2 (7)) ;

						wavlike_msadpcm_write_adapt_coeffs (psf) ;
						} ;

					add_fact_chunk = SF_TRUE ;
					break ;

		case SF_FORMAT_G721_32 :
					/* fmt chunk. */
					fmt_size = 2 + 2 + 4 + 4 + 2 + 2 + 2 + 2 ;

					/* fmt : size, WAV format type, channels, samplerate, bytespersec */
					psf_binheader_writef (psf, "42244", BHW4 (fmt_size), BHW2 (WAVE_FORMAT_G721_ADPCM),
								BHW2 (psf->sf.channels), BHW4 (psf->sf.samplerate), BHW4 (psf->sf.samplerate * psf->sf.channels / 2)) ;

					/* fmt : blockalign, bitwidth, extrabytes, auxblocksize. */
					psf_binheader_writef (psf, "2222", BHW2 (64), BHW2 (4), BHW2 (2), BHW2 (0)) ;

					add_fact_chunk = SF_TRUE ;
					break ;

		case SF_FORMAT_NMS_ADPCM_16 :
		case SF_FORMAT_NMS_ADPCM_24 :
		case SF_FORMAT_NMS_ADPCM_32 :
					{	int bytespersec, blockalign, bitwidth ;

						bitwidth 	= subformat == SF_FORMAT_NMS_ADPCM_16 ? 2 : subformat == SF_FORMAT_NMS_ADPCM_24 ? 3 : 4 ;
						blockalign 	= 20 * bitwidth + 2 ;
						bytespersec	= psf->sf.samplerate * blockalign / 160 ;

						/* fmt chunk. */
						fmt_size = 2 + 2 + 4 + 4 + 2 + 2 ;

						/* fmt : format, channels, samplerate */
						psf_binheader_writef (psf, "4224", BHW4 (fmt_size), BHW2 (WAVE_FORMAT_NMS_VBXADPCM),
									BHW2 (psf->sf.channels), BHW4 (psf->sf.samplerate)) ;
						/*  fmt : bytespersec, blockalign, bitwidth */
						psf_binheader_writef (psf, "422", BHW4 (bytespersec), BHW2 (blockalign), BHW2 (bitwidth)) ;

						add_fact_chunk = SF_TRUE ;
						break ;
						}

		/* Lite remove end */

		case SF_FORMAT_GSM610 :
					{	int	blockalign, framesperblock, bytespersec ;

						blockalign		= WAVLIKE_GSM610_BLOCKSIZE ;
						framesperblock	= WAVLIKE_GSM610_SAMPLES ;
						bytespersec		= (psf->sf.samplerate * blockalign) / framesperblock ;

						/* fmt chunk. */
						fmt_size = 2 + 2 + 4 + 4 + 2 + 2 + 2 + 2 ;

						/* fmt : size, WAV format type, channels. */
						psf_binheader_writef (psf, "422", BHW4 (fmt_size), BHW2 (WAVE_FORMAT_GSM610), BHW2 (psf->sf.channels)) ;

						/* fmt : samplerate, bytespersec. */
						psf_binheader_writef (psf, "44", BHW4 (psf->sf.samplerate), BHW4 (bytespersec)) ;

						/* fmt : blockalign, bitwidth, extrabytes, framesperblock. */
						psf_binheader_writef (psf, "2222", BHW2 (blockalign), BHW2 (0), BHW2 (2), BHW2 (framesperblock)) ;
						} ;

					add_fact_chunk = SF_TRUE ;
					break ;

		default : 	return SFE_UNIMPLEMENTED ;
		} ;

	if (add_fact_chunk)
		psf_binheader_writef (psf, "tm48", BHWm (fact_MARKER), BHW4 (4), BHW8 (psf->sf.frames)) ;

	return 0 ;
} /* wav_write_fmt_chunk */


rf64_close (SF_PRIVATE *psf)
{
	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
	{	rf64_write_tailer (psf) ;
		rf64_write_header (psf, SF_TRUE) ;
		} ;

	return 0 ;
} /* rf64_close */


w64_close (SF_PRIVATE *psf)
{
	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
		w64_write_header (psf, SF_TRUE) ;

	return 0 ;
} /* w64_close */


znulldevice(i_ctx_t *i_ctx_p)
{
    int code = gs_nulldevice(igs);
    invalidate_stack_devices(i_ctx_p);
    clear_pagedevice(istate);
    return code;
}


zoutputpage(i_ctx_t *i_ctx_p)
{
    os_ptr op = osp;
    int code;

    check_type(op[-1], t_integer);
    check_type(*op, t_boolean);
    if (gs_debug[':']) {
        gs_main_instance *minst = get_minst_from_memory((gs_memory_t *)i_ctx_p->memory.current->non_gc_memory);

        print_resource_usage(minst, &(i_ctx_p->memory), "Outputpage start");
    }
    code = gs_output_page(igs, (int)op[-1].value.intval,
                          op->value.boolval);
    if (code < 0)
        return code;
    pop(2);
    if (gs_debug[':']) {
        gs_main_instance *minst = get_minst_from_memory((gs_memory_t *)i_ctx_p->memory.current->non_gc_memory);

        print_resource_usage(minst, &(i_ctx_p->memory), "Outputpage end");
    }
    return 0;
}


static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
					      struct kvm_vcpu_events *events)
{
	if (events->flags & ~(KVM_VCPUEVENT_VALID_NMI_PENDING
			      | KVM_VCPUEVENT_VALID_SIPI_VECTOR
			      | KVM_VCPUEVENT_VALID_SHADOW
			      | KVM_VCPUEVENT_VALID_SMM
			      | KVM_VCPUEVENT_VALID_PAYLOAD))
		return -EINVAL;

	if (events->flags & KVM_VCPUEVENT_VALID_PAYLOAD) {
		if (!vcpu->kvm->arch.exception_payload_enabled)
			return -EINVAL;
		if (events->exception.pending)
			events->exception.injected = 0;
		else
			events->exception_has_payload = 0;
	} else {
		events->exception.pending = 0;
		events->exception_has_payload = 0;
	}

	if ((events->exception.injected || events->exception.pending) &&
	    (events->exception.nr > 31 || events->exception.nr == NMI_VECTOR))
		return -EINVAL;

	/* INITs are latched while in SMM */
	if (events->flags & KVM_VCPUEVENT_VALID_SMM &&
	    (events->smi.smm || events->smi.pending) &&
	    vcpu->arch.mp_state == KVM_MP_STATE_INIT_RECEIVED)
		return -EINVAL;

	process_nmi(vcpu);
	vcpu->arch.exception.injected = events->exception.injected;
	vcpu->arch.exception.pending = events->exception.pending;
	vcpu->arch.exception.nr = events->exception.nr;
	vcpu->arch.exception.has_error_code = events->exception.has_error_code;
	vcpu->arch.exception.error_code = events->exception.error_code;
	vcpu->arch.exception.has_payload = events->exception_has_payload;
	vcpu->arch.exception.payload = events->exception_payload;

	vcpu->arch.interrupt.injected = events->interrupt.injected;
	vcpu->arch.interrupt.nr = events->interrupt.nr;
	vcpu->arch.interrupt.soft = events->interrupt.soft;
	if (events->flags & KVM_VCPUEVENT_VALID_SHADOW)
		kvm_x86_ops->set_interrupt_shadow(vcpu,
						  events->interrupt.shadow);

	vcpu->arch.nmi_injected = events->nmi.injected;
	if (events->flags & KVM_VCPUEVENT_VALID_NMI_PENDING)
		vcpu->arch.nmi_pending = events->nmi.pending;
	kvm_x86_ops->set_nmi_mask(vcpu, events->nmi.masked);

	if (events->flags & KVM_VCPUEVENT_VALID_SIPI_VECTOR &&
	    lapic_in_kernel(vcpu))
		vcpu->arch.apic->sipi_vector = events->sipi_vector;

	if (events->flags & KVM_VCPUEVENT_VALID_SMM) {
		u32 hflags = vcpu->arch.hflags;
		if (events->smi.smm)
			hflags |= HF_SMM_MASK;
		else
			hflags &= ~HF_SMM_MASK;
		kvm_set_hflags(vcpu, hflags);

		vcpu->arch.smi_pending = events->smi.pending;

		if (events->smi.smm) {
			if (events->smi.smm_inside_nmi)
				vcpu->arch.hflags |= HF_SMM_INSIDE_NMI_MASK;
			else
				vcpu->arch.hflags &= ~HF_SMM_INSIDE_NMI_MASK;
			if (lapic_in_kernel(vcpu)) {
				if (events->smi.latched_init)
					set_bit(KVM_APIC_INIT, &vcpu->arch.apic->pending_events);
				else
					clear_bit(KVM_APIC_INIT, &vcpu->arch.apic->pending_events);
			}
		}
	}

	kvm_make_request(KVM_REQ_EVENT, vcpu);

	return 0;
}


static inline bool kvm_guest_apic_has_interrupt(struct kvm_vcpu *vcpu)
{
	return (is_guest_mode(vcpu) &&
			kvm_x86_ops->guest_apic_has_interrupt &&
			kvm_x86_ops->guest_apic_has_interrupt(vcpu));
}