1089 lines
22 KiB
C
1089 lines
22 KiB
C
/* VIPS package handling.
|
|
*
|
|
* J. Cupitt, 8/4/93.
|
|
*
|
|
* 18/2/04 JC
|
|
* - now uses g_module_*() instead of dlopen()
|
|
* 9/8/04
|
|
* - uses glib dir scanning stuff instead of dirent.h
|
|
* 20/5/08
|
|
* - note_dependencies() does IMAGEVEC as well as IMAGE
|
|
* 5/8/08
|
|
* - silent success in loading plugins if the dir isn't there
|
|
*/
|
|
|
|
/*
|
|
|
|
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif /*HAVE_CONFIG_H*/
|
|
#include <vips/intl.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#ifdef HAVE_SYS_PARAM_H
|
|
#include <sys/param.h>
|
|
#endif /*HAVE_SYS_PARAM_H*/
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <limits.h>
|
|
|
|
#include <vips/vips.h>
|
|
|
|
#ifdef WITH_DMALLOC
|
|
#include <dmalloc.h>
|
|
#endif /*WITH_DMALLOC*/
|
|
|
|
/* Standard VIPS packages.
|
|
*/
|
|
extern im_package im__arithmetic;
|
|
extern im_package im__cimg;
|
|
extern im_package im__boolean;
|
|
extern im_package im__colour;
|
|
extern im_package im__conversion;
|
|
extern im_package im__convolution;
|
|
extern im_package im__format;
|
|
extern im_package im__freq_filt;
|
|
extern im_package im__histograms_lut;
|
|
extern im_package im__inplace;
|
|
extern im_package im__matrix;
|
|
extern im_package im__morphology;
|
|
extern im_package im__mosaicing;
|
|
extern im_package im__other;
|
|
extern im_package im__relational;
|
|
extern im_package im__resample;
|
|
extern im_package im__video;
|
|
|
|
/* im_guess_prefix() args.
|
|
*/
|
|
static im_arg_desc guess_prefix_args[] = {
|
|
IM_INPUT_STRING( "argv0" ),
|
|
IM_INPUT_STRING( "env_name" ),
|
|
IM_OUTPUT_STRING( "PREFIX" )
|
|
};
|
|
|
|
/* Call im_guess_prefix() via arg vector.
|
|
*/
|
|
static int
|
|
guess_prefix_vec( im_object *argv )
|
|
{
|
|
const char *prefix = im_guess_prefix( argv[0], argv[1] );
|
|
|
|
if( !prefix ) {
|
|
argv[2] = NULL;
|
|
return( -1 );
|
|
}
|
|
|
|
argv[2] = im_strdup( NULL, prefix );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Description of im_guess_prefix.
|
|
*/
|
|
static im_function guess_prefix_desc = {
|
|
"im_guess_prefix", /* Name */
|
|
"guess install area", /* Description */
|
|
0, /* Flags */
|
|
guess_prefix_vec, /* Dispatch function */
|
|
IM_NUMBER( guess_prefix_args ), /* Size of arg list */
|
|
guess_prefix_args /* Arg list */
|
|
};
|
|
|
|
/* im_guess_libdir() args.
|
|
*/
|
|
static im_arg_desc guess_libdir_args[] = {
|
|
IM_INPUT_STRING( "argv0" ),
|
|
IM_INPUT_STRING( "env_name" ),
|
|
IM_OUTPUT_STRING( "LIBDIR" )
|
|
};
|
|
|
|
/* Call im_guess_libdir() via arg vector.
|
|
*/
|
|
static int
|
|
guess_libdir_vec( im_object *argv )
|
|
{
|
|
const char *libdir = im_guess_libdir( argv[0], argv[1] );
|
|
|
|
if( !libdir ) {
|
|
argv[2] = NULL;
|
|
return( -1 );
|
|
}
|
|
|
|
argv[2] = im_strdup( NULL, libdir );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Description of im_guess_libdir.
|
|
*/
|
|
static im_function guess_libdir_desc = {
|
|
"im_guess_libdir", /* Name */
|
|
"guess library area", /* Description */
|
|
0, /* Flags */
|
|
guess_libdir_vec, /* Dispatch function */
|
|
IM_NUMBER( guess_libdir_args ), /* Size of arg list */
|
|
guess_libdir_args /* Arg list */
|
|
};
|
|
|
|
/* im_header_int() args.
|
|
*/
|
|
static im_arg_desc header_int_args[] = {
|
|
IM_INPUT_STRING( "field" ),
|
|
IM_INPUT_IMAGE( "image" ),
|
|
IM_OUTPUT_INT( "value" )
|
|
};
|
|
|
|
/* Call im_header_int() via arg vector.
|
|
*/
|
|
static int
|
|
header_int_vec( im_object *argv )
|
|
{
|
|
return( im_header_int( (IMAGE *) argv[1], (const char *) argv[0],
|
|
(int *) argv[2] ) );
|
|
}
|
|
|
|
/* Description of im_header_int().
|
|
*/
|
|
static im_function header_int_desc = {
|
|
"im_header_int", /* Name */
|
|
"extract int fields from header", /* Description */
|
|
0, /* Flags */
|
|
header_int_vec, /* Dispatch function */
|
|
IM_NUMBER( header_int_args ), /* Size of arg list */
|
|
header_int_args /* Arg list */
|
|
};
|
|
|
|
/* im_header_get_type() args.
|
|
*/
|
|
static im_arg_desc header_get_type_args[] = {
|
|
IM_INPUT_STRING( "field" ),
|
|
IM_INPUT_IMAGE( "image" ),
|
|
IM_OUTPUT_INT( "gtype" )
|
|
};
|
|
|
|
/* Call im_header_get_type() via arg vector.
|
|
*/
|
|
static int
|
|
header_get_type_vec( im_object *argv )
|
|
{
|
|
int *out = ((int *) argv[2]);
|
|
|
|
*out = im_header_get_type( (IMAGE *) argv[1], (const char *) argv[0] );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Description of im_header_get_type().
|
|
*/
|
|
static im_function header_get_type_desc = {
|
|
"im_header_get_type", /* Name */
|
|
"return field type", /* Description */
|
|
0, /* Flags */
|
|
header_get_type_vec, /* Dispatch function */
|
|
IM_NUMBER( header_get_type_args ),/* Size of arg list */
|
|
header_get_type_args /* Arg list */
|
|
};
|
|
|
|
/* im_header_double() args.
|
|
*/
|
|
static im_arg_desc header_double_args[] = {
|
|
IM_INPUT_STRING( "field" ),
|
|
IM_INPUT_IMAGE( "image" ),
|
|
IM_OUTPUT_DOUBLE( "value" )
|
|
};
|
|
|
|
/* Call im_header_double() via arg vector.
|
|
*/
|
|
static int
|
|
header_double_vec( im_object *argv )
|
|
{
|
|
return( im_header_double( (IMAGE *) argv[1], (const char *) argv[0],
|
|
(double *) argv[2] ) );
|
|
}
|
|
|
|
/* Description of im_header_double().
|
|
*/
|
|
static im_function header_double_desc = {
|
|
"im_header_double", /* Name */
|
|
"extract double fields from header", /* Description */
|
|
0, /* Flags */
|
|
header_double_vec, /* Dispatch function */
|
|
IM_NUMBER( header_double_args ), /* Size of arg list */
|
|
header_double_args /* Arg list */
|
|
};
|
|
|
|
/* im_header_string() args.
|
|
*/
|
|
static im_arg_desc header_string_args[] = {
|
|
IM_INPUT_STRING( "field" ),
|
|
IM_INPUT_IMAGE( "image" ),
|
|
IM_OUTPUT_STRING( "value" )
|
|
};
|
|
|
|
/* Call im_header_string() via arg vector.
|
|
*/
|
|
static int
|
|
header_string_vec( im_object *argv )
|
|
{
|
|
char *out;
|
|
|
|
if( im_header_string( (IMAGE *) argv[1],
|
|
(const char *) argv[0], &out ) ||
|
|
!(argv[2] = im_strdup( NULL, out )) )
|
|
return( -1 );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Description of im_header_string().
|
|
*/
|
|
static im_function header_string_desc = {
|
|
"im_header_string", /* Name */
|
|
"extract string fields from header", /* Description */
|
|
0, /* Flags */
|
|
header_string_vec, /* Dispatch function */
|
|
IM_NUMBER( header_string_args ), /* Size of arg list */
|
|
header_string_args /* Arg list */
|
|
};
|
|
|
|
/* im_version_string() args.
|
|
*/
|
|
static im_arg_desc version_string_args[] = {
|
|
IM_OUTPUT_STRING( "version" )
|
|
};
|
|
|
|
/* Call im_version_string() via arg vector.
|
|
*/
|
|
static int
|
|
version_string_vec( im_object *argv )
|
|
{
|
|
if( !(argv[0] = im_strdup( NULL, im_version_string() )) )
|
|
return( -1 );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Description of im_version_string.
|
|
*/
|
|
static im_function version_string_desc = {
|
|
"im_version_string", /* Name */
|
|
"VIPS version string", /* Description */
|
|
0, /* Flags */
|
|
version_string_vec, /* Dispatch function */
|
|
IM_NUMBER( version_string_args ), /* Size of arg list */
|
|
version_string_args /* Arg list */
|
|
};
|
|
|
|
/* im_version() args.
|
|
*/
|
|
static im_arg_desc version_args[] = {
|
|
IM_INPUT_INT( "flag" ),
|
|
IM_OUTPUT_INT( "version" )
|
|
};
|
|
|
|
/* Call im_version() via arg vector.
|
|
*/
|
|
static int
|
|
version_vec( im_object *argv )
|
|
{
|
|
int flag = *((int *) argv[0]);
|
|
int *out = ((int *) argv[1]);
|
|
|
|
int version = im_version( flag );
|
|
|
|
if( version < 0 )
|
|
return( -1 );
|
|
|
|
*out = version;
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Description of im_version.
|
|
*/
|
|
static im_function version_desc = {
|
|
"im_version", /* Name */
|
|
"VIPS version number", /* Description */
|
|
0, /* Flags */
|
|
version_vec, /* Dispatch function */
|
|
IM_NUMBER( version_args ), /* Size of arg list */
|
|
version_args /* Arg list */
|
|
};
|
|
|
|
/* im_cache() args.
|
|
*/
|
|
static im_arg_desc cache_args[] = {
|
|
IM_INPUT_IMAGE( "in" ),
|
|
IM_OUTPUT_IMAGE( "out" ),
|
|
IM_INPUT_INT( "tile_width" ),
|
|
IM_INPUT_INT( "tile_height" ),
|
|
IM_INPUT_INT( "max_tiles" )
|
|
};
|
|
|
|
/* Call im_cache() via arg vector.
|
|
*/
|
|
static int
|
|
cache_vec( im_object *argv )
|
|
{
|
|
int tile_width = *((int *) argv[2]);
|
|
int tile_height = *((int *) argv[3]);
|
|
int max_tiles = *((int *) argv[4]);
|
|
|
|
return( im_cache( argv[0], argv[1],
|
|
tile_width, tile_height, max_tiles ) );
|
|
}
|
|
|
|
/* Description of im_cache.
|
|
*/
|
|
static im_function cache_desc = {
|
|
"im_cache", /* Name */
|
|
"cache results of an operation",/* Description */
|
|
0, /* Flags */
|
|
cache_vec, /* Dispatch function */
|
|
IM_NUMBER( cache_args ), /* Size of arg list */
|
|
cache_args /* Arg list */
|
|
};
|
|
|
|
/* im_binfile() args.
|
|
*/
|
|
static im_arg_desc binfile_args[] = {
|
|
IM_INPUT_STRING( "filename" ),
|
|
IM_OUTPUT_IMAGE( "out" ),
|
|
IM_INPUT_INT( "width" ),
|
|
IM_INPUT_INT( "height" ),
|
|
IM_INPUT_INT( "bands" ),
|
|
IM_INPUT_INT( "offset" )
|
|
};
|
|
|
|
/* Call im_binfile() via arg vector.
|
|
*/
|
|
static int
|
|
binfile_vec( im_object *argv )
|
|
{
|
|
int width = *((int *) argv[2]);
|
|
int height = *((int *) argv[3]);
|
|
int bands = *((int *) argv[4]);
|
|
int offset = *((int *) argv[5]);
|
|
IMAGE *im;
|
|
|
|
if( !(im = im_binfile( argv[0], width, height, bands, offset )) )
|
|
return( -1 );
|
|
|
|
if( im_copy( im, argv[1] ) ||
|
|
im_add_close_callback( argv[1],
|
|
(im_callback_fn) im_close, im, NULL ) ) {
|
|
im_close( im );
|
|
return( -1 );
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Description of im_binfile.
|
|
*/
|
|
static im_function binfile_desc = {
|
|
"im_binfile", /* Name */
|
|
"open a headerless binary file",/* Description */
|
|
0, /* Flags */
|
|
binfile_vec, /* Dispatch function */
|
|
IM_NUMBER( binfile_args ), /* Size of arg list */
|
|
binfile_args /* Arg list */
|
|
};
|
|
|
|
/* Package up iofuncs functions.
|
|
*/
|
|
static im_function *iofuncs_list[] = {
|
|
&binfile_desc,
|
|
&cache_desc,
|
|
&guess_prefix_desc,
|
|
&guess_libdir_desc,
|
|
&header_get_type_desc,
|
|
&header_int_desc,
|
|
&header_double_desc,
|
|
&header_string_desc,
|
|
&version_desc,
|
|
&version_string_desc
|
|
};
|
|
|
|
/* Package of io functions.
|
|
*/
|
|
static im_package im__iofuncs = {
|
|
"iofuncs",
|
|
IM_NUMBER( iofuncs_list ),
|
|
iofuncs_list
|
|
};
|
|
|
|
/* List of built-in VIPS packages.
|
|
*/
|
|
static im_package *built_in[] = {
|
|
&im__arithmetic,
|
|
&im__boolean,
|
|
#ifdef WITH_CIMG
|
|
&im__cimg,
|
|
#endif /*WITH_CIMG*/
|
|
&im__colour,
|
|
&im__conversion,
|
|
&im__convolution,
|
|
&im__format,
|
|
&im__freq_filt,
|
|
&im__histograms_lut,
|
|
&im__inplace,
|
|
&im__iofuncs,
|
|
&im__matrix,
|
|
&im__morphology,
|
|
&im__mosaicing,
|
|
&im__other,
|
|
&im__relational,
|
|
&im__resample,
|
|
&im__video
|
|
};
|
|
/* How we represent a loaded plugin.
|
|
*/
|
|
typedef struct _Plugin {
|
|
GModule *module; /* As loaded by g_module_open() */
|
|
char *name; /* Name we loaded */
|
|
im_package *pack; /* Package table */
|
|
} Plugin;
|
|
|
|
/* List of loaded plugins.
|
|
*/
|
|
static GSList *plugin_list = NULL;
|
|
|
|
/* Free a plugin.
|
|
*/
|
|
static int
|
|
plugin_free( Plugin *plug )
|
|
{
|
|
char *name = plug->name ? plug->name : "<unknown>";
|
|
|
|
if( plug->module ) {
|
|
if( !g_module_close( plug->module ) ) {
|
|
im_error( "plugin",
|
|
_( "unable to close plugin \"%s\"" ), name );
|
|
im_error( "plugin", "%s", g_module_error() );
|
|
return( -1 );
|
|
}
|
|
|
|
plug->module = NULL;
|
|
}
|
|
IM_FREE( plug->name );
|
|
plug->pack = NULL;
|
|
im_free( plug );
|
|
|
|
plugin_list = g_slist_remove( plugin_list, plug );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Load a plugin.
|
|
*/
|
|
im_package *
|
|
im_load_plugin( const char *name )
|
|
{
|
|
Plugin *plug;
|
|
|
|
if( !g_module_supported() ) {
|
|
im_error( "plugin",
|
|
"%s", _( "plugins not supported on this platform" ) );
|
|
return( NULL );
|
|
}
|
|
|
|
/* Build a new plugin.
|
|
*/
|
|
if( !(plug = IM_NEW( NULL, Plugin )) )
|
|
return( NULL );
|
|
plug->module = NULL;
|
|
plug->name = NULL;
|
|
plug->pack = NULL;
|
|
plugin_list = g_slist_prepend( plugin_list, plug );
|
|
|
|
/* Attach name.
|
|
*/
|
|
if( !(plug->name = im_strdup( NULL, name )) ) {
|
|
plugin_free( plug );
|
|
return( NULL );
|
|
}
|
|
|
|
/* Open library.
|
|
*/
|
|
if( !(plug->module = g_module_open( name, 0 )) ) {
|
|
im_error( "plugin", _( "unable to open plugin \"%s\"" ), name );
|
|
im_error( "plugin", "%s", g_module_error() );
|
|
plugin_free( plug );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
/* Find package.
|
|
*/
|
|
/* Bizarre double-cast stops a bogus gcc 4.1 compiler warning.
|
|
*/
|
|
if( !g_module_symbol( plug->module,
|
|
"package_table", (gpointer *) ((void *) &plug->pack) ) ) {
|
|
im_error( "plugin",
|
|
_( "unable to find symbol \"package_table\" "
|
|
"in plugin \"%s\"" ), name );
|
|
im_error( "plugin", "%s", g_module_error() );
|
|
plugin_free( plug );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
/* Sanity check.
|
|
*/
|
|
if( !plug->pack->name || plug->pack->nfuncs < 0 ||
|
|
plug->pack->nfuncs > 10000 ) {
|
|
im_error( "plugin",
|
|
_( "corrupted package table in plugin \"%s\"" ), name );
|
|
plugin_free( plug );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf( "added package \"%s\" ...\n", plug->pack->name );
|
|
#endif /*DEBUG*/
|
|
|
|
return( plug->pack );
|
|
}
|
|
|
|
/* Load all plugins in a directory ... look for '.plg' suffix. Error if we had
|
|
* any probs.
|
|
*/
|
|
int
|
|
im_load_plugins( const char *fmt, ... )
|
|
{
|
|
va_list ap;
|
|
char dir_name[PATH_MAX];
|
|
GDir *dir;
|
|
const char *name;
|
|
int result;
|
|
|
|
/* Silently succeed if we can't do modules.
|
|
*/
|
|
if( !g_module_supported() )
|
|
return( 0 );
|
|
|
|
va_start( ap, fmt );
|
|
(void) im_vsnprintf( dir_name, PATH_MAX - 1, fmt, ap );
|
|
va_end( ap );
|
|
|
|
if( !(dir = g_dir_open( dir_name, 0, NULL )) )
|
|
/* Silent success for dir not there.
|
|
*/
|
|
return( 0 );
|
|
|
|
result = 0;
|
|
while( (name = g_dir_read_name( dir )) )
|
|
if( im_ispostfix( name, ".plg" ) ) {
|
|
char path[PATH_MAX];
|
|
|
|
im_snprintf( path, PATH_MAX - 1,
|
|
"%s" G_DIR_SEPARATOR_S "%s", dir_name, name );
|
|
if( !im_load_plugin( path ) )
|
|
result = -1;
|
|
}
|
|
g_dir_close( dir );
|
|
|
|
return( result );
|
|
}
|
|
|
|
/* Close all loaded plugins.
|
|
*/
|
|
int
|
|
im_close_plugins( void )
|
|
{
|
|
while( plugin_list )
|
|
if( plugin_free( (Plugin *) plugin_list->data ) )
|
|
return( -1 );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Apply a user-function to a plugin package.
|
|
*/
|
|
static void *
|
|
apply_plugin( Plugin *plug, VSListMap2Fn fn, void *a )
|
|
{
|
|
if( !plug->pack )
|
|
return( NULL );
|
|
else
|
|
return( fn( plug->pack, a, NULL ) );
|
|
}
|
|
|
|
/* Map a function over all packages. Map over plugins first, to allow
|
|
* overriding of VIPS functions.
|
|
*/
|
|
void *
|
|
im_map_packages( VSListMap2Fn fn, void *a )
|
|
{
|
|
void *r = im_slist_map2( plugin_list,
|
|
(VSListMap2Fn) apply_plugin, (void *) fn, a );
|
|
int i;
|
|
|
|
/* If not there, try main VIPS package list.
|
|
*/
|
|
if( !r )
|
|
for( i = 0; i < IM_NUMBER( built_in ); i++ )
|
|
if( (r = fn( built_in[i], a, NULL )) )
|
|
return( r );
|
|
|
|
return( r );
|
|
}
|
|
|
|
/* Search a package for a function.
|
|
*/
|
|
static im_function *
|
|
search_package( im_package *pack, const char *name )
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < pack->nfuncs; i++ )
|
|
if( strcmp( pack->table[i]->name, name ) == 0 )
|
|
return( pack->table[i] );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
/* Search all packages for a function.
|
|
*/
|
|
im_function *
|
|
im_find_function( const char *name )
|
|
{
|
|
im_function *fn = im_map_packages(
|
|
(VSListMap2Fn) search_package, (void *) name );
|
|
|
|
if( !fn ) {
|
|
im_error( "im_find_function", _( "\"%s\" not found" ), name );
|
|
return( NULL );
|
|
}
|
|
|
|
return( fn );
|
|
}
|
|
|
|
/* Test for package is of name.
|
|
*/
|
|
static im_package *
|
|
package_name( im_package *pack, const char *name )
|
|
{
|
|
if( strcmp( pack->name, name ) == 0 )
|
|
return( pack );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
/* Find a package.
|
|
*/
|
|
im_package *
|
|
im_find_package( const char *name )
|
|
{
|
|
im_package *pack = im_map_packages(
|
|
(VSListMap2Fn) package_name, (void *) name );
|
|
|
|
if( !pack ) {
|
|
im_error( "im_find_package", _( "\"%s\" not found" ), name );
|
|
return( NULL );
|
|
}
|
|
|
|
return( pack );
|
|
}
|
|
|
|
/* Test for package contains a function.
|
|
*/
|
|
static im_package *
|
|
package_function( im_package *pack, const char *name )
|
|
{
|
|
if( search_package( pack, name ) )
|
|
return( pack );
|
|
else
|
|
return( NULL );
|
|
}
|
|
|
|
/* Find a function's package by name.
|
|
*/
|
|
im_package *
|
|
im_package_of_function( const char *name )
|
|
{
|
|
im_package *pack = im_map_packages(
|
|
(VSListMap2Fn) package_function, (void *) name );
|
|
|
|
if( !pack ) {
|
|
im_error( "im_package_of_function",
|
|
_( "\"%s\" not found" ), name );
|
|
return( NULL );
|
|
}
|
|
|
|
return( pack );
|
|
}
|
|
|
|
/* Free any store we allocated for the argument list.
|
|
*/
|
|
int
|
|
im_free_vargv( im_function *fn, im_object *vargv )
|
|
{
|
|
int i;
|
|
int vargc = fn->argc;
|
|
|
|
/* Free all elements.
|
|
*/
|
|
for( i = 0; i < vargc; i++ )
|
|
if( vargv[i] ) {
|
|
/* If there is local storage, free it.
|
|
*/
|
|
if( fn->argv[i].desc->size != 0 )
|
|
im_free( vargv[i] );
|
|
|
|
/* NULL out pointer.
|
|
*/
|
|
vargv[i] = NULL;
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Allocate any local store the args will need; NULL out all others.
|
|
*/
|
|
int
|
|
im_allocate_vargv( im_function *fn, im_object *vargv )
|
|
{
|
|
int i;
|
|
int vargc = fn->argc;
|
|
|
|
/* NULL out all pointers.
|
|
*/
|
|
for( i = 0; i < vargc; i++ )
|
|
vargv[i] = NULL;
|
|
|
|
/* Allocate any space we will need.
|
|
*/
|
|
for( i = 0; i < vargc; i++ ) {
|
|
int sz = fn->argv[i].desc->size;
|
|
|
|
if( sz != 0 )
|
|
if( !(vargv[i] = im_malloc( NULL, sz )) ) {
|
|
/* Free anything we did allocate.
|
|
*/
|
|
(void) im_free_vargv( fn, vargv );
|
|
return( -1 );
|
|
}
|
|
|
|
/* Zero memory.
|
|
*/
|
|
memset( vargv[i], 0, sz );
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Destroy the objects in the arg list.
|
|
*/
|
|
static int
|
|
destroy_args( im_function *fn, im_object *vargv )
|
|
{
|
|
int i;
|
|
int vargc = fn->argc;
|
|
|
|
/* Destroy all elements with destroy functions.
|
|
*/
|
|
for( i = 0; i < vargc; i++ )
|
|
if( vargv[i] )
|
|
/* If there's a destroy function for this type,
|
|
* trigger it.
|
|
*/
|
|
if( fn->argv[i].desc->dest &&
|
|
fn->argv[i].desc->dest( vargv[i] ) )
|
|
return( -1 );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Init an im_object array from a set of command-line arguments.
|
|
*/
|
|
static int
|
|
build_args( im_function *fn, im_object *vargv, int argc, char **argv )
|
|
{
|
|
im_arg_desc *arg = fn->argv;
|
|
int vargc = fn->argc;
|
|
char *str;
|
|
int i, j;
|
|
|
|
/* Loop, constructing each im_arg.
|
|
*/
|
|
for( i = 0, j = 0; i < vargc; i++ ) {
|
|
/* Find type for this arg.
|
|
*/
|
|
im_type_desc *type = arg[i].desc;
|
|
|
|
/* Do we need to use up a command line argument?
|
|
*/
|
|
if( type->flags & IM_TYPE_ARG ) {
|
|
if( !argv[j] ) {
|
|
im_error( "im_run_command",
|
|
"%s", _( "too few arguments" ) );
|
|
return( -1 );
|
|
}
|
|
str = argv[j++];
|
|
|
|
/* Init object.
|
|
*/
|
|
if( type->init && type->init( &vargv[i], str ) )
|
|
return( -1 );
|
|
}
|
|
else {
|
|
/* Init object with no arg.
|
|
*/
|
|
if( type->init && type->init( &vargv[i], "no arg" ) )
|
|
return( -1 );
|
|
}
|
|
}
|
|
|
|
/* Have we used up all the command-line args?
|
|
*/
|
|
if( argv[j] ) {
|
|
im_error( "im_run_command", "%s", _( "too many arguments" ) );
|
|
return( -1 );
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Free a region, but return 0 so we can be used as a close callback.
|
|
*/
|
|
static int
|
|
region_free( REGION *reg )
|
|
{
|
|
im_region_free( reg );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Make a region on sub, closed by callback on main.
|
|
*/
|
|
static int
|
|
region_local_image( IMAGE *main, IMAGE *sub )
|
|
{
|
|
REGION *reg;
|
|
|
|
if( !(reg = im_region_create( sub )) )
|
|
return( -1 );
|
|
if( im_add_close_callback( main,
|
|
(im_callback_fn) region_free, reg, NULL ) ) {
|
|
im_region_free( reg );
|
|
|
|
return( -1 );
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* vargv[i] is an output image on a PIO function ... make all input images
|
|
* depend on it.
|
|
*/
|
|
static int
|
|
note_dependencies( im_function *fn, im_object *vargv, int i )
|
|
{
|
|
int j;
|
|
|
|
for( j = 0; j < fn->argc; j++ ) {
|
|
im_type_desc *type = fn->argv[j].desc;
|
|
|
|
if( !(type->flags & IM_TYPE_OUTPUT) ) {
|
|
if( strcmp( type->type, IM_TYPE_IMAGE ) == 0 ) {
|
|
if( region_local_image( vargv[i], vargv[j] ) )
|
|
return( -1 );
|
|
}
|
|
else if( strcmp( type->type, IM_TYPE_IMAGEVEC ) == 0 ) {
|
|
im_imagevec_object *iv = vargv[j];
|
|
int k;
|
|
|
|
for( k = 0; k < iv->n; k++ )
|
|
if( region_local_image( vargv[i],
|
|
iv->vec[k] ) )
|
|
return( -1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Call all defined print functions.
|
|
*/
|
|
static int
|
|
print_args( im_function *fn, im_object *vargv )
|
|
{
|
|
int i;
|
|
int vargc = fn->argc;
|
|
|
|
/* Print all elements.
|
|
*/
|
|
for( i = 0; i < vargc; i++ )
|
|
if( fn->argv[i].print && vargv[i] )
|
|
if( fn->argv[i].print( vargv[i] ) )
|
|
return( -1 );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Add to the hist of all output images.
|
|
*/
|
|
static int
|
|
add_hist( im_function *fn, im_object *vargv, int argc, char **argv )
|
|
{
|
|
int i;
|
|
int vargc = fn->argc;
|
|
|
|
/* Search for output images.
|
|
*/
|
|
for( i = 0; i < vargc; i++ )
|
|
if( strcmp( fn->argv[i].desc->type, IM_TYPE_IMAGE ) == 0 &&
|
|
(fn->argv[i].desc->flags & IM_TYPE_OUTPUT) )
|
|
if( im_updatehist( vargv[i], fn->name, argc, argv ) )
|
|
return( -1 );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Call a VIPS function.
|
|
*/
|
|
static int
|
|
dispatch_function( im_function *fn, im_object *vargv, int argc, char **argv )
|
|
{
|
|
int i;
|
|
|
|
/* Init memory from command line arguments.
|
|
*/
|
|
if( build_args( fn, vargv, argc, argv ) )
|
|
return( -1 );
|
|
|
|
/* If this is a PIO function, we need to make sure that we close
|
|
* the input images after the output images, since the output image
|
|
* may include delayed image conversion filters which will not run
|
|
* until the output is closed.
|
|
*
|
|
* Do this by:
|
|
* - for each output image
|
|
* - for each input image
|
|
* - create a region on the input, closed by a
|
|
* close callback on the output image
|
|
*/
|
|
if( fn->flags & IM_FN_PIO )
|
|
for( i = 0; i < fn->argc; i++ ) {
|
|
im_type_desc *type = fn->argv[i].desc;
|
|
|
|
if( type->flags & IM_TYPE_OUTPUT &&
|
|
strcmp( type->type, IM_TYPE_IMAGE ) == 0 )
|
|
if( note_dependencies( fn, vargv, i ) )
|
|
return( -1 );
|
|
}
|
|
|
|
/* Call function.
|
|
*/
|
|
if( fn->disp( vargv ) )
|
|
return( -1 );
|
|
|
|
/* Print output.
|
|
*/
|
|
if( print_args( fn, vargv ) )
|
|
return( -1 );
|
|
|
|
/* Add to history of all output images.
|
|
*/
|
|
if( add_hist( fn, vargv, argc, argv ) )
|
|
return( -1 );
|
|
|
|
/* All ok!
|
|
*/
|
|
return( 0 );
|
|
}
|
|
|
|
/* Run a command.
|
|
*/
|
|
int
|
|
im_run_command( char *name, int argc, char **argv )
|
|
{
|
|
static im_object object_array[IM_MAX_ARGS];
|
|
im_object *vargv = object_array;
|
|
im_function *fn;
|
|
|
|
/* Search packages for a matching function.
|
|
*/
|
|
if( !(fn = im_find_function( name )) )
|
|
return( -1 );
|
|
|
|
/* Allocate space for arguments.
|
|
*/
|
|
if( im_allocate_vargv( fn, vargv ) )
|
|
return( -1 );
|
|
|
|
/* Call it.
|
|
*/
|
|
if( dispatch_function( fn, vargv, argc, argv ) ) {
|
|
destroy_args( fn, vargv );
|
|
im_free_vargv( fn, vargv );
|
|
return( -1 );
|
|
}
|
|
|
|
/* Clean up and exit.
|
|
*/
|
|
if( destroy_args( fn, vargv ) )
|
|
return( -1 );
|
|
im_free_vargv( fn, vargv );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Return the version string from configure.in
|
|
*/
|
|
const char *
|
|
im_version_string( void )
|
|
{
|
|
return( IM_VERSION_STRING );
|
|
}
|
|
|
|
/* Return major/minor/micro release numbers.
|
|
*/
|
|
int
|
|
im_version( int flag )
|
|
{
|
|
switch( flag ) {
|
|
case 0:
|
|
return( IM_MAJOR_VERSION );
|
|
|
|
case 1:
|
|
return( IM_MINOR_VERSION );
|
|
|
|
case 2:
|
|
return( IM_MICRO_VERSION );
|
|
|
|
default:
|
|
im_error( "im_version", "%s", _( "flag not 0,1,2" ) );
|
|
return( -1 );
|
|
}
|
|
}
|