/* Start up the world of vips. * * 7/1/04 JC * - 1st version * 7/6/05 * - g_type_init() too, so we can use gobject * 2/9/06 * - also set g_prg_name() and load plugins * 8/12/06 * - add liboil support * 5/2/07 * - stop a loop if we're called recursively during VIPS startup ... it * can happen if (for example) vips_guess_prefix() fails and tries to * i18n an error message (thanks Christian) * 8/6/07 * - just warn if plugins fail to load correctly: too annoying to have * VIPS refuse to start because of a dodgy plugin * 7/11/07 * - progress feedback option * 5/8/08 * - load plugins from libdir/vips-x.x * 5/10/09 * - gtkdoc comments * 14/3/10 * - init image and region before we start, we need all types to be fully * constructed before we go parallel * 18/9/16 * - call _setmaxstdio() on win32 * 4/8/17 * - hide warnings is VIPS_WARNING is set * 20/4/19 * - set the min stack, if we can * 17/9/21 * - don't use atexit for cleanup, it's too unreliable ... users should * call vips_shutdown explicitly if they want a clean exit, though a * dirty exit is fine */ /* 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 */ /* pthread_setattr_default_np() is a non-portable GNU extension. */ #define _GNU_SOURCE #ifdef HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ #include #ifdef HAVE_PTHREAD_DEFAULT_NP #include #endif /*HAVE_PTHREAD_DEFAULT_NP*/ #include #include #ifdef HAVE_SYS_PARAM_H #include #endif /*HAVE_SYS_PARAM_H*/ #ifdef HAVE_UNISTD_H #include #endif /*HAVE_UNISTD_H*/ #ifdef HAVE_DIRECT_H #include #endif /*HAVE_DIRECT_H*/ #include #include /* Disable deprecation warnings from gsf. There are loads, and still not * patched as of 12/2020. */ #ifdef HAVE_GSF #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #include #pragma GCC diagnostic pop #endif /*HAVE_GSF*/ #define VIPS_DISABLE_DEPRECATION_WARNINGS #include #include #include #include #if ENABLE_DEPRECATED #include #endif /* abort() on the first warning or error. */ int vips__fatal = 0; /* Use in various small places where we need a mutex and it's not worth * making a private one. */ GMutex *vips__global_lock = NULL; /* A debugging timer, zero at library init. */ GTimer *vips__global_timer = NULL; /* Keep a copy of the argv0 here. */ static char *vips__argv0 = NULL; /* Keep a copy of the last component of argv0 here. */ static char *vips__prgname = NULL; /* Leak check on exit. */ int vips__leak = 0; #ifdef DEBUG_LEAK /* Count pixels processed per image here. */ GQuark vips__image_pixels_quark = 0; #endif /*DEBUG_LEAK*/ static gint64 vips_pipe_read_limit = 1024 * 1024 * 1024; /** * vips_get_argv0: * * See also: VIPS_INIT(). * * Returns: (transfer none): a pointer to an internal copy of the * argv0 string passed to * VIPS_INIT(). Do not free this value */ const char * vips_get_argv0( void ) { return( vips__argv0 ); } /** * vips_get_prgname: * * Return the program name. This can be useful for the user tio see,. * * See also: VIPS_INIT(). * * Returns: (transfer none): a pointer to an internal copy of the program * name. Do not free this value */ const char * vips_get_prgname( void ) { const char *prgname; if( (prgname = g_get_prgname()) ) return( prgname ); else return( vips__prgname ); } /** * VIPS_INIT: * @ARGV0: name of application * * VIPS_INIT() starts up the world of VIPS. You should call this on * program startup before using any other VIPS operations. If you do not call * VIPS_INIT(), VIPS will call it for you when you use your first VIPS * operation, but it may not be able to get hold of @ARGV0 and VIPS may * therefore be unable to find its data files. It is much better to call * this macro yourself. * * @ARGV0 is used to help discover message catalogues if libvips has been * relocated. If you don't need a relocatable package, you can just pass `""` * and it'll be fine. * * Additionally, VIPS_INIT() can be run from any thread, but it must not be * called from more than one thread at the same time. This is much easier to * guarantee if you call it yourself. * * VIPS_INIT() is a macro, since it tries to check ABI compatibility * between the caller and the library. You can also call vips_init(), the * non-macro version, if macros are not available to you. * * You may call VIPS_INIT() many times and vips_shutdown() many times, but you * must not call VIPS_INIT() after vips_shutdown(). In other words, you cannot * stop and restart vips. * * Use the environment variable `VIPS_MIN_STACK_SIZE` to set the minimum stack * size. For example, `2m` for a minimum of two megabytes of stack. This can * be important for systems like musl where the default stack is very small. * * VIPS_INIT() does approximately the following: * * + checks that the libvips your program is expecting is * binary-compatible with the vips library you're running against * * + sets a minimum stack size, see above * * + initialises any libraries that VIPS is using, including GObject * and the threading system, if neccessary * * + guesses where the VIPS data files are and sets up * internationalisation --- see vips_guess_prefix() * * + creates the main vips types, including #VipsImage and friends * * + loads any plugins from $libdir/vips-x.y/, where x and y are the * major and minor version numbers for this VIPS. * * Example: * * |[ * int main (int argc, char **argv) * { * if (VIPS_INIT (argv[0])) * vips_error_exit ("unable to start VIPS"); * * vips_shutdown (); * * return 0; * } * ]| * * See also: vips_shutdown(), vips_add_option_entries(), vips_version(), * vips_guess_prefix(), vips_guess_libdir(). * * Returns: 0 on success, -1 otherwise */ #ifdef ENABLE_MODULES /* Load all plugins in a directory ... look for '.' or * '.plg' (deprecated) suffix. Error if we had any probs. */ static int vips_load_plugins( const char *fmt, ... ) { va_list ap; char dir_name[VIPS_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) vips_vsnprintf( dir_name, VIPS_PATH_MAX - 1, fmt, ap ); va_end( ap ); g_info( "searching \"%s\"", dir_name ); 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( vips_ispostfix( name, "." G_MODULE_SUFFIX ) #if ENABLE_DEPRECATED || vips_ispostfix( name, ".plg" ) #endif ) { char path[VIPS_PATH_MAX]; GModule *module; vips_snprintf( path, VIPS_PATH_MAX - 1, "%s" G_DIR_SEPARATOR_S "%s", dir_name, name ); g_info( "loading \"%s\"", path ); module = g_module_open( path, G_MODULE_BIND_LAZY ); if( !module ) { g_warning( _( "unable to load \"%s\" -- %s" ), path, g_module_error() ); result = -1; } /* Modules will almost certainly create new types, so * they can't be unloaded. */ g_module_make_resident( module ); } g_dir_close( dir ); return( result ); } #endif /*ENABLE_MODULES*/ /* Install this log handler to hide warning messages. */ static void empty_log_handler( const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data ) { } /* Attempt to set a minimum stacksize. This can be important on systems with a * very low default, like musl. */ static void set_stacksize( guint64 size ) { #ifdef HAVE_PTHREAD_DEFAULT_NP pthread_attr_t attr; size_t cur_stack_size; /* Don't allow stacks less than 2mb. */ size = VIPS_MAX( size, 2 * 1024 * 1024 ); if( pthread_attr_init( &attr ) || pthread_attr_getstacksize( &attr, &cur_stack_size ) ) { g_warning( "set_stacksize: unable to get stack size" ); return; } if( cur_stack_size < size ) { if( pthread_attr_setstacksize( &attr, size ) || pthread_setattr_default_np( &attr ) ) g_warning( "set_stacksize: unable to set stack size" ); else g_info( "set stack size to %" G_GUINT64_FORMAT "k", size / (guint64) 1024 ); } #endif /*HAVE_PTHREAD_DEFAULT_NP*/ } static void vips_verbose( void ) { const char *old; old = g_getenv( "G_MESSAGES_DEBUG" ); if( !old ) g_setenv( "G_MESSAGES_DEBUG", G_LOG_DOMAIN, TRUE ); else if( !g_str_equal( old, "all" ) && !g_strrstr( old, G_LOG_DOMAIN ) ) { char *new; new = g_strconcat( old, " ", G_LOG_DOMAIN, NULL ); g_setenv( "G_MESSAGES_DEBUG", new, TRUE ); g_free( new ); } } static int vips_leak( void ) { char txt[1024]; VipsBuf buf = VIPS_BUF_STATIC( txt ); int n_leaks; n_leaks = 0; n_leaks += vips__object_leak(); n_leaks += vips__type_leak(); n_leaks += vips_tracked_get_allocs(); n_leaks += vips_tracked_get_mem(); n_leaks += vips_tracked_get_files(); if( vips_tracked_get_allocs() || vips_tracked_get_mem() || vips_tracked_get_files() ) { vips_buf_appendf( &buf, "memory: %d allocations, %zd bytes\n", vips_tracked_get_allocs(), vips_tracked_get_mem() ); vips_buf_appendf( &buf, "files: %d open\n", vips_tracked_get_files() ); } vips_buf_appendf( &buf, "memory: high-water mark " ); vips_buf_append_size( &buf, vips_tracked_get_mem_highwater() ); vips_buf_appends( &buf, "\n" ); if( strlen( vips_error_buffer() ) > 0 ) { vips_buf_appendf( &buf, "error buffer: %s", vips_error_buffer() ); n_leaks += strlen( vips_error_buffer() ); } fprintf( stderr, "%s", vips_buf_all( &buf ) ); n_leaks += vips__print_renders(); #ifdef DEBUG vips_buffer_dump_all(); #endif /*DEBUG*/ return( n_leaks ); } /** * vips_init: * @argv0: name of application * * This function starts up libvips, see VIPS_INIT(). * * This function is for bindings which need to start up vips. C programs * should use the VIPS_INIT() macro, which does some extra checks. * * See also: VIPS_INIT(). * * Returns: 0 on success, -1 otherwise */ int vips_init( const char *argv0 ) { extern GType vips_system_get_type( void ); extern GType write_thread_state_get_type( void ); extern GType sink_memory_thread_state_get_type( void ); extern GType render_thread_state_get_type( void ); extern GType vips_source_get_type( void ); extern GType vips_source_custom_get_type( void ); extern GType vips_target_get_type( void ); extern GType vips_target_custom_get_type( void ); extern GType vips_g_input_stream_get_type( void ); static gboolean started = FALSE; static gboolean done = FALSE; const char *vips_min_stack_size; gint64 min_stack_size; const char *prefix; const char *libdir; #ifdef ENABLE_NLS char *locale; #endif /* ENABLE_NLS */ /* Two stage done handling: 'done' means we've completed, 'started' * means we're currently initialising. Use this to prevent recursive * invocation. */ if( done ) /* Called more than once, we succeeded, just return OK. */ return( 0 ); if( started ) /* Recursive invocation, something has broken horribly. * Hopefully the first init will handle it. */ return( 0 ); started = TRUE; /* Try to set a minimum stacksize, default 2mb. We need to do this * before any threads start. */ min_stack_size = 2 * 1024 * 1024; if( (vips_min_stack_size = g_getenv( "VIPS_MIN_STACK_SIZE" )) ) min_stack_size = vips__parse_size( vips_min_stack_size ); (void) set_stacksize( min_stack_size ); if( g_getenv( "VIPS_INFO" ) #if ENABLE_DEPRECATED || g_getenv( "IM_INFO" ) #endif ) vips_verbose(); if( g_getenv( "VIPS_PROFILE" ) ) vips_profile_set( TRUE ); if( g_getenv( "VIPS_LEAK" ) ) vips_leak_set( TRUE ); if( g_getenv( "VIPS_TRACE" ) ) vips_cache_set_trace( TRUE ); if( g_getenv( "VIPS_PIPE_READ_LIMIT" ) ) vips_pipe_read_limit = g_ascii_strtoll( g_getenv( "VIPS_PIPE_READ_LIMIT" ), NULL, 10 ); vips_pipe_read_limit_set( vips_pipe_read_limit ); #ifdef G_OS_WIN32 /* Windows has a limit of 512 files open at once for the fopen() family * of functions, and 2048 for the _open() family. This raises the limit * of fopen() to the same level as _open(). * * It will not go any higher than this, unfortunately. */ (void) _setmaxstdio( 2048 ); #endif /*G_OS_WIN32*/ vips__thread_init(); vips__threadpool_init(); vips__buffer_init(); vips__meta_init(); /* This does an unsynchronised static hash table init on first call -- * we have to make sure we do this single-threaded. See: * https://github.com/openslide/openslide/issues/161 */ #if !GLIB_CHECK_VERSION( 2, 48, 1 ) (void) g_get_language_names(); #endif if( !vips__global_lock ) vips__global_lock = vips_g_mutex_new(); if( !vips__global_timer ) vips__global_timer = g_timer_new(); VIPS_SETSTR( vips__argv0, argv0 ); vips__prgname = g_path_get_basename( argv0 ); vips__thread_profile_attach( "main" ); /* We can't do VIPS_GATE_START() until command-line processing * happens, since vips__thread_profile may not be set yet. Call * directly. */ vips__thread_gate_start( "init: main" ); vips__thread_gate_start( "init: startup" ); /* Try to discover our prefix. */ if( (prefix = g_getenv( "VIPSHOME" )) ) g_info( "VIPSHOME = %s", prefix ); if( !(prefix = vips_guess_prefix( argv0, "VIPSHOME" )) || !(libdir = vips_guess_libdir( argv0, "VIPSHOME" )) ) return( -1 ); g_info( "VIPS_PREFIX = %s", VIPS_PREFIX ); g_info( "VIPS_LIBDIR = %s", VIPS_LIBDIR ); g_info( "prefix = %s", prefix ); g_info( "libdir = %s", libdir ); /* Get i18n .mo files from $VIPSHOME/share/locale/. */ #ifdef ENABLE_NLS locale = g_build_filename( prefix, "share", "locale", NULL ); bindtextdomain( GETTEXT_PACKAGE, locale ); g_free( locale ); #ifdef HAVE_BIND_TEXTDOMAIN_CODESET bind_textdomain_codeset( GETTEXT_PACKAGE, "UTF-8" ); #endif /* HAVE_BIND_TEXTDOMAIN_CODESET */ #endif /* ENABLE_NLS */ /* Register base vips types. */ (void) vips_image_get_type(); (void) vips_region_get_type(); (void) write_thread_state_get_type(); (void) sink_memory_thread_state_get_type(); (void) render_thread_state_get_type(); (void) vips_source_get_type(); (void) vips_source_custom_get_type(); (void) vips_target_get_type(); (void) vips_target_custom_get_type(); vips__meta_init_types(); vips__interpolate_init(); #if ENABLE_DEPRECATED im__format_init(); #endif /* Start up operator cache. */ vips__cache_init(); /* Recomp reordering system. */ vips__reorder_init(); /* Start up packages. */ (void) vips_system_get_type(); vips_arithmetic_operation_init(); vips_conversion_operation_init(); vips_create_operation_init(); vips_foreign_operation_init(); vips_resample_operation_init(); vips_colour_operation_init(); vips_histogram_operation_init(); vips_convolution_operation_init(); vips_freqfilt_operation_init(); vips_morphology_operation_init(); vips_draw_operation_init(); vips_mosaicing_operation_init(); vips_g_input_stream_get_type(); #ifdef ENABLE_MODULES /* Load any vips8 modules from the vips libdir. Keep going, even if * some modules fail to load. * * Only do this if we have been built as a set of loadable * modules, or we might try loading an operation into a library that * already has that operation built in. */ (void) vips_load_plugins( "%s/vips-modules-%d.%d", libdir, VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION ); #if ENABLE_DEPRECATED /* Load any vips8 plugins from the vips libdir. */ (void) vips_load_plugins( "%s/vips-plugins-%d.%d", libdir, VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION ); /* Load up any vips7 plugins in the vips libdir. We don't error on * failure, it's too annoying to have VIPS refuse to start because of * a broken plugin. */ if( im_load_plugins( "%s/vips-%d.%d", libdir, VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION ) ) { g_warning( "%s", vips_error_buffer() ); vips_error_clear(); } /* Also load from libdir. This is old and slightly broken behaviour * :-( kept for back compat convenience. */ if( im_load_plugins( "%s", libdir ) ) { g_warning( "%s", vips_error_buffer() ); vips_error_clear(); } #endif /*ENABLE_DEPRECATED*/ #endif /*ENABLE_MODULES*/ /* Get the run-time compiler going. */ vips_vector_init(); #ifdef HAVE_GSF /* Use this for structured file write. */ gsf_init(); #endif /*HAVE_GSF*/ #ifdef DEBUG_LEAK vips__image_pixels_quark = g_quark_from_static_string( "vips-image-pixels" ); #endif /*DEBUG_LEAK*/ /* If VIPS_WARNING is defined, suppress all warning messages from vips. * * Libraries should not call g_log_set_handler(), it is * supposed to be for the application layer, but this can be awkward to * set up if you are using libvips from something like Ruby. Allow this * env var hack as a workaround. */ if( g_getenv( "VIPS_WARNING" ) #if ENABLE_DEPRECATED || g_getenv( "IM_WARNING" ) #endif ) g_log_set_handler( G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, empty_log_handler, NULL ); /* Block any untrusted operations. This must come after plugin load. */ if( g_getenv( "VIPS_BLOCK_UNTRUSTED" ) ) vips_block_untrusted_set( TRUE ); done = TRUE; vips__thread_gate_stop( "init: startup" ); return( 0 ); } /* Call this before vips stuff that uses stuff we need to have inited. */ void vips_check_init( void ) { /* Pass in a nonsense name for argv0 ... this init path is only here * for old programs which are missing an vips_init() call. We need * i18n set up before we can translate. */ if( vips_init( "vips" ) ) vips_error_clear(); } /** * vips_thread_shutdown: * * Free any thread-private data and flush any profiling information. * * This function needs to be called when a thread that has been using vips * exits. It is called for you by vips_shutdown() and for any threads created * within the #VipsThreadPool. * * You will need to call it from threads created in * other ways or there will be memory leaks. If you do not call it, vips * will generate a warning message. * * It may be called many times, and you can continue using vips after * calling it. Calling it too often will reduce performance. */ void vips_thread_shutdown( void ) { vips__thread_profile_detach(); vips__buffer_shutdown(); } /** * vips_shutdown: * * Call this to drop caches, close plugins, terminate background threads, and * finalize any internal library testing. * * vips_shutdown() is optional. If you don't call it, your platform will * clean up for you. The only negative consequences are that the leak checker * and the profiler will not work. * * You may call VIPS_INIT() many times and vips_shutdown() many times, but you * must not call VIPS_INIT() after vips_shutdown(). In other words, you cannot * stop and restart libvips. * * See also: vips_profile_set(), vips_leak_set(). */ void vips_shutdown( void ) { #ifdef DEBUG printf( "vips_shutdown:\n" ); #endif /*DEBUG*/ vips_cache_drop_all(); #if ENABLE_DEPRECATED im_close_plugins(); #endif /* Mustn't run this more than once. Don't use the VIPS_GATE macro, * since we don't for gate start. */ { static gboolean done = FALSE; if( !done ) vips__thread_gate_stop( "init: main" ); } vips__render_shutdown(); vips_thread_shutdown(); vips__thread_profile_stop(); vips__threadpool_shutdown(); #ifdef HAVE_GSF gsf_shutdown(); #endif /*HAVE_GSF*/ /* Don't free vips__global_lock -- we want to be able to use * vips_error_buffer() after vips_shutdown(), since vips_leak() can * call it. */ VIPS_FREE( vips__argv0 ); VIPS_FREE( vips__prgname ); VIPS_FREEF( g_timer_destroy, vips__global_timer ); /* In dev releases, always show leaks. But not more than once, it's * annoying. */ #ifndef DEBUG_LEAK if( vips__leak ) #endif /*DEBUG_LEAK*/ { static gboolean done = FALSE; if( !done ) { done = TRUE; vips_leak(); } } } static gboolean vips_lib_info_cb( const gchar *option_name, const gchar *value, gpointer data, GError **error ) { vips_verbose(); return( TRUE ); } static gboolean vips_set_fatal_cb( const gchar *option_name, const gchar *value, gpointer data, GError **error ) { vips__fatal = 1; /* 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 ); return( TRUE ); } static gboolean vips_lib_version_cb( const gchar *option_name, const gchar *value, gpointer data, GError **error ) { printf( "libvips %s\n", VIPS_VERSION_STRING ); vips_shutdown(); exit( 0 ); } static gboolean vips_lib_config_cb( const gchar *option_name, const gchar *value, gpointer data, GError **error ) { printf( "%s\n", VIPS_CONFIG ); vips_shutdown(); exit( 0 ); } static gboolean vips_cache_max_cb( const gchar *option_name, const gchar *value, gpointer data, GError **error ) { vips_cache_set_max( vips__parse_size( value ) ); return( TRUE ); } static gboolean vips_cache_max_memory_cb( const gchar *option_name, const gchar *value, gpointer data, GError **error ) { vips_cache_set_max_mem( vips__parse_size( value ) ); return( TRUE ); } static gboolean vips_cache_max_files_cb( const gchar *option_name, const gchar *value, gpointer data, GError **error ) { vips_cache_set_max_files( vips__parse_size( value ) ); return( TRUE ); } static GOptionEntry option_entries[] = { { "vips-info", 0, G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &vips_lib_info_cb, N_( "show informative messages" ), NULL }, { "vips-fatal", 0, G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &vips_set_fatal_cb, N_( "abort on first error or warning" ), NULL }, { "vips-concurrency", 0, 0, G_OPTION_ARG_INT, &vips__concurrency, N_( "evaluate with N concurrent threads" ), "N" }, { "vips-tile-width", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_INT, &vips__tile_width, N_( "set tile width to N (DEBUG)" ), "N" }, { "vips-tile-height", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_INT, &vips__tile_height, N_( "set tile height to N (DEBUG)" ), "N" }, { "vips-thinstrip-height", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_INT, &vips__thinstrip_height, N_( "set thinstrip height to N (DEBUG)" ), "N" }, { "vips-fatstrip-height", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_INT, &vips__fatstrip_height, N_( "set fatstrip height to N (DEBUG)" ), "N" }, { "vips-progress", 0, 0, G_OPTION_ARG_NONE, &vips__progress, N_( "show progress feedback" ), NULL }, { "vips-leak", 0, 0, G_OPTION_ARG_NONE, &vips__leak, N_( "leak-check on exit" ), NULL }, { "vips-profile", 0, 0, G_OPTION_ARG_NONE, &vips__thread_profile, N_( "profile and dump timing on exit" ), NULL }, { "vips-disc-threshold", 0, 0, G_OPTION_ARG_STRING, &vips__disc_threshold, N_( "images larger than N are decompressed to disc" ), "N" }, { "vips-novector", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &vips__vector_enabled, N_( "disable vectorised versions of operations" ), NULL }, { "vips-cache-max", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &vips_cache_max_cb, N_( "cache at most N operations" ), "N" }, { "vips-cache-max-memory", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &vips_cache_max_memory_cb, N_( "cache at most N bytes in memory" ), "N" }, { "vips-cache-max-files", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &vips_cache_max_files_cb, N_( "allow at most N open files" ), "N" }, { "vips-cache-trace", 0, 0, G_OPTION_ARG_NONE, &vips__cache_trace, N_( "trace operation cache" ), NULL }, { "vips-cache-dump", 0, 0, G_OPTION_ARG_NONE, &vips__cache_dump, N_( "dump operation cache on exit" ), NULL }, { "vips-version", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &vips_lib_version_cb, N_( "print libvips version" ), NULL }, { "vips-config", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &vips_lib_config_cb, N_( "print libvips config" ), NULL }, { "vips-pipe-read-limit", 0, 0, G_OPTION_ARG_INT64, (gpointer) &vips_pipe_read_limit, N_( "read at most this many bytes from a pipe" ), NULL }, { NULL } }; /** * vips_add_option_entries: * @option_group: group to add to * * Add the standard vips %GOptionEntry to a %GOptionGroup. * * See also: g_option_group_new(). */ void vips_add_option_entries( GOptionGroup *option_group ) { g_option_group_add_entries( option_group, option_entries ); } /* Find the prefix part of a dir ... name is the name of this prog from argv0. * * dir name guess prefix * * /home/john/vips-7.6.4/bin/vips-7.6 vips-7.6 /home/john/vips-7.6.4 * /usr/local/bin/ip ip /usr/local * * all other forms ... return NULL. */ static char * extract_prefix( const char *dir, const char *name ) { char edir[VIPS_PATH_MAX]; static char vname[VIPS_PATH_MAX]; int i; g_info( "trying for dir = \"%s\", name = \"%s\"", dir, name ); /* Is dir relative? Prefix with cwd. */ if( !g_path_is_absolute( dir ) ) { char *cwd; cwd = g_get_current_dir(); vips_snprintf( edir, VIPS_PATH_MAX, "%s" G_DIR_SEPARATOR_S "%s", cwd, dir ); g_free( cwd ); } else { vips_strncpy( edir, dir, VIPS_PATH_MAX ); } /* Chop off the trailing prog name, plus the trailing * G_DIR_SEPARATOR_S. */ if( !vips_ispostfix( edir, name ) ) return( NULL ); vips_strncpy( vname, edir, VIPS_PATH_MAX ); vname[strlen( edir ) - strlen( name ) - 1] = '\0'; /* Remove any "/./", any trailing "/.", any trailing "/". */ for( i = 0; i < (int) strlen( vname ); i++ ) if( vips_isprefix( G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S, vname + i ) ) memmove( vname + i, vname + i + 2, strlen( vname + i + 2 ) + 1 ); if( vips_ispostfix( vname, G_DIR_SEPARATOR_S "." ) ) vname[strlen( vname ) - 2] = '\0'; if( vips_ispostfix( vname, G_DIR_SEPARATOR_S ) ) vname[strlen( vname ) - 1] = '\0'; g_info( "canonicalised path = \"%s\"", vname ); /* Ought to be a "/bin" at the end now. */ if( !vips_ispostfix( vname, G_DIR_SEPARATOR_S "bin" ) ) return( NULL ); vname[strlen( vname ) - strlen( G_DIR_SEPARATOR_S "bin" )] = '\0'; g_info( "found \"%s\"", vname ); return( vname ); } /* Search a path for a file ... we overwrite the PATH string passed in. */ static char * scan_path( char *path, const char *name ) { char *p, *q; char *prefix; for( p = path; (q = vips_break_token( p, G_SEARCHPATH_SEPARATOR_S )); p = q ) { char str[VIPS_PATH_MAX]; /* Form complete path. */ vips_snprintf( str, VIPS_PATH_MAX, "%s" G_DIR_SEPARATOR_S "%s", p, name ); g_info( "looking in \"%s\" for \"%s\"", p, name ); if( vips_existsf( "%s", str ) && (prefix = extract_prefix( str, name )) ) { return( prefix ); } } return( NULL ); } /* Look for a file along PATH. If we find it, look for an enclosing prefix. */ static char * find_file( const char *name ) { const char *path = g_getenv( "PATH" ); char *prefix; char full_path[VIPS_PATH_MAX]; if( !path ) return( NULL ); g_info( "g_getenv( \"PATH\" ) == \"%s\"", path ); #ifdef G_OS_WIN32 { char *dir; /* Windows always searches '.' first, so prepend cwd to path. */ dir = g_get_current_dir(); vips_snprintf( full_path, VIPS_PATH_MAX, "%s" G_SEARCHPATH_SEPARATOR_S "%s", dir, path ); g_free( dir ); } #else /*!G_OS_WIN32*/ vips_strncpy( full_path, path, VIPS_PATH_MAX ); #endif /*G_OS_WIN32*/ if( (prefix = scan_path( full_path, name )) ) return( prefix ); return( NULL ); } /* Guess a value for the install PREFIX. */ static const char * guess_prefix( const char *argv0, const char *name ) { char *prefix; /* We've already checked for VIPSHOME. If the configure-time * library prefix looks OK, use the configure-time prefix. */ if( vips_existsf( "%s/vips-modules-%d.%d", VIPS_LIBDIR, VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION ) ) { g_info( "found %s/vips-modules-%d.%d", VIPS_LIBDIR, VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION ); g_info( "using configure-time prefix" ); return( VIPS_PREFIX ); } /* Try to guess from argv0. */ if( argv0 ) { if( g_path_is_absolute( argv0 ) ) { /* Must point to our executable. */ if( (prefix = extract_prefix( argv0, name )) ) { g_info( "found \"%s\" from argv0", prefix ); return( prefix ); } } /* Look along path for name. */ if( (prefix = find_file( name )) ) { g_info( "found \"%s\" from PATH", prefix ); return( prefix ); } } /* Try to guess from cwd. Only if this is a relative path, though. */ if( argv0 && !g_path_is_absolute( argv0 ) ) { char *dir; char full_path[VIPS_PATH_MAX]; char *resolved; dir = g_get_current_dir(); vips_snprintf( full_path, VIPS_PATH_MAX, "%s" G_DIR_SEPARATOR_S "%s", dir, argv0 ); g_free( dir ); if( (resolved = vips_realpath( full_path )) ) { prefix = extract_prefix( resolved, name ); g_free( resolved ); if( prefix ) { g_info( "found \"%s\" from cwd", prefix ); return( prefix ); } } } /* Fall back to the configure-time prefix. */ return( VIPS_PREFIX ); } /** * vips_guess_prefix: * @argv0: program name (typically argv[0]) * @env_name: save prefix in this environment variable * * vips_guess_prefix() tries to guess the install directory. You should pass * in the value of argv[0] (the name your program was run as) as a clue to * help it out, plus the name of the environment variable you let the user * override your package install area with (eg. "VIPSHOME"). * * On success, vips_guess_prefix() returns the prefix it discovered, and as a * side effect, sets the environment variable (if it's not set). * * Don't free the return string! * * See also: vips_guess_libdir(). * * Returns: (transfer none): the install prefix as a static string, do not free. */ const char * vips_guess_prefix( const char *argv0, const char *env_name ) { const char *prefix; /* Already set? */ if( (prefix = g_getenv( env_name )) ) return( prefix ); #ifdef G_OS_WIN32 prefix = vips__windows_prefix(); #else /*!G_OS_WIN32*/ { char *basename; basename = g_path_get_basename( argv0 ); prefix = guess_prefix( argv0, basename ); g_free( basename ); } #endif /*G_OS_WIN32*/ g_setenv( env_name, prefix, TRUE ); return( prefix ); } /** * vips_guess_libdir: * @argv0: program name (typically argv[0]) * @env_name: save prefix in this environment variable * * vips_guess_libdir() tries to guess the install directory (usually the * configure libdir, or $prefix/lib). You should pass * in the value of argv[0] (the name your program was run as) as a clue to * help it out, plus the name of the environment variable you let the user * override your package install area with (eg. "VIPSHOME"). * * On success, vips_guess_libdir() returns the libdir it discovered, and as a * side effect, sets the prefix environment variable (if it's not set). * * Don't free the return string! * * See also: vips_guess_prefix(). * * Returns: (transfer none): the libdir as a static string, do not free. */ const char * vips_guess_libdir( const char *argv0, const char *env_name ) { const char *prefix = vips_guess_prefix( argv0, env_name ); static char *libdir = NULL; char *suffix; if( libdir ) return( libdir ); /* Have we been moved since configure? If not, use the configure-time * libdir. * * The lib directory name can be eg. "lib", "lib64" etc. depending on * the platform, so copy that from the configure-time libdir if we can. * The configure-time LIBDIR is generated by autotools and always uses * '/', even on Windows. */ if( strcmp( prefix, VIPS_PREFIX ) == 0 ) libdir = VIPS_LIBDIR; else if( (suffix = strrchr( VIPS_LIBDIR, '/' )) ) libdir = g_strdup_printf( "%s%s", prefix, suffix ); else libdir = g_strdup_printf( "%s/lib", prefix ); return( libdir ); } /** * vips_version_string: * * Get the VIPS version as a static string, including a build date and time. * Do not free. * * Returns: (transfer none): a static version string */ const char * vips_version_string( void ) { return( VIPS_VERSION_STRING ); } /** * vips_version: * @flag: which field of the version to get * * Get the major, minor or micro library version, with @flag values 0, 1 and * 2. * * Get the ABI current, revision and age (as used by Meson) with @flag * values 3, 4, 5. * * Returns: library version number */ int vips_version( int flag ) { switch( flag ) { case 0: return( VIPS_MAJOR_VERSION ); case 1: return( VIPS_MINOR_VERSION ); case 2: return( VIPS_MICRO_VERSION ); case 3: return( VIPS_LIBRARY_CURRENT ); case 4: return( VIPS_LIBRARY_REVISION ); case 5: return( VIPS_LIBRARY_AGE ); default: vips_error( "vips_version", "%s", _( "flag not in [0, 5]" ) ); return( -1 ); } } /** * vips_leak_set: * @leak: turn leak checking on or off * * Turn on or off vips leak checking. See also --vips-leak, * vips_add_option_entries() and the `VIPS_LEAK` environment variable. * * You should call this very early in your program. */ void vips_leak_set( gboolean leak ) { vips__leak = leak; } static void * vips_block_untrusted_set_operation( VipsOperationClass *class, gboolean *state ) { g_assert( VIPS_IS_OPERATION_CLASS( class ) ); if( class->flags & VIPS_OPERATION_UNTRUSTED ) vips_operation_block_set( G_OBJECT_CLASS_NAME( class ), *state ); return( NULL ); } /** * vips_block_untrusted_set: * @state: the block state to set * * Set the block state on all untrusted operations. * * |[ * vips_block_untrusted_set( TRUE ); * ]| * * Will block all untrusted operations from running. * * Use `vips -l` at the command-line to see the class hierarchy and which * operations are marked as untrusted. * * Set the environment variable `VIPS_BLOCK_UNTRUSTED` to block all untrusted * operations on vips_init(). */ void vips_block_untrusted_set( gboolean state ) { vips_class_map_all( g_type_from_name( "VipsOperation" ), (VipsClassMapFn) vips_block_untrusted_set_operation, &state ); }