904 lines
21 KiB
C
904 lines
21 KiB
C
/* VIPS universal main program.
|
|
*
|
|
* J. Cupitt, 8/4/93.
|
|
* 12/5/06
|
|
* - use GOption. g_*_prgname()
|
|
* 16/7/06
|
|
* - hmm, was broken for function name as argv1 case
|
|
* 11/7/06
|
|
* - add "all" option to -l
|
|
* 14/7/06
|
|
* - ignore "--" arguments.
|
|
* 2/9/06
|
|
* - do less init ... im_init_world() does more now
|
|
* 18/8/06
|
|
* - use IM_EXEEXT
|
|
* 16/10/06
|
|
* - add --version
|
|
* 17/10/06
|
|
* - add --swig
|
|
* - cleanups
|
|
* - remove --swig again, sigh
|
|
* - add throw() decls to C++ to help SWIG
|
|
* 14/1/07
|
|
* - add --list packages
|
|
* 26/2/07
|
|
* - add input *VEC arg types to C++ binding
|
|
* 17/8/08
|
|
* - add --list formats
|
|
* 29/11/08
|
|
* - add --list interpolators
|
|
* 9/2/09
|
|
* - and now we just have --list packages/classes/package-name
|
|
* 13/11/09
|
|
* - drop _f postfixes, drop many postfixes
|
|
* 24/6/10
|
|
* - less chatty error messages
|
|
* - oops, don't rename "copy_set" as "copy_"
|
|
* 6/2/12
|
|
* - long arg names in decls to help SWIG
|
|
* - don't wrap im_remainderconst_vec()
|
|
* 31/12/12
|
|
* - parse options in two passes (thanks Haida)
|
|
* 26/11/17
|
|
* - remove throw() decls, they are now deprecated everywhere
|
|
* 18/6/20 kleisauke
|
|
* - avoid using vips7 symbols
|
|
* - remove deprecated vips7 C++ generator
|
|
* 1/11/22
|
|
* - add "-c" flag
|
|
*/
|
|
|
|
/*
|
|
|
|
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 DEBUG
|
|
#define DEBUG_FATAL
|
|
*/
|
|
|
|
/* Need to disable these sometimes.
|
|
#undef DEBUG_FATAL
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif /*HAVE_CONFIG_H*/
|
|
#include <glib/gi18n.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <locale.h>
|
|
|
|
#define VIPS_DISABLE_DEPRECATION_WARNINGS
|
|
#include <vips/vips.h>
|
|
#include <vips/internal.h>
|
|
|
|
#if ENABLE_DEPRECATED
|
|
#include <vips/vips7compat.h>
|
|
#endif
|
|
|
|
static char *main_option_plugin = NULL;
|
|
static gboolean main_option_version;
|
|
|
|
static void *
|
|
list_class( GType type, void *user_data )
|
|
{
|
|
VipsObjectClass *class = VIPS_OBJECT_CLASS( g_type_class_ref( type ) );
|
|
int depth = vips_type_depth( type );
|
|
|
|
int i;
|
|
|
|
if( class->deprecated )
|
|
return( NULL );
|
|
if( VIPS_IS_OPERATION_CLASS( class ) &&
|
|
(VIPS_OPERATION_CLASS( class )->flags &
|
|
VIPS_OPERATION_DEPRECATED) )
|
|
return( NULL );
|
|
|
|
for( i = 0; i < depth * 2; i++ )
|
|
printf( " " );
|
|
vips_object_print_summary_class(
|
|
VIPS_OBJECT_CLASS( g_type_class_ref( type ) ) );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
static void *
|
|
test_class_name( GType type, void *data )
|
|
{
|
|
const char *name = (const char *) data;
|
|
|
|
VipsObjectClass *class;
|
|
|
|
/* Test the classname too.
|
|
*/
|
|
if( (class = VIPS_OBJECT_CLASS( g_type_class_ref( type ) )) &&
|
|
strcmp( class->nickname, name ) == 0 )
|
|
return( class );
|
|
if( strcmp( G_OBJECT_CLASS_NAME( class ), name ) == 0 )
|
|
return( class );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
static gboolean
|
|
parse_main_option_list( const gchar *option_name, const gchar *value,
|
|
gpointer data, GError **error )
|
|
{
|
|
VipsObjectClass *class;
|
|
|
|
if( value &&
|
|
(class = (VipsObjectClass *) vips_type_map_all(
|
|
g_type_from_name( "VipsObject" ),
|
|
test_class_name, (void *) value )) ) {
|
|
vips_type_map_all( G_TYPE_FROM_CLASS( class ),
|
|
list_class, NULL );
|
|
}
|
|
else if( value ) {
|
|
vips_error( g_get_prgname(),
|
|
_( "'%s' is not the name of a vips class" ), value );
|
|
vips_error_g( error );
|
|
|
|
return( FALSE );
|
|
}
|
|
else {
|
|
vips_type_map_all( g_type_from_name( "VipsOperation" ),
|
|
list_class, NULL );
|
|
}
|
|
|
|
exit( 0 );
|
|
}
|
|
|
|
static void *
|
|
list_operation( GType type, void *user_data )
|
|
{
|
|
VipsObjectClass *class = VIPS_OBJECT_CLASS( g_type_class_ref( type ) );
|
|
|
|
if( G_TYPE_IS_ABSTRACT( type ) )
|
|
return( NULL );
|
|
if( class->deprecated )
|
|
return( NULL );
|
|
if( VIPS_OPERATION_CLASS( class )->flags & VIPS_OPERATION_DEPRECATED )
|
|
return( NULL );
|
|
|
|
/* Complete on class names as well as nicknames -- "crop", for
|
|
* example, is a class name.
|
|
*/
|
|
printf( "%s\n", class->nickname );
|
|
printf( "%s\n", G_OBJECT_CLASS_NAME( class ) );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
static void *
|
|
list_operation_arg( VipsObjectClass *object_class,
|
|
GParamSpec *pspec, VipsArgumentClass *argument_class,
|
|
void *_data1, void *_data2 )
|
|
{
|
|
GType type = G_PARAM_SPEC_VALUE_TYPE( pspec );
|
|
|
|
if( !(argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) ||
|
|
(argument_class->flags & VIPS_ARGUMENT_DEPRECATED) )
|
|
return( NULL );
|
|
|
|
/* We don't try to complete options, though maybe we should.
|
|
*/
|
|
if( !(argument_class->flags & VIPS_ARGUMENT_REQUIRED) )
|
|
return( NULL );
|
|
|
|
/* These are the pspecs that vips uses that have interesting values.
|
|
*/
|
|
if( G_IS_PARAM_SPEC_ENUM( pspec ) ) {
|
|
GTypeClass *class = g_type_class_ref( type );
|
|
|
|
GEnumClass *genum;
|
|
int i;
|
|
|
|
/* Should be impossible, no need to warn.
|
|
*/
|
|
if( !class )
|
|
return( NULL );
|
|
|
|
genum = G_ENUM_CLASS( class );
|
|
|
|
printf( "word:" );
|
|
|
|
/* -1 since we always have a "last" member.
|
|
*/
|
|
for( i = 0; i < genum->n_values - 1; i++ ) {
|
|
if( i > 0 )
|
|
printf( "|" );
|
|
printf( "%s", genum->values[i].value_nick );
|
|
}
|
|
|
|
printf( "\n" );
|
|
}
|
|
else if( G_IS_PARAM_SPEC_BOOLEAN( pspec ) )
|
|
printf( "word:true|false\n" );
|
|
else if( G_IS_PARAM_SPEC_DOUBLE( pspec ) ) {
|
|
GParamSpecDouble *pspec_double = (GParamSpecDouble *) pspec;
|
|
|
|
printf( "word:%g\n", pspec_double->default_value );
|
|
}
|
|
else if( G_IS_PARAM_SPEC_INT( pspec ) ) {
|
|
GParamSpecInt *pspec_int = (GParamSpecInt *) pspec;
|
|
|
|
printf( "word:%d\n", pspec_int->default_value );
|
|
}
|
|
else if( G_IS_PARAM_SPEC_OBJECT( pspec ) )
|
|
/* Eg. an image input or output.
|
|
*/
|
|
printf( "file\n" );
|
|
else
|
|
/* We can offer no useful suggestion for eg. array_int etc.
|
|
*/
|
|
printf( "none\n" );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
static gboolean
|
|
parse_main_option_completion( const gchar *option_name, const gchar *value,
|
|
gpointer data, GError **error )
|
|
{
|
|
VipsObjectClass *class;
|
|
|
|
if( value &&
|
|
(class = (VipsObjectClass *) vips_type_map_all(
|
|
g_type_from_name( "VipsOperation" ),
|
|
test_class_name, (void *) value )) )
|
|
vips_argument_class_map( class,
|
|
(VipsArgumentClassMapFn) list_operation_arg,
|
|
NULL, NULL );
|
|
else if( value ) {
|
|
vips_error( g_get_prgname(),
|
|
_( "'%s' is not the name of a vips operation" ),
|
|
value );
|
|
vips_error_g( error );
|
|
|
|
return( FALSE );
|
|
}
|
|
else {
|
|
vips_type_map_all( g_type_from_name( "VipsOperation" ),
|
|
list_operation, NULL );
|
|
}
|
|
|
|
exit( 0 );
|
|
}
|
|
|
|
static GOptionEntry main_option[] = {
|
|
{ "list", 'l', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
|
|
(GOptionArgFunc) parse_main_option_list,
|
|
N_( "list objects" ),
|
|
N_( "BASE-NAME" ) },
|
|
{ "plugin", 'p', 0, G_OPTION_ARG_FILENAME, &main_option_plugin,
|
|
N_( "load PLUGIN" ),
|
|
N_( "PLUGIN" ) },
|
|
{ "version", 'v', 0, G_OPTION_ARG_NONE, &main_option_version,
|
|
N_( "print version" ), NULL },
|
|
{ "completion", 'c', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
|
|
(GOptionArgFunc) parse_main_option_completion,
|
|
N_( "print completions" ),
|
|
N_( "BASE-NAME" ) },
|
|
{ NULL }
|
|
};
|
|
|
|
#if ENABLE_DEPRECATED
|
|
typedef void *(*map_name_fn)( im_function * );
|
|
|
|
/* Loop over a package.
|
|
*/
|
|
static void *
|
|
map_package( im_package *pack, map_name_fn fn )
|
|
{
|
|
int i;
|
|
void *result;
|
|
|
|
for( i = 0; i < pack->nfuncs; i++ )
|
|
if( (result = fn( pack->table[i] )) )
|
|
return( result );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
/* Apply a function to a vips operation, or map over a package of operations.
|
|
*/
|
|
static void *
|
|
map_name( const char *name, map_name_fn fn )
|
|
{
|
|
im_package *pack;
|
|
im_function *func;
|
|
|
|
if( !name || strcmp( name, "all" ) == 0 )
|
|
/* Do all packages.
|
|
*/
|
|
im_map_packages( (VSListMap2Fn) map_package, fn );
|
|
else if( (pack = im_find_package( name )) )
|
|
/* Do one package.
|
|
*/
|
|
map_package( pack, fn );
|
|
else if( (func = im_find_function( name )) )
|
|
/* Do a single function.
|
|
*/
|
|
fn( func );
|
|
else {
|
|
vips_error( "map_name",
|
|
_( "no package or function \"%s\"" ), name );
|
|
return( fn );
|
|
}
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
static void *
|
|
list_package( im_package *pack )
|
|
{
|
|
printf( "%-20s - %d operations\n", pack->name, pack->nfuncs );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
static void *
|
|
list_function( im_function *func )
|
|
{
|
|
printf( "%-20s - %s\n", func->name, _( func->desc ) );
|
|
|
|
return( NULL );
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
print_list( int argc, char **argv )
|
|
{
|
|
#if ENABLE_DEPRECATED
|
|
if( !argv[0] || strcmp( argv[0], "packages" ) == 0 )
|
|
im_map_packages( (VSListMap2Fn) list_package, NULL );
|
|
else if( strcmp( argv[0], "classes" ) == 0 )
|
|
#else
|
|
if( !argv[0] || strcmp( argv[0], "classes" ) == 0 )
|
|
#endif
|
|
vips_type_map_all( g_type_from_name( "VipsObject" ),
|
|
list_class, NULL );
|
|
else if( g_type_from_name( argv[0] ) &&
|
|
g_type_is_a( g_type_from_name( argv[0] ), VIPS_TYPE_OBJECT ) ) {
|
|
vips_type_map_all( g_type_from_name( argv[0] ),
|
|
list_class, NULL );
|
|
}
|
|
else {
|
|
#if ENABLE_DEPRECATED
|
|
if( map_name( argv[0], list_function ) )
|
|
vips_error_exit( "unknown package \"%s\"", argv[0] );
|
|
#else
|
|
vips_error_exit( "unknown operation \"%s\"", argv[0] );
|
|
#endif
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
#if ENABLE_DEPRECATED
|
|
/* Print "ln -s" lines for this package.
|
|
*/
|
|
static void *
|
|
print_links_package( im_package *pack )
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < pack->nfuncs; i++ )
|
|
printf( "rm -f %s" IM_EXEEXT "; "
|
|
"ln -s vips" IM_EXEEXT " %s" IM_EXEEXT "\n",
|
|
pack->table[i]->name, pack->table[i]->name );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
/* Print "ln -s" lines for this package.
|
|
*/
|
|
static int
|
|
print_links( int argc, char **argv )
|
|
{
|
|
im_map_packages( (VSListMap2Fn) print_links_package, NULL );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Does a function have any printing output?
|
|
*/
|
|
static int
|
|
has_print( im_function *fn )
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < fn->argc; i++ )
|
|
if( fn->argv[i].print )
|
|
return( -1 );
|
|
|
|
return( 0 );
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
isvips( const char *name )
|
|
{
|
|
/* If we're running uninstalled we get the lt- prefix.
|
|
*/
|
|
if( vips_isprefix( "lt-", name ) )
|
|
name += 3;
|
|
|
|
return( vips_isprefix( "vips", name ) );
|
|
}
|
|
|
|
#if ENABLE_DEPRECATED
|
|
/* Print a usage string from an im_function descriptor.
|
|
*/
|
|
static void
|
|
usage( im_function *fn )
|
|
{
|
|
int i;
|
|
im_package *pack = im_package_of_function( fn->name );
|
|
|
|
/* Don't print the prgname if we're being run as a symlink.
|
|
*/
|
|
fprintf( stderr, "usage: " );
|
|
if( isvips( g_get_prgname() ) )
|
|
fprintf( stderr, "%s ", g_get_prgname() );
|
|
fprintf( stderr, "%s ", fn->name );
|
|
|
|
/* Print args requiring command-line input.
|
|
*/
|
|
for( i = 0; i < fn->argc; i++ )
|
|
if( fn->argv[i].desc->flags & IM_TYPE_ARG )
|
|
fprintf( stderr, "%s ", fn->argv[i].name );
|
|
|
|
/* Print types of command line args.
|
|
*/
|
|
fprintf( stderr, "\nwhere:\n" );
|
|
for( i = 0; i < fn->argc; i++ )
|
|
if( fn->argv[i].desc->flags & IM_TYPE_ARG )
|
|
fprintf( stderr, "\t%s is of type \"%s\"\n",
|
|
fn->argv[i].name, fn->argv[i].desc->type );
|
|
|
|
/* Print output print args.
|
|
*/
|
|
if( has_print( fn ) ) {
|
|
fprintf( stderr, "prints:\n" );
|
|
for( i = 0; i < fn->argc; i++ )
|
|
if( fn->argv[i].print )
|
|
fprintf( stderr, "\t%s of type \"%s\"\n",
|
|
fn->argv[i].name,
|
|
fn->argv[i].desc->type );
|
|
}
|
|
|
|
/* Print description of this function, and package it comes from.
|
|
*/
|
|
fprintf( stderr, "%s", _( fn->desc ) );
|
|
if( pack )
|
|
fprintf( stderr, ", from package \"%s\"", pack->name );
|
|
fprintf( stderr, "\n" );
|
|
|
|
/* Print any flags this function has.
|
|
*/
|
|
fprintf( stderr, "flags: " );
|
|
if( fn->flags & IM_FN_PIO )
|
|
fprintf( stderr, "(PIO function) " );
|
|
else
|
|
fprintf( stderr, "(WIO function) " );
|
|
if( fn->flags & IM_FN_TRANSFORM )
|
|
fprintf( stderr, "(coordinate transformer) " );
|
|
else
|
|
fprintf( stderr, "(no coordinate transformation) " );
|
|
if( fn->flags & IM_FN_PTOP )
|
|
fprintf( stderr, "(point-to-point operation) " );
|
|
else
|
|
fprintf( stderr, "(area operation) " );
|
|
if( fn->flags & IM_FN_NOCACHE )
|
|
fprintf( stderr, "(nocache operation) " );
|
|
else
|
|
fprintf( stderr, "(result can be cached) " );
|
|
|
|
fprintf( stderr, "\n" );
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
print_help( int argc, char **argv )
|
|
{
|
|
return( 0 );
|
|
}
|
|
|
|
/* All our built-in actions.
|
|
*/
|
|
|
|
typedef int (*Action)( int argc, char **argv );
|
|
|
|
typedef struct _ActionEntry {
|
|
char *name;
|
|
char *description;
|
|
GOptionEntry *group;
|
|
Action action;
|
|
} ActionEntry;
|
|
|
|
static GOptionEntry empty_options[] = {
|
|
{ NULL }
|
|
};
|
|
|
|
static ActionEntry actions[] = {
|
|
#if ENABLE_DEPRECATED
|
|
{ "list", N_( "list classes|packages|all|package-name|operation-name" ),
|
|
#else
|
|
{ "list", N_( "list classes|all|operation-name" ),
|
|
#endif
|
|
&empty_options[0], print_list },
|
|
#if ENABLE_DEPRECATED
|
|
{ "links", N_( "generate links for vips/bin" ),
|
|
&empty_options[0], print_links },
|
|
#endif
|
|
{ "help", N_( "list possible actions" ),
|
|
&empty_options[0], print_help },
|
|
};
|
|
|
|
static void
|
|
parse_options( GOptionContext *context, int *argc, char **argv )
|
|
{
|
|
char txt[1024];
|
|
VipsBuf buf = VIPS_BUF_STATIC( txt );
|
|
GError *error = NULL;
|
|
int i, j;
|
|
|
|
#ifdef DEBUG
|
|
printf( "parse_options:\n" );
|
|
for( i = 0; i < *argc; i++ )
|
|
printf( "%d) %s\n", i, argv[i] );
|
|
#endif /*DEBUG*/
|
|
|
|
vips_buf_appendf( &buf, "%7s - %s\n",
|
|
"OPER", _( "execute vips operation OPER" ) );
|
|
g_option_context_set_summary( context, vips_buf_all( &buf ) );
|
|
|
|
#ifdef G_OS_WIN32
|
|
if( !g_option_context_parse_strv( context, &argv, &error ) )
|
|
#else /*!G_OS_WIN32*/
|
|
if( !g_option_context_parse( context, argc, &argv, &error ) )
|
|
#endif /*G_OS_WIN32*/
|
|
{
|
|
if( error ) {
|
|
fprintf( stderr, "%s\n", error->message );
|
|
g_error_free( error );
|
|
}
|
|
|
|
vips_error_exit( NULL );
|
|
}
|
|
|
|
/* On Windows, argc will not have been updated by
|
|
* g_option_context_parse_strv().
|
|
*/
|
|
for( *argc = 0; argv[*argc]; (*argc)++ )
|
|
;
|
|
|
|
/* Remove any "--" argument. If one of our arguments is a negative
|
|
* number, the user will need to have added the "--" flag to stop
|
|
* GOption parsing. But "--" is still passed down to us and we need to
|
|
* ignore it.
|
|
*/
|
|
for( i = 1; i < *argc; i++ )
|
|
if( strcmp( argv[i], "--" ) == 0 ) {
|
|
for( j = i; j < *argc; j++ )
|
|
argv[j] = argv[j + 1];
|
|
|
|
*argc -= 1;
|
|
}
|
|
}
|
|
|
|
static GOptionGroup *
|
|
add_operation_group( GOptionContext *context, VipsOperation *user_data )
|
|
{
|
|
GOptionGroup *group;
|
|
|
|
group = g_option_group_new( "operation",
|
|
_( "Operation" ), _( "Operation help" ), user_data, NULL );
|
|
g_option_group_set_translation_domain( group, GETTEXT_PACKAGE );
|
|
g_option_context_add_group( context, group );
|
|
|
|
return( group );
|
|
}
|
|
|
|
/* VIPS universal main program.
|
|
*/
|
|
int
|
|
main( int argc, char **argv )
|
|
{
|
|
char *action;
|
|
GOptionContext *context;
|
|
GOptionGroup *main_group;
|
|
GOptionGroup *group;
|
|
VipsOperation *operation;
|
|
#if ENABLE_DEPRECATED
|
|
im_function *fn;
|
|
#endif
|
|
int i, j;
|
|
gboolean handled;
|
|
|
|
GError *error = NULL;
|
|
|
|
if( VIPS_INIT( argv[0] ) )
|
|
vips_error_exit( NULL );
|
|
|
|
#ifdef ENABLE_NLS
|
|
textdomain( GETTEXT_PACKAGE );
|
|
#endif /* ENABLE_NLS */
|
|
setlocale( LC_ALL, "" );
|
|
|
|
{
|
|
char *basename;
|
|
|
|
basename = g_path_get_basename( argv[0] );
|
|
g_set_prgname( basename );
|
|
g_free( basename );
|
|
}
|
|
|
|
/* On Windows, argv is ascii-only .. use this to get a utf-8 version of
|
|
* the args.
|
|
*/
|
|
#ifdef G_OS_WIN32
|
|
argv = g_win32_get_command_line();
|
|
#endif /*G_OS_WIN32*/
|
|
|
|
#ifdef DEBUG_FATAL
|
|
/* Set masks for debugging ... stop on any problem.
|
|
*/
|
|
g_log_set_always_fatal(
|
|
G_LOG_FLAG_RECURSION |
|
|
G_LOG_FLAG_FATAL |
|
|
G_LOG_LEVEL_ERROR |
|
|
G_LOG_LEVEL_CRITICAL |
|
|
G_LOG_LEVEL_WARNING );
|
|
#endif /*!DEBUG_FATAL*/
|
|
|
|
context = g_option_context_new( _( "[ACTION] [OPTIONS] [PARAMETERS] - "
|
|
"VIPS driver program" ) );
|
|
|
|
/* Add and parse the outermost options: the ones this program uses.
|
|
* For example, we need
|
|
* to be able to spot that in the case of "--plugin ./poop.plg" we
|
|
* must remove two args.
|
|
*/
|
|
main_group = g_option_group_new( NULL, NULL, NULL, NULL, NULL );
|
|
g_option_group_add_entries( main_group, main_option );
|
|
vips_add_option_entries( main_group );
|
|
g_option_group_set_translation_domain( main_group, GETTEXT_PACKAGE );
|
|
g_option_context_set_main_group( context, main_group );
|
|
|
|
/* We add more options later, for example as options to vips8
|
|
* operations. Ignore any unknown options in this first parse.
|
|
*/
|
|
g_option_context_set_ignore_unknown_options( context, TRUE );
|
|
|
|
/* "vips" with no arguments does "vips --help".
|
|
*/
|
|
if( argc == 1 ) {
|
|
char *help;
|
|
|
|
help = g_option_context_get_help( context, TRUE, NULL );
|
|
printf( "%s", help );
|
|
g_free( help );
|
|
|
|
exit( 0 );
|
|
}
|
|
|
|
/* Also disable help output: we want to be able to display full help
|
|
* in a second pass after all options have been created.
|
|
*/
|
|
g_option_context_set_help_enabled( context, FALSE );
|
|
|
|
#ifdef G_OS_WIN32
|
|
if( !g_option_context_parse_strv( context, &argv, &error ) )
|
|
#else /*!G_OS_WIN32*/
|
|
if( !g_option_context_parse( context, &argc, &argv, &error ) )
|
|
#endif /*G_OS_WIN32*/
|
|
{
|
|
if( error ) {
|
|
fprintf( stderr, "%s\n", error->message );
|
|
g_error_free( error );
|
|
}
|
|
|
|
vips_error_exit( NULL );
|
|
}
|
|
|
|
/* On Windows, argc will not have been updated by
|
|
* g_option_context_parse_strv().
|
|
*/
|
|
for( argc = 0; argv[argc]; argc++ )
|
|
;
|
|
|
|
if( main_option_plugin ) {
|
|
#if ENABLE_MODULES
|
|
#if ENABLE_DEPRECATED
|
|
if( !im_load_plugin( main_option_plugin ) )
|
|
vips_error_exit( NULL );
|
|
#else /*!ENABLE_DEPRECATED*/
|
|
GModule *module;
|
|
|
|
module = g_module_open( main_option_plugin, G_MODULE_BIND_LAZY );
|
|
if( !module ) {
|
|
vips_error_exit( _( "unable to load \"%s\" -- %s" ),
|
|
main_option_plugin, g_module_error() );
|
|
}
|
|
#endif /*ENABLE_DEPRECATED*/
|
|
#else /*!ENABLE_MODULES*/
|
|
g_warning( "%s", _( "plugin load disabled: "
|
|
"libvips built without modules support" ) );
|
|
#endif /*ENABLE_MODULES*/
|
|
}
|
|
|
|
if( main_option_version )
|
|
printf( "vips-%s\n", vips_version_string() );
|
|
|
|
/* Reenable help and unknown option detection ready for the second
|
|
* option parse.
|
|
*/
|
|
g_option_context_set_ignore_unknown_options( context, FALSE );
|
|
g_option_context_set_help_enabled( context, TRUE );
|
|
|
|
/* Try to find our action.
|
|
*/
|
|
handled = FALSE;
|
|
action = NULL;
|
|
|
|
/* Should we try to run the thing we are named as?
|
|
*/
|
|
if( !isvips( g_get_prgname() ) )
|
|
action = argv[0];
|
|
|
|
if( !action ) {
|
|
/* Look for the first non-option argument, if any, and make
|
|
* that our action. The parse above will have removed most of
|
|
* them, but --help (for example) could still remain.
|
|
*/
|
|
for( i = 1; i < argc; i++ )
|
|
if( argv[i][0] != '-' ) {
|
|
action = argv[i];
|
|
|
|
/* Remove the action from argv.
|
|
*/
|
|
for( j = i; j < argc; j++ )
|
|
argv[j] = argv[j + 1];
|
|
argc -= 1;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Could be one of our built-in actions.
|
|
*/
|
|
if( action )
|
|
for( i = 0; i < VIPS_NUMBER( actions ); i++ )
|
|
if( strcmp( action, actions[i].name ) == 0 ) {
|
|
group = add_operation_group( context, NULL );
|
|
g_option_group_add_entries( group,
|
|
actions[i].group );
|
|
parse_options( context, &argc, argv );
|
|
|
|
if( actions[i].action( argc - 1, argv + 1 ) )
|
|
vips_error_exit( "%s", action );
|
|
|
|
handled = TRUE;
|
|
break;
|
|
}
|
|
|
|
#if ENABLE_DEPRECATED
|
|
/* Could be a vips7 im_function. We need to test for vips7 first,
|
|
* since we don't want to use the vips7 compat wrappers in vips8
|
|
* unless we have to. They don't support all args types.
|
|
*/
|
|
if( action &&
|
|
!handled &&
|
|
(fn = im_find_function( action )) ) {
|
|
if( im_run_command( action, argc - 1, argv + 1 ) ) {
|
|
if( argc == 1 )
|
|
usage( fn );
|
|
else
|
|
vips_error_exit( NULL );
|
|
}
|
|
|
|
handled = TRUE;
|
|
}
|
|
|
|
/* im_find_function() set an error msg.
|
|
*/
|
|
if( action &&
|
|
!handled )
|
|
vips_error_clear();
|
|
#endif
|
|
|
|
/* Could be a vips8 VipsOperation.
|
|
*/
|
|
if( action &&
|
|
!handled &&
|
|
(operation = vips_operation_new( action )) ) {
|
|
group = add_operation_group( context, operation );
|
|
vips_call_options( group, operation );
|
|
parse_options( context, &argc, argv );
|
|
|
|
if( vips_call_argv( operation, argc - 1, argv + 1 ) ) {
|
|
if( argc == 1 )
|
|
vips_operation_class_print_usage(
|
|
VIPS_OPERATION_GET_CLASS( operation ) );
|
|
|
|
vips_object_unref_outputs( VIPS_OBJECT( operation ) );
|
|
g_object_unref( operation );
|
|
|
|
if( argc == 1 )
|
|
/* We don't exit with an error for something
|
|
* like "vips fitsload" failing, we use it to
|
|
* decide if an optional component has been
|
|
* configured. If we've been built without
|
|
* fits support, fitsload will fail to find
|
|
* the operation and we'll error with "unknown
|
|
* action" below.
|
|
*/
|
|
exit( 0 );
|
|
else
|
|
vips_error_exit( NULL );
|
|
}
|
|
|
|
vips_object_unref_outputs( VIPS_OBJECT( operation ) );
|
|
g_object_unref( operation );
|
|
|
|
handled = TRUE;
|
|
}
|
|
|
|
/* vips_operation_new() sets an error msg for unknown operation.
|
|
*/
|
|
if( action &&
|
|
!handled )
|
|
vips_error_clear();
|
|
|
|
if( action &&
|
|
!handled ) {
|
|
vips_error_exit( _( "unknown action \"%s\"" ), action );
|
|
}
|
|
|
|
/* Still not handled? We may not have called parse_options(), so
|
|
* --help args may not have been processed.
|
|
*/
|
|
if( !handled )
|
|
parse_options( context, &argc, argv );
|
|
|
|
g_option_context_free( context );
|
|
|
|
#ifdef G_OS_WIN32
|
|
g_strfreev( argv );
|
|
#endif /*G_OS_WIN32*/
|
|
|
|
vips_shutdown();
|
|
|
|
return( 0 );
|
|
}
|