/* 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 <vips/intl.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/thread.h>
#include <vips/debug.h>

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

/**
 * SECTION: errors
 * @short_description: error messages and error handling
 * @stability: Stable
 * @include: vips/vips.h
 *
 * VIPS maintains an error buffer (a log of localised text messages), 
 * a set of functions
 * for adding messages, and a way to access and clear the buffer.
 *
 * The error buffer is global, that is, it is shared between all threads. You
 * can add to the buffer from any thread (there is a lock to prevent
 * corruption), but it's sensible to only read and clear the buffer from the
 * main thread of execution.
 *
 * The general principle is: if you detect an error, log a message for the
 * user. If a function you call detects an error, just propogate it and don't
 * add another message.
 *
 * |[
 * IMAGE *im;
 *
 * if( !(im = vips_image_new_from_file( filename, NULL )) )
 *   // vips_image_new_from_file() will set a message, we don't need to
 *   return( -1 );
 *
 * if( vips_image_get_width( im ) &lt; 100 ) {
 *   // we have detected an error, we must set a message
 *   vips_error( "myprogram", "%s", _( "width too small" ) );
 *   return( -1 );
 * }
 * ]|
 *
 * The domain argument most of these functions take is not localised and is
 * supposed to indicate the component which failed.
 *
 * libvips uses g_warning() and g_info() to send warning and information
 * messages to the user. You can use the usual glib mechanisms to display or
 * divert these messages. For example, info messages are hidden by default, but
 * you can see them with: 
 *
 * |[
 * $ G_MESSAGES_DEBUG=VIPS vipsthumbnail k2.jpg 
 * VIPS-INFO: thumbnailing k2.jpg
 * VIPS-INFO: selected loader is VipsForeignLoadJpegFile
 * VIPS-INFO: input size is 1450 x 2048
 * VIPS-INFO: loading jpeg with factor 8 pre-shrink
 * VIPS-INFO: converting to processing space srgb
 * VIPS-INFO: residual reducev by 0.5
 * VIPS-INFO: 13 point mask
 * VIPS-INFO: using vector path
 * VIPS-INFO: residual reduceh by 0.5
 * VIPS-INFO: 13 point mask
 * VIPS-INFO: thumbnailing k2.jpg as ./tn_k2.jpg
 * ]|
 *
 */

/* 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 vips_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:
 *
 * Reenable 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.
 *
 * See also: vips_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.
 *
 * See also: vips_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() and append to the error buffer.
 *
 * See also: vips_error_system(), vips_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() and append to the error buffer.
 * Then create and append a localised message based on the system error code,
 * usually the value of errno.
 *
 * See also: vips_error_system().
 */
void
vips_verror_system( int err, const char *domain, const char *fmt, va_list ap )
{
	vips_verror( domain, fmt, ap );

#ifdef G_OS_WIN32
{
	char *buf;

	if( FormatMessageA(
		FORMAT_MESSAGE_ALLOCATE_BUFFER |
		FORMAT_MESSAGE_IGNORE_INSERTS |
		FORMAT_MESSAGE_FROM_SYSTEM,
		NULL,
		err,
		MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), 
		(LPSTR) &buf, 0, NULL ) ) {
		vips_error( _( "windows error" ), "%s", buf );
		LocalFree( buf );
	}
}
#else /*!G_OS_WIN32*/
{
	char *buf;

	buf = g_locale_to_utf8( strerror( err ), -1, NULL, NULL, NULL );
	vips_error( _( "unix error" ), "%s", buf );
	g_free( buf );
}
#endif /*G_OS_WIN32*/
}

/**
 * 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() and append to the error buffer.
 * Then create and append a localised message based on the system error code,
 * usually the value of errno.
 *
 * See also: vips_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 vips_g_error() for the inverse operation.
 *
 * See also: g_set_error(), vips_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 %GError to the vips error buffer and clears it. It's
 * the opposite of vips_error_g().
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 * vips_check_bands_1orn().
 * If not, set an error message
 * and return non-zero.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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.
 *
 * See also: vips_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. 
 *
 * See also: vips_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.
 *
 * See also: vips_error().
 *
 * Returns: 0 if OK, -1 otherwise.
 */
int
vips_check_vector( const char *domain, int n, VipsImage *im )
{
	if( n != 1 && im->Bands != 1 && n != im->Bands ) {
		vips_error( domain, 
			_( "vector must have 1 or %d elements" ), im->Bands );
		return( -1 );
	}

	return( 0 );
}

/**
 * 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.
 *
 * See also: vips_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
 * VIPS_MATRIX() to address values in @out. 
 *
 * You must unref @out when you are done with it.
 *
 * See also: VIPS_MATRIX(), vips_object_local()
 *
 * 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.
 *
 * See also: vips_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.
 *
 * See also: vips_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 );
}
