/* error.c -- error message handling
 *
 * Copyright: N. Dessipris
 * Written on: 18/03/1991
 * Updated on: 9/7/92 KM
 * 20/12/2003 JC
 *	- i18n added, domain now separate arg
 * 14/2/07
 * 	- lock around error buffer changes
 * 20/2/08
 * 	- lock around warnings and diagnostics too, why not
 * 2/10/09
 * 	- error_exit() moved here
 * 	- gtkdoc comments
 * 24/6/10
 * 	- fmt to error_exit() may be NULL
 * 12/9/19 [dineshkannaa]
 * 	- add vips_error_buffer_copy()
 */

/*

	This file is part of VIPS.

	VIPS is free software; you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
	02110-1301  USA

 */

/*

	These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk

 */

/*
#define VIPS_DEBUG
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <glib/gi18n-lib.h>

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

#include <vips/vips.h>
#include <vips/internal.h>
#include <vips/buf.h>
#include <vips/debug.h>

#ifdef G_OS_WIN32
#include <windows.h>
#include <lmerr.h>
#endif /*G_OS_WIN32*/

/* Make global array to keep the error message buffer.
 */
#define VIPS_MAX_ERROR (10240)
static char vips_error_text[VIPS_MAX_ERROR] = "";
static VipsBuf vips_error_buf = VIPS_BUF_STATIC(vips_error_text);
static int vips_error_freeze_count = 0;

/**
 * vips_error_freeze:
 *
 * Stop errors being logged. Use [func@error_thaw] to unfreeze. You can
 * nest freeze/thaw pairs.
 */
void
vips_error_freeze(void)
{
	g_mutex_lock(&vips__global_lock);
	g_assert(vips_error_freeze_count >= 0);
	vips_error_freeze_count += 1;
	g_mutex_unlock(&vips__global_lock);
}

/**
 * vips_error_thaw:
 *
 * Re-enable error logging.
 */
void
vips_error_thaw(void)
{
	g_mutex_lock(&vips__global_lock);
	vips_error_freeze_count -= 1;
	g_assert(vips_error_freeze_count >= 0);
	g_mutex_unlock(&vips__global_lock);
}

/**
 * vips_error_buffer:
 *
 * Get a pointer to the start of the error buffer as a C string.
 * The string is owned by the error system and must not be freed.
 *
 * ::: seealso
 *     [func@error_clear].
 *
 * Returns: the error buffer as a C string which must not be freed
 */
const char *
vips_error_buffer(void)
{
	const char *msg;

	g_mutex_lock(&vips__global_lock);
	msg = vips_buf_all(&vips_error_buf);
	g_mutex_unlock(&vips__global_lock);

	return msg;
}

/**
 * vips_error_buffer_copy:
 *
 * Return a copy of the vips error buffer, and clear it.
 *
 * Returns: a copy of the libvips error buffer
 */
char *
vips_error_buffer_copy(void)
{
	char *msg;

	g_mutex_lock(&vips__global_lock);
	msg = g_strdup(vips_buf_all(&vips_error_buf));
	vips_buf_rewind(&vips_error_buf);
	g_mutex_unlock(&vips__global_lock);

	return msg;
}

/* Some systems do not have va_copy() ... this might work (it does on MSVC,
 * apparently).
 *
 * FIXME ... this should be in configure.in
 */
#ifndef va_copy
#define va_copy(d, s) ((d) = (s))
#endif

/**
 * vips_verror:
 * @domain: the source of the error
 * @fmt: `printf()`-style format string for the error
 * @ap: arguments to the format string
 *
 * Append a message to the error buffer.
 *
 * ::: seealso
 *     [func@error].
 */
void
vips_verror(const char *domain, const char *fmt, va_list ap)
{
#ifdef VIPS_DEBUG
	{
		char txt[256];
		VipsBuf buf = VIPS_BUF_STATIC(txt);
		va_list ap2;

		vips_buf_appendf(&buf, "%s: ", domain);
		va_copy(ap2, ap);
		vips_buf_vappendf(&buf, fmt, ap2);
		vips_buf_appends(&buf, "\n");
		VIPS_DEBUG_MSG("vips_verror: %s", vips_buf_all(&buf));
	}
#endif /*VIPS_DEBUG*/

	g_mutex_lock(&vips__global_lock);
	g_assert(vips_error_freeze_count >= 0);
	if (!vips_error_freeze_count) {
		if (domain)
			vips_buf_appendf(&vips_error_buf, "%s: ", domain);
		vips_buf_vappendf(&vips_error_buf, fmt, ap);
		vips_buf_appends(&vips_error_buf, "\n");
	}
	g_mutex_unlock(&vips__global_lock);

	if (vips__fatal)
		vips_error_exit("vips__fatal");
}

/**
 * vips_error:
 * @domain: the source of the error
 * @fmt: `printf()`-style format string for the error
 * @...: arguments to the format string
 *
 * Format the string in the style of [`printf()`](man:printf(3)) and append to the error buffer.
 *
 * ::: seealso
 *     [func@error_system], [func@verror].
 */
void
vips_error(const char *domain, const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	vips_verror(domain, fmt, ap);
	va_end(ap);
}

/**
 * vips_verror_system:
 * @err: the system error code
 * @domain: the source of the error
 * @fmt: `printf()`-style format string for the error
 * @ap: arguments to the format string
 *
 * Format the string in the style of [`printf()`](man:printf(3)) and append to the error buffer.
 * Then create and append a localised message based on the system error code,
 * usually the value of errno.
 *
 * ::: seealso
 *     [func@error_system].
 */
void
vips_verror_system(int err, const char *domain, const char *fmt, va_list ap)
{
	vips_verror(domain, fmt, ap);
	vips_error(_("system error"), "%s", g_strerror(err));
}

/**
 * vips_error_system:
 * @err: the system error code
 * @domain: the source of the error
 * @fmt: `printf()`-style format string for the error
 * @...: arguments to the format string
 *
 * Format the string in the style of [`printf()`](man:printf(3)) and append to the error buffer.
 * Then create and append a localised message based on the system error code,
 * usually the value of errno.
 *
 * ::: seealso
 *     [func@verror_system].
 */
void
vips_error_system(int err, const char *domain, const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	vips_verror_system(err, domain, fmt, ap);
	va_end(ap);
}

/**
 * vips_error_g:
 * @error: (out): glib error pointer
 *
 * This function sets the glib error pointer from the vips error buffer and
 * clears it. It's handy for returning errors to glib functions from vips.
 *
 * See [func@g_error] for the inverse operation.
 *
 * ::: seealso
 *     [func@GLib.set_error], [func@g_error].
 */
void
vips_error_g(GError **error)
{
	static GQuark vips_domain = 0;

	if (!vips_domain)
		vips_domain = g_quark_from_string("libvips");

	/* glib does not expect a trailing '\n' and vips always has one.
	 */
	g_mutex_lock(&vips__global_lock);
	vips_buf_removec(&vips_error_buf, '\n');
	g_mutex_unlock(&vips__global_lock);

	g_set_error(error, vips_domain, -1, "%s", vips_error_buffer());
	vips_error_clear();
}

/**
 * vips_g_error:
 * @error: glib error pointer
 *
 * This function adds the [struct@GLib.Error] to the vips error buffer and clears it. It's
 * the opposite of [func@error_g].
 *
 * ::: seealso
 *     [func@error_g].
 */
void
vips_g_error(GError **error)
{
	if (error &&
		*error) {
		vips_error("glib", "%s\n", (*error)->message);
		g_error_free(*error);
		*error = NULL;
	}
}

/**
 * vips_error_clear:
 *
 * Clear and reset the error buffer. This is typically called after presenting
 * an error to the user.
 *
 * ::: seealso
 *     [func@error_buffer].
 */
void
vips_error_clear(void)
{
	g_mutex_lock(&vips__global_lock);
	vips_buf_rewind(&vips_error_buf);
	g_mutex_unlock(&vips__global_lock);
}

/**
 * vips_error_exit:
 * @fmt: `printf()`-style format string for the message
 * @...: arguments to the format string
 *
 * Sends a formatted error message to stderr, then sends the contents of the
 * error buffer, if any, then shuts down vips and terminates the program with
 * an error code.
 *
 * @fmt may be `NULL`, in which case only the error buffer is printed before
 * exiting.
 *
 * ::: seealso
 *     [func@error].
 */
void
vips_error_exit(const char *fmt, ...)
{
	if (fmt) {
		va_list ap;

		fprintf(stderr, "%s: ", vips_get_prgname());

		va_start(ap, fmt);
		(void) vfprintf(stderr, fmt, ap);
		va_end(ap);

		fprintf(stderr, "\n");
	}

	fprintf(stderr, "%s", vips_error_buffer());

	vips_shutdown();

	if (vips__fatal)
		abort();
	else
		exit(1);
}

/**
 * vips_check_uncoded:
 * @domain: the originating domain for the error message
 * @im: image to check
 *
 * Check that the image is not coded.
 * If not, set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 on OK, or -1 on error.
 */
int
vips_check_uncoded(const char *domain, VipsImage *im)
{
	if (im->Coding != VIPS_CODING_NONE) {
		vips_error(domain, "%s", _("image must be uncoded"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_coding_noneorlabq:
 * @domain: the originating domain for the error message
 * @im: image to check
 *
 * Check that the image is uncoded or LABQ coded.
 * If not, set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 on OK, or -1 on error.
 */
int
vips_check_coding_noneorlabq(const char *domain, VipsImage *im)
{
	/* These all have codings that extract/ifthenelse/etc can ignore.
	 */
	if (im->Coding != VIPS_CODING_NONE &&
		im->Coding != VIPS_CODING_LABQ) {
		vips_error(domain,
			"%s", _("image coding must be 'none' or 'labq'"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_coding_known:
 * @domain: the originating domain for the error message
 * @im: image to check
 *
 * Check that the image is uncoded, LABQ coded or RAD coded.
 * If not, set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 on OK, or -1 on error.
 */
int
vips_check_coding_known(const char *domain, VipsImage *im)
{
	/* These all have codings that extract/ifthenelse/etc can ignore.
	 */
	if (im->Coding != VIPS_CODING_NONE &&
		im->Coding != VIPS_CODING_LABQ &&
		im->Coding != VIPS_CODING_RAD) {
		vips_error(domain, "%s", _("unknown image coding"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_coding:
 * @domain: the originating domain for the error message
 * @im: image to check
 * @coding: required coding
 *
 * Check that the image has the required @coding.
 * If not, set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 on OK, or -1 on error.
 */
int
vips_check_coding(const char *domain, VipsImage *im, VipsCoding coding)
{
	if (im->Coding != coding) {
		vips_error(domain, _("coding '%s' only"),
			vips_enum_nick(VIPS_TYPE_CODING, coding));
		return -1;
	}

	return 0;
}

/**
 * vips_check_mono:
 * @domain: the originating domain for the error message
 * @im: image to check
 *
 * Check that the image has exactly one band.
 * Otherwise set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_mono(const char *domain, VipsImage *im)
{
	if (im->Bands != 1) {
		vips_error(domain, "%s", _("image must one band"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_bands:
 * @domain: the originating domain for the error message
 * @im: image to check
 * @bands: must have this many bands
 *
 * Check that the image has @bands bands.
 * Otherwise set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_bands(const char *domain, VipsImage *im, int bands)
{
	if (im->Bands != bands) {
		vips_error(domain, _("image must have %d bands"), bands);
		return -1;
	}

	return 0;
}

/**
 * vips_check_bands_1or3:
 * @domain: the originating domain for the error message
 * @im: image to check
 *
 * Check that the image has either one or three bands.
 * Otherwise set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_bands_1or3(const char *domain, VipsImage *im)
{
	if (im->Bands != 1 && im->Bands != 3) {
		vips_error(domain, "%s",
			_("image must have one or three bands"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_bands_atleast:
 * @domain: the originating domain for the error message
 * @im: image to check
 * @bands: at least this many bands
 *
 * Check that the image has at least @bands bands.
 * Otherwise set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_bands_atleast(const char *domain, VipsImage *im, int bands)
{
	if (im->Bands < bands) {
		vips_error(domain,
			_("image must have at least %d bands"), bands);
		return -1;
	}

	return 0;
}

/**
 * vips_check_bands_1orn:
 * @domain: the originating domain for the error message
 * @im1: first image to check
 * @im2: second image to check
 *
 * Check that the images have the same number of bands, or that one of the
 * images has just 1 band.
 * If not, set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 on OK, or -1 on error.
 */
int
vips_check_bands_1orn(const char *domain, VipsImage *im1, VipsImage *im2)
{
	if (im1->Bands != im2->Bands &&
		(im1->Bands != 1 && im2->Bands != 1)) {
		vips_error(domain, "%s",
			_("images must have the same number of bands, "
			  "or one must be single-band"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_bands_1orn_unary:
 * @domain: the originating domain for the error message
 * @im: image to check
 * @n: number of bands, or 1
 *
 * Check that an image has 1 or @n bands. Handy for unary operations, cf.
 * [func@check_bands_1orn].
 * If not, set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@check_bands_1orn].
 *
 * Returns: 0 on OK, or -1 on error.
 */
int
vips_check_bands_1orn_unary(const char *domain, VipsImage *im, int n)
{
	if (im->Bands != 1 && im->Bands != n) {
		vips_error(domain, _("image must have 1 or %d bands"), n);
		return -1;
	}

	return 0;
}

/**
 * vips_check_noncomplex:
 * @domain: the originating domain for the error message
 * @im: image to check
 *
 * Check that the image is not complex.
 * Otherwise set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_noncomplex(const char *domain, VipsImage *im)
{
	if (vips_band_format_iscomplex(im->BandFmt)) {
		vips_error(domain, "%s", _("image must be non-complex"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_complex:
 * @domain: the originating domain for the error message
 * @im: image to check
 *
 * Check that the image is complex.
 * Otherwise set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_complex(const char *domain, VipsImage *im)
{
	if (!vips_band_format_iscomplex(im->BandFmt)) {
		vips_error(domain, "%s", _("image must be complex"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_twocomponents:
 * @domain: the originating domain for the error message
 * @im: image to check
 *
 * Check that the image is has two "components", ie. is a one-band complex or
 * a two-band non-complex.
 * Otherwise set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_twocomponents(const char *domain, VipsImage *im)
{
	if (!vips_band_format_iscomplex(im->BandFmt) &&
		im->Bands != 2) {
		vips_error(domain,
			"%s", _("image must be two-band or complex"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_format:
 * @domain: the originating domain for the error message
 * @im: image to check
 * @fmt: format to test for
 *
 * Check that the image has the specified format.
 * Otherwise set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_format(const char *domain, VipsImage *im, VipsBandFormat fmt)
{
	if (im->BandFmt != fmt) {
		vips_error(domain,
			_("image must be %s"),
			vips_enum_string(VIPS_TYPE_BAND_FORMAT, fmt));
		return -1;
	}

	return 0;
}

/**
 * vips_check_int:
 * @domain: the originating domain for the error message
 * @im: image to check
 *
 * Check that the image is in one of the integer formats.
 * Otherwise set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_int(const char *domain, VipsImage *im)
{
	if (!vips_band_format_isint(im->BandFmt)) {
		vips_error(domain, "%s", _("image must be integer"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_uint:
 * @domain: the originating domain for the error message
 * @im: image to check
 *
 * Check that the image is in one of the unsigned integer formats.
 * Otherwise set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_uint(const char *domain, VipsImage *im)
{
	if (!vips_band_format_isuint(im->BandFmt)) {
		vips_error(domain,
			"%s", _("image must be unsigned integer"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_8or16:
 * @domain: the originating domain for the error message
 * @im: image to check
 *
 * Check that the image is 8 or 16-bit integer, signed or unsigned.
 * Otherwise set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_8or16(const char *domain, VipsImage *im)
{
	if (im->BandFmt != VIPS_FORMAT_UCHAR &&
		im->BandFmt != VIPS_FORMAT_USHORT &&
		im->BandFmt != VIPS_FORMAT_CHAR &&
		im->BandFmt != VIPS_FORMAT_SHORT) {
		vips_error(domain, "%s",
			_("image must be 8- or 16-bit integer, signed or unsigned"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_u8or16:
 * @domain: the originating domain for the error message
 * @im: image to check
 *
 * Check that the image is 8 or 16-bit unsigned integer.
 * Otherwise set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_u8or16(const char *domain, VipsImage *im)
{
	if (im->BandFmt != VIPS_FORMAT_UCHAR &&
		im->BandFmt != VIPS_FORMAT_USHORT) {
		vips_error(domain, "%s",
			_("image must be 8- or 16-bit unsigned integer"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_u8or16orf:
 * @domain: the originating domain for the error message
 * @im: image to check
 *
 * Check that the image is 8 or 16-bit unsigned integer, or float.
 * Otherwise set an error message and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_u8or16orf(const char *domain, VipsImage *im)
{
	if (im->BandFmt != VIPS_FORMAT_UCHAR &&
		im->BandFmt != VIPS_FORMAT_USHORT &&
		im->BandFmt != VIPS_FORMAT_FLOAT) {
		vips_error(domain, "%s",
			_("image must be 8- or 16-bit unsigned integer, or float"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_uintorf:
 * @domain: the originating domain for the error message
 * @im: image to check
 *
 * Check that the image is unsigned int or float.
 * Otherwise set an error message and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_uintorf(const char *domain, VipsImage *im)
{
	if (im->BandFmt != VIPS_FORMAT_UCHAR &&
		im->BandFmt != VIPS_FORMAT_USHORT &&
		im->BandFmt != VIPS_FORMAT_UINT &&
		im->BandFmt != VIPS_FORMAT_FLOAT) {
		vips_error(domain, "%s",
			_("image must be unsigned int or float"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_size_same:
 * @domain: the originating domain for the error message
 * @im1: first image to check
 * @im2: second image to check
 *
 * Check that the images have the same size.
 * If not, set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_size_same(const char *domain, VipsImage *im1, VipsImage *im2)
{
	if (im1->Xsize != im2->Xsize ||
		im1->Ysize != im2->Ysize) {
		vips_error(domain, "%s", _("images must match in size"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_oddsquare:
 * @domain: the originating domain for the error message
 * @im: image to check
 *
 * Check that the image is square and that the sides are odd.
 * If not, set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_oddsquare(const char *domain, VipsImage *im)
{
	if (im->Xsize != im->Ysize ||
		im->Xsize % 2 == 0) {
		vips_error(domain,
			"%s", _("images must be odd and square"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_bands_same:
 * @domain: the originating domain for the error message
 * @im1: first image to check
 * @im2: second image to check
 *
 * Check that the images have the same number of bands.
 * If not, set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_bands_same(const char *domain, VipsImage *im1, VipsImage *im2)
{
	if (im1->Bands != im2->Bands) {
		vips_error(domain, "%s",
			_("images must have the same number of bands"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_bandno:
 * @domain: the originating domain for the error message
 * @im: image to check
 * @bandno: band number
 *
 * @bandno should be a valid band number (ie. 0 to im->Bands - 1), or can be
 * -1, meaning all bands.
 * If not, set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_bandno(const char *domain, VipsImage *im, int bandno)
{
	if (bandno < -1 ||
		bandno > im->Bands - 1) {
		vips_error(domain, "bandno must be -1, or less than %d",
			im->Bands);
		return -1;
	}

	return 0;
}

/**
 * vips_check_format_same:
 * @domain: the originating domain for the error message
 * @im1: first image to check
 * @im2: second image to check
 *
 * Check that the images have the same format.
 * If not, set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_format_same(const char *domain, VipsImage *im1, VipsImage *im2)
{
	if (im1->BandFmt != im2->BandFmt) {
		vips_error(domain, "%s",
			_("images must have the same band format"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_coding_same:
 * @domain: the originating domain for the error message
 * @im1: first image to check
 * @im2: second image to check
 *
 * Check that the images have the same coding.
 * If not, set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_coding_same(const char *domain, VipsImage *im1, VipsImage *im2)
{
	if (im1->Coding != im2->Coding) {
		vips_error(domain, "%s",
			_("images must have the same coding"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_vector_length:
 * @domain: the originating domain for the error message
 * @n: number of elements in vector
 * @len: number of elements vector should have
 *
 * Check that @n == @len.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_vector_length(const char *domain, int n, int len)
{
	if (n != len) {
		vips_error(domain, _("vector must have %d elements"), len);
		return -1;
	}

	return 0;
}

/**
 * vips_check_vector:
 * @domain: the originating domain for the error message
 * @n: number of elements in vector
 * @im: image to check against
 *
 * Operations with a vector constant need a 1-element vector, or a vector with
 * the same number of elements as there are bands in the image, or a 1-band
 * image and a many-element vector.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_vector(const char *domain, int n, VipsImage *im)
{
	/* Here it's clearer to list the cases that are OK.
	 */
	if (n == im->Bands)
		return 0;
	if (n == 1)
		return 0;
	if (im->Bands == 1 &&
		n > 1)
		return 0;

	if (im->Bands == 1)
		vips_error(domain,
			"%s", _("vector must have 1 element"));
	else
		vips_error(domain,
			_("vector must have 1 or %d elements"),
			im->Bands);

	return -1;
}

/**
 * vips_check_hist:
 * @domain: the originating domain for the error message
 * @im: image to check
 *
 * Histogram images must have width or height 1, and must not have more than
 * 65536 elements. Return 0 if the image will pass as a histogram, or -1 and
 * set an error message otherwise.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_hist(const char *domain, VipsImage *im)
{
	if (im->Xsize != 1 &&
		im->Ysize != 1) {
		vips_error(domain, "%s",
			_("histograms must have width or height 1"));
		return -1;
	}
	if (VIPS_IMAGE_N_PELS(im) > 65536) {
		vips_error(domain, "%s",
			_("histograms must have not have more than 65536 elements"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_matrix:
 * @domain: the originating domain for the error message
 * @im: image to check
 * @out: (out): put image as in-memory doubles here
 *
 * Matrix images must have width and height less than 100000 and have 1 band.
 *
 * Return 0 if the image will pass as a matrix, or -1 and set an error
 * message otherwise.
 *
 * @out is set to be @im cast to double and stored in memory. Use
 * [func@MATRIX] to address values in @out.
 *
 * You must unref @out when you are done with it.
 *
 * ::: seealso
 *     [func@MATRIX].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_matrix(const char *domain, VipsImage *im, VipsImage **out)
{
	VipsImage *t;

	*out = NULL;

	if (im->Xsize > 100000 ||
		im->Ysize > 100000) {
		vips_error(domain, "%s", _("matrix image too large"));
		return -1;
	}
	if (im->Bands != 1) {
		vips_error(domain,
			"%s", _("matrix image must have one band"));
		return -1;
	}

	if (vips_cast(im, &t, VIPS_FORMAT_DOUBLE, NULL))
		return -1;
	if (!(*out = vips_image_copy_memory(t))) {
		VIPS_UNREF(t);
		return -1;
	}
	VIPS_UNREF(t);

	return 0;
}

/**
 * vips_check_separable:
 * @domain: the originating domain for the error message
 * @im: image to check
 *
 * Separable matrix images must have width or height 1.
 * Return 0 if the image will pass, or -1 and
 * set an error message otherwise.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_separable(const char *domain, VipsImage *im)
{
	if (im->Xsize != 1 &&
		im->Ysize != 1) {
		vips_error(domain,
			"%s", _("separable matrix images must have width or height 1"));
		return -1;
	}

	return 0;
}

/**
 * vips_check_precision_intfloat:
 * @domain: the originating domain for the error message
 * @precision: precision to check
 *
 * Check that @prec image is either float or int.
 * If not, set an error message
 * and return non-zero.
 *
 * ::: seealso
 *     [func@error].
 *
 * Returns: 0 on OK, or -1 on error.
 */
int
vips_check_precision_intfloat(const char *domain, VipsPrecision precision)
{
	if (precision != VIPS_PRECISION_INTEGER &&
		precision != VIPS_PRECISION_FLOAT) {
		vips_error(domain,
			"%s", _("precision must be int or float"));
		return -1;
	}

	return 0;
}
