1 /* main() ... start everything up. See mainw.c for main window stuff.
2  */
3 
4 /*
5 
6     Copyright (C) 1991-2003 The National Gallery
7 
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version.
12 
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License along
19     with this program; if not, write to the Free Software Foundation, Inc.,
20     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21 
22  */
23 
24 /*
25 
26     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
27 
28  */
29 
30 #include "ip.h"
31 
32 /*
33 #define DEBUG
34  */
35 
36 /* Show all paint actions with flashing stuff.
37 #define DEBUG_UPDATES
38  */
39 
40 /* Stop startup creation of externs for all VIPS functions etc.
41 #define DEBUG_NOAUTO
42  */
43 
44 /* Stop on any gtk error/warning/whatever. Usually set by configure for dev
45  * builds.
46 #define DEBUG_FATAL
47  */
48 
49 /* But some themes can trigger warnings, argh, so sometimes we need to
50  * undef it. VipsObject sets can trigger warnings. libgoffice will warn about
51  * precision issues if run under valgrind.
52  */
53 #undef DEBUG_FATAL
54 
55 /* Time startup.
56 #define DEBUG_TIME
57  */
58 
59 /* On quit, make sure we free stuff we can free.
60 #define DEBUG_LEAK
61  */
62 
63 /* Sometimes we need to be able to disable these at build time.
64 #undef DEBUG_LEAK
65 #undef DEBUG_FATAL
66  */
67 
68 /* General stuff.
69  */
70 Workspaceroot *main_workspaceroot = NULL;	/* All the workspaces */
71 Toolkitgroup *main_toolkitgroup = NULL;		/* All the toolkits */
72 Symbol *main_symbol_root = NULL;		/* Root of symtable */
73 Watchgroup *main_watchgroup = NULL;		/* All of the watches */
74 Imageinfogroup *main_imageinfogroup = NULL;	/* All of the images */
75 
76 void *main_c_stack_base = NULL;			/* Base of C stack */
77 
78 gboolean main_starting = TRUE;			/* In startup */
79 
80 static const char *main_argv0 = NULL;		/* argv[0] */
81 static iOpenFile *main_stdin = NULL;		/* stdin as an iOpenFile */
82 static GtkIconFactory *main_icon_factory = NULL;/* Add stocks to this */
83 
84 static char *main_option_script = NULL;
85 static char *main_option_expression = NULL;
86 gboolean main_option_batch = FALSE;
87 static gboolean main_option_no_load_menus = FALSE;
88 static gboolean main_option_no_load_args = FALSE;
89 static gboolean main_option_stdin_ws = FALSE;
90 static gboolean main_option_stdin_def = FALSE;
91 static char *main_option_output = NULL;
92 static char **main_option_set = NULL;
93 static gboolean main_option_benchmark = FALSE;
94 gboolean main_option_time_save = FALSE;
95 gboolean main_option_profile = FALSE;
96 gboolean main_option_i18n = FALSE;
97 gboolean main_option_verbose = FALSE;
98 static gboolean main_option_print_main = FALSE;
99 static gboolean main_option_version = FALSE;
100 static gboolean main_option_test = FALSE;
101 static char *main_option_prefix = NULL;
102 
103 static GOptionEntry main_option[] = {
104 	{ "expression", 'e', 0, G_OPTION_ARG_STRING, &main_option_expression,
105 		N_( "evaluate and print EXPRESSION" ),
106 		"EXPRESSION" },
107 	{ "script", 's', 0, G_OPTION_ARG_FILENAME, &main_option_script,
108 		N_( "load FILE as a set of definitions" ),
109 		"FILE" },
110 	{ "output", 'o', 0, G_OPTION_ARG_FILENAME, &main_option_output,
111 		N_( "write value of 'main' to FILE" ), "FILE" },
112 	{ "batch", 'b', 0, G_OPTION_ARG_NONE, &main_option_batch,
113 		N_( "run in batch mode" ), NULL },
114 	{ "set", '=', 0, G_OPTION_ARG_STRING_ARRAY, &main_option_set,
115 		N_( "set values" ), NULL },
116 	{ "verbose", 'V', 0, G_OPTION_ARG_NONE, &main_option_verbose,
117 		N_( "verbose error output" ), NULL },
118 	{ "no-load-menus", 'm', 0, G_OPTION_ARG_NONE,
119 		&main_option_no_load_menus,
120 		N_( "don't load menu definitions" ), NULL },
121 	{ "no-load-args", 'a', 0, G_OPTION_ARG_NONE, &main_option_no_load_args,
122 		N_( "don't try to load command-line arguments" ), NULL },
123 	{ "stdin-ws", 'w', 0, G_OPTION_ARG_NONE, &main_option_stdin_ws,
124 		N_( "load stdin as a workspace" ), NULL },
125 	{ "stdin-def", 'd', 0, G_OPTION_ARG_NONE, &main_option_stdin_def,
126 		N_( "load stdin as a set of definitions" ), NULL },
127 	{ "print-main", 'p', 0, G_OPTION_ARG_NONE, &main_option_print_main,
128 		N_( "print value of 'main' to stdout" ),
129 		NULL },
130 	{ "benchmark", 'c', 0, G_OPTION_ARG_NONE, &main_option_benchmark,
131 		N_( "start up and shut down" ),
132 		NULL },
133 	{ "time-save", 't', 0, G_OPTION_ARG_NONE, &main_option_time_save,
134 		N_( "time image save operations" ),
135 		NULL },
136 	{ "profile", 'r', 0, G_OPTION_ARG_NONE, &main_option_profile,
137 		N_( "profile workspace calculation" ),
138 		NULL },
139 	{ "prefix", 'x', 0, G_OPTION_ARG_FILENAME, &main_option_prefix,
140 		N_( "start as if installed to PREFIX" ), "PREFIX" },
141 	{ "i18n", 'i', 0, G_OPTION_ARG_NONE, &main_option_i18n,
142 		N_( "output strings for internationalisation" ),
143 		NULL },
144 	{ "version", 'v', 0, G_OPTION_ARG_NONE, &main_option_version,
145 		N_( "print version number" ),
146 		NULL },
147 	{ "test", 'T', 0, G_OPTION_ARG_NONE, &main_option_test,
148 		N_( "test for errors and quit" ), NULL },
149 	{ NULL }
150 };
151 
152 /* Accumulate startup errors here.
153  */
154 static char main_start_error_txt[MAX_STRSIZE];
155 static VipsBuf main_start_error = VIPS_BUF_STATIC( main_start_error_txt );
156 
157 static void
main_log_add(const char * fmt,...)158 main_log_add( const char *fmt, ... )
159 {
160 	va_list ap;
161 
162         va_start( ap, fmt );
163 	vips_buf_vappendf( &main_start_error, fmt, ap );
164         va_end( ap );
165 }
166 
167 static const char *
main_log_get(void)168 main_log_get( void )
169 {
170 	return( vips_buf_all( &main_start_error ) );
171 }
172 
173 static gboolean
main_log_is_empty(void)174 main_log_is_empty( void )
175 {
176 	return( vips_buf_is_empty( &main_start_error ) );
177 }
178 
179 /* NULL log handler. Used to suppress output on win32 without DEBUG_FATAL.
180  */
181 #ifndef DEBUG_FATAL
182 #ifdef OS_WIN32
183 static void
main_log_null(const char * log_domain,GLogLevelFlags log_level,const char * message,void * user_data)184 main_log_null( const char *log_domain, GLogLevelFlags log_level,
185 	const char *message, void *user_data )
186 {
187 }
188 #endif /*OS_WIN32*/
189 #endif /*!DEBUG_FATAL*/
190 
191 /* Print all errors and quit. Batch mode only.
192  */
193 static void
main_error_exit(const char * fmt,...)194 main_error_exit( const char *fmt, ... )
195 {
196 	va_list args;
197 
198         va_start( args, fmt );
199         (void) vfprintf( stderr, fmt, args );
200         va_end( args );
201 	fprintf( stderr, "\n" );
202 
203 	if( strcmp( error_get_top(), "" ) != 0 ) {
204 		fprintf( stderr, "%s\n", error_get_top() );
205 		if( strcmp( error_get_sub(), "" ) != 0 )
206 			fprintf( stderr, "%s\n", error_get_sub() );
207 	}
208 
209 	if( main_option_verbose ) {
210 		char txt[MAX_STRSIZE];
211 		VipsBuf buf = VIPS_BUF_STATIC( txt );
212 
213 		slist_map( expr_error_all,
214 			(SListMapFn) expr_error_print, &buf );
215 		fprintf( stderr, "%s", vips_buf_all( &buf ) );
216 	}
217 
218 	exit( 1 );
219 }
220 
221 /* Output a single main.
222  */
223 static void
main_print_main(Symbol * sym)224 main_print_main( Symbol *sym )
225 {
226 	PElement *root;
227 
228 	root = &sym->expr->root;
229 	if( !symbol_recalculate_check( sym ) ||
230 		!reduce_pelement( reduce_context, reduce_spine_strict, root ) )
231 		main_error_exit( _( "error calculating \"%s\"" ),
232 			symbol_name_scope( sym ) );
233 
234 	if( main_option_output ) {
235 		char filename[FILENAME_MAX];
236 
237 		im_strncpy( filename, main_option_output, FILENAME_MAX );
238 		if( !group_save_item( root, filename ) )
239 			main_error_exit( _( "error saving \"%s\"" ),
240 				symbol_name_scope( sym ) );
241 	}
242 
243 	if( main_option_print_main )
244 		graph_value( root );
245 }
246 
247 static void *
main_print_ws(Workspace * ws,gboolean * found)248 main_print_ws( Workspace *ws, gboolean *found )
249 {
250 	Symbol *sym;
251 
252 	if( (sym = compile_lookup( ws->sym->expr->compile, "main" )) ) {
253 		main_print_main( sym );
254 		*found = TRUE;
255 	}
256 
257 	return( NULL );
258 }
259 
260 /* Clean up our application and quit. Not interactive! Do any "has been
261  * modified, OK to quit?" stuff before this, see main_quit_test().
262  */
263 static void
main_quit(void)264 main_quit( void )
265 {
266 #if HAVE_FFTW || HAVE_FFTW3
267 	iOpenFile *of;
268 #endif /*HAVE_FFTW || HAVE_FFTW3*/
269 
270 #ifdef DEBUG
271 	printf( "main_quit: cleaning up ...\n" );
272 #endif/*DEBUG*/
273 
274 	if( main_option_print_main ||
275 		main_option_output ) {
276 		Symbol *sym;
277 		gboolean found;
278 
279 		symbol_recalculate_all();
280 
281 		/* Process all the mains we can find: one at the top level,
282 		 * one in each workspace.
283 		 */
284 		found = FALSE;
285 		if( (sym = compile_lookup(
286 			symbol_root->expr->compile, "main" )) ) {
287 			main_print_main( sym );
288 			found = TRUE;
289 		}
290 		workspace_map( (workspace_map_fn) main_print_ws, &found, NULL );
291 
292 		if( !found )
293 			main_error_exit( "%s", _( "no \"main\" found" ) );
294 	}
295 
296 	/* Force all our windows down.
297 	 */
298 	iwindow_map_all( (iWindowMapFn) iwindow_kill, NULL );
299 
300 	/* Saves recent and stuff like that.
301 	 */
302 	mainw_shutdown();
303 
304 	/* Dump wisdom back again.
305 	 */
306 #if HAVE_FFTW || HAVE_FFTW3
307 	if( (of = ifile_open_write( "%s" G_DIR_SEPARATOR_S "wisdom",
308 		get_savedir() )) ) {
309 		fftw_export_wisdom_to_file( of->fp );
310 		ifile_close( of );
311 	}
312 #endif /*HAVE_FFTW*/
313 
314 	/* Remove any ws retain files.
315 	 */
316 	workspacegroup_autosave_clean();
317 
318 	/* Junk all symbols. This may remove a bunch of intermediate images
319 	 * too.
320 	 */
321 	UNREF( main_watchgroup );
322 	UNREF( main_symbol_root );
323 	UNREF( main_toolkitgroup );
324 	UNREF( main_workspaceroot );
325 
326 	/* Junk reduction machine ... this should remove all image temps.
327 	 */
328 	reduce_destroy( reduce_context );
329 
330 #ifdef DEBUG_LEAK
331 	/* Free other GTK stuff.
332 	 */
333 	if( main_icon_factory )
334 		gtk_icon_factory_remove_default( main_icon_factory );
335 	junk_tooltips();
336 
337 #ifdef HAVE_LIBGOFFICE
338 	/* Not quite sure what this does, but don't do it in batch mode.
339  	 */
340 	if( !main_option_batch )
341 		libgoffice_shutdown ();
342 #endif /*HAVE_LIBGOFFICE*/
343 
344 	path_rewrite_free_all();
345 
346 	/* Should have freed everything now.
347 	 */
348 
349 	/* Make sure!
350 
351 		FIXME ... #ifdef this lot out at some point
352 
353 	 */
354 	UNREF( main_imageinfogroup );
355 	heap_check_all_destroyed();
356 	vips_shutdown();
357 	managed_check_all_destroyed();
358 	util_check_all_destroyed();
359 	call_check_all_destroyed();
360 #endif /*DEBUG_LEAK*/
361 
362 #ifdef DEBUG
363 	printf( "main_quit: exit( 0 )\n" );
364 #endif/*DEBUG*/
365 
366 	/* And exit.
367 	 */
368 	exit( 0 );
369 }
370 
371 /* We mustn't quit recursively!
372  */
373 static gboolean main_quit_running = FALSE;
374 
375 static void
main_quit_test_cb(void * sys,iWindowResult result)376 main_quit_test_cb( void *sys, iWindowResult result )
377 {
378 #ifdef DEBUG
379 	printf( "main_quit_test_cb:\n" );
380 #endif/*DEBUG*/
381 
382 	if( result == IWINDOW_YES )
383 		/* No return from this.
384 		 */
385 		main_quit();
386 	else
387 		/* Quit has been cancelled.
388 		 */
389 		main_quit_running = FALSE;
390 }
391 
392 /* Check before quitting.
393  */
394 void
main_quit_test(void)395 main_quit_test( void )
396 {
397 	if( main_quit_running ) {
398 #ifdef DEBUG
399 		printf( "main_quit_test: recursive quit blocked\n" );
400 #endif/*DEBUG*/
401 		return;
402 	}
403 	main_quit_running = TRUE;
404 
405 #ifdef DEBUG
406 	printf( "main_quit_test:\n" );
407 #endif/*DEBUG*/
408 
409 	/* Flush any pending preference saves before we look for dirty
410 	 * objects.
411 	 */
412 	watchgroup_flush( main_watchgroup );
413 
414 	/* Close registered models.
415 	 */
416 	filemodel_inter_close_registered_cb( iwindow_pick_one(), NULL,
417 		main_quit_test_cb, NULL );
418 }
419 
420 static void
main_watchgroup_changed_cb(void)421 main_watchgroup_changed_cb( void )
422 {
423 	/* Only set this in GUI mode. Otherwise, let the user control CPUs
424 	 * with the env variable and --vips-concurrency args.
425 	 */
426 	if( !main_option_batch )
427 		im_concurrency_set( VIPS_CPUS );
428 }
429 
430 /* Try to load a thing, anything at all. Actually, we don't load plugins
431  * experimentally, win32 pops up an annoying error dialog if you try that.
432  */
433 static gboolean
main_load(Workspace * ws,const char * filename)434 main_load( Workspace *ws, const char *filename )
435 {
436 	Workspacegroup *new_wsg;
437 
438 	if( (new_wsg = workspacegroup_new_from_file( main_workspaceroot,
439 		filename, filename )) ) {
440 		Mainw *mainw;
441 
442 		if( !main_option_batch ) {
443 			mainw = mainw_new( new_wsg );
444 			gtk_widget_show( GTK_WIDGET( mainw ) );
445 		}
446 
447 		mainw_recent_add( &mainw_recent_workspace, filename );
448 
449 		return( TRUE );
450 	}
451 
452 	error_clear();
453 
454 	/* workspace_load_file() needs to recalc to work, try to avoid that by
455 	 * doing .defs first.
456 	 */
457 	if( is_file_type( &filesel_dfile_type, filename ) ) {
458 		if( toolkit_new_from_file( main_toolkitgroup, filename ) )
459 			return( TRUE );
460 	}
461 
462 	/* Try as matrix or image. Have to do these via definitions.
463 	 */
464 	if( workspace_load_file( ws, filename ) )
465 		return( TRUE );
466 
467 	error_clear();
468 
469 	error_top( _( "Unknown file type." ) );
470 	error_sub( _( "Unable to load \"%s\"." ), filename );
471 
472 	return( FALSE );
473 }
474 
475 #ifndef DEBUG_NOAUTO
476 static void *
main_load_plug(char * name)477 main_load_plug( char *name )
478 {
479 	if( !calli_string_filename( (calli_string_fn) im_load_plugin,
480 		name, NULL, NULL, NULL ) ) {
481 		error_top( _( "Unable to load." ) );
482 		error_sub( _( "Error loading plug-in \"%s\"." ), name );
483 		error_vips();
484 		iwindow_alert( NULL, GTK_MESSAGE_ERROR );
485 	}
486 
487 	return( NULL );
488 }
489 #endif /*!DEBUG_NOAUTO*/
490 
491 static void *
main_load_def(const char * filename)492 main_load_def( const char *filename )
493 {
494 	Toolkit *kit;
495 
496 	if( !main_option_no_load_menus || im_skip_dir( filename )[0] == '_' ) {
497 		progress_update_loading( 0, im_skip_dir( filename ) );
498 
499 		if( !(kit = toolkit_new_from_file( main_toolkitgroup,
500 			filename )) )
501 			iwindow_alert( NULL, GTK_MESSAGE_ERROR );
502 		else
503 			filemodel_set_auto_load( FILEMODEL( kit ) );
504 	}
505 
506 	return( NULL );
507 }
508 
509 static void *
main_load_wsg(const char * filename)510 main_load_wsg( const char *filename )
511 {
512 	Workspacegroup *wsg;
513 
514 #ifdef DEBUG
515 	printf( "main_load_wsg: %s\n", filename );
516 #endif/*DEBUG*/
517 
518 	progress_update_loading( 0, im_skip_dir( filename ) );
519 
520 	if( !(wsg = workspacegroup_new_from_file( main_workspaceroot,
521 		filename, filename )) )
522 		iwindow_alert( NULL, GTK_MESSAGE_ERROR );
523 	else {
524 		filemodel_set_auto_load( FILEMODEL( wsg ) );
525 	}
526 
527 	return( NULL );
528 }
529 
530 #ifndef DEBUG_NOAUTO
531 /* Link all the packages in a function.
532  */
533 static void *
main_link_package(im_package * pack)534 main_link_package( im_package *pack)
535 {
536 	char name[MAX_STRSIZE];
537 	Toolkit *kit;
538         int i;
539 
540 	im_snprintf( name, MAX_STRSIZE, "_%s", pack->name );
541 	kit = toolkit_new( main_toolkitgroup, name );
542 
543         for( i = 0; i < pack->nfuncs; i++ )
544 		if( call_is_callable( pack->table[i] ) ) {
545 			Symbol *sym;
546 
547 			sym = symbol_new( symbol_root->expr->compile,
548 				pack->table[i]->name );
549 			g_assert( sym->type == SYM_ZOMBIE );
550 			sym->type = SYM_EXTERNAL;
551 			sym->function = pack->table[i];
552 			sym->fn_nargs = call_n_args( pack->table[i] );
553 			(void) tool_new_sym( kit, -1, sym );
554 			symbol_made( sym );
555 		}
556 
557 	filemodel_set_auto_load( FILEMODEL( kit ) );
558 	filemodel_set_modified( FILEMODEL( kit ), FALSE );
559 	kit->pseudo = TRUE;
560 
561         return( NULL );
562 }
563 #endif /*!DEBUG_NOAUTO*/
564 
565 /* Load all plugins and defs.
566  */
567 static void
main_load_startup(void)568 main_load_startup( void )
569 {
570 	mainw_recent_freeze();
571 
572 /* Stop load of builtins, plugs and vips ... handy for debugging if you're
573  * tracing symbol.c
574  */
575 #ifdef DEBUG_NOAUTO
576 	printf( "*** DEBUG_NOAUTO set, not loading builtin, plugs and vips\n" );
577 #else /*!DEBUG_NOAUTO*/
578 
579 #ifdef DEBUG
580 	printf( "built-ins init\n" );
581 #endif/*DEBUG*/
582 
583 	/* Add builtin toolkit.
584 	 */
585 	builtin_init();
586 
587 #ifdef DEBUG
588 	printf( "plug-ins init\n" );
589 #endif/*DEBUG*/
590 
591 	/* Load any plug-ins on PATH_START.
592 	 */
593 	(void) path_map( PATH_START, "*.plg",
594 		(path_map_fn) main_load_plug, NULL );
595 
596 	/* Link all VIPS functions as SYM_EXTERNAL.
597 	 */
598         (void) im_map_packages( (VSListMap2Fn) main_link_package, NULL );
599 #endif /*!DEBUG_NOAUTO*/
600 
601 	/* Load up all defs and wses.
602 	 */
603 #ifdef DEBUG
604 	printf( "definitions init\n" );
605 #endif/*DEBUG*/
606 	(void) path_map( PATH_START, "*.def",
607 		(path_map_fn) main_load_def, NULL );
608 
609 #ifdef DEBUG
610 	printf( "ws init\n" );
611 #endif/*DEBUG*/
612 	(void) path_map( PATH_START, "*.ws",
613 		(path_map_fn) main_load_wsg, NULL );
614 
615 	mainw_recent_thaw();
616 }
617 
618 static void *
main_junk_auto_load(Filemodel * filemodel)619 main_junk_auto_load( Filemodel *filemodel )
620 {
621 	g_assert( IS_FILEMODEL( filemodel ) );
622 
623 	if( filemodel->auto_load )
624 		IDESTROY( filemodel );
625 
626 	return( NULL );
627 }
628 
629 /* Remove and reload all menus/plugins/workspaces.
630  */
631 void
main_reload(void)632 main_reload( void )
633 {
634 	progress_begin();
635 
636 	/* Remove.
637 	 */
638 	toolkitgroup_map( main_toolkitgroup,
639 		(toolkit_map_fn) main_junk_auto_load, NULL, NULL );
640 	workspace_map( (workspace_map_fn) main_junk_auto_load, NULL, NULL );
641 	im_close_plugins();
642 
643 	/* Reload.
644 	 */
645 	main_load_startup();
646 
647 	/* We may have changed our prefs ... link the watches to the
648 	 * new prefs workspace.
649 	 */
650 	watch_relink_all();
651 
652 	progress_end();
653 }
654 
655 /* Use a file to paint a named stock item.
656  */
657 static void
main_file_for_stock(GtkIconFactory * icon_factory,const char * stock,const char * file)658 main_file_for_stock( GtkIconFactory *icon_factory,
659 	const char *stock, const char *file )
660 {
661 	GtkIconSource *icon_source;
662 	GtkIconSet *icon_set;
663 	char buf[FILENAME_MAX];
664 
665 	im_snprintf( buf, FILENAME_MAX,
666 		"$VIPSHOME/share/$PACKAGE/data/%s", file );
667 	path_expand( buf );
668 	icon_source = gtk_icon_source_new();
669 	gtk_icon_source_set_filename( icon_source, buf );
670 	icon_set = gtk_icon_set_new();
671 	gtk_icon_set_add_source( icon_set, icon_source );
672 	gtk_icon_source_free( icon_source );
673 	gtk_icon_factory_add( icon_factory, stock, icon_set );
674 	gtk_icon_set_unref( icon_set );
675 }
676 
677 /* Make our custom icon sets.
678  */
679 static void
main_register_icons(void)680 main_register_icons( void )
681 {
682 	static const GtkStockItem stock_item[] = {
683 /* Can be (eg.)
684  *
685  *   { GTK_STOCK_COPY, N_("_Copy"), GDK_CONTROL_MASK, 'c', GETTEXT_PACKAGE },
686  *
687  */
688 		{ STOCK_NEXT_ERROR,
689 			N_( "Next _Error" ), 0, 0, GETTEXT_PACKAGE },
690 		{ STOCK_DROPPER, N_( "Ink dropper" ), 0, 0, GETTEXT_PACKAGE },
691 		{ STOCK_DUPLICATE, N_( "D_uplicate" ), 0, 0, GETTEXT_PACKAGE },
692 		{ STOCK_PAINTBRUSH, N_( "Pen" ), 0, 0, GETTEXT_PACKAGE },
693 		{ STOCK_LINE, N_( "Line" ), 0, 0, GETTEXT_PACKAGE },
694 		{ STOCK_TEXT, N_( "Text" ), 0, 0, GETTEXT_PACKAGE },
695 		{ STOCK_SMUDGE, N_( "Smudge" ), 0, 0, GETTEXT_PACKAGE },
696 		{ STOCK_FLOOD, N_( "Flood" ), 0, 0, GETTEXT_PACKAGE },
697 		{ STOCK_FLOOD_BLOB, N_( "Flood Blob" ), 0, 0, GETTEXT_PACKAGE },
698 		{ STOCK_RECT, N_( "Fill Rectangle" ), 0, 0, GETTEXT_PACKAGE },
699 		{ STOCK_MOVE, N_( "Pan" ), 0, 0, GETTEXT_PACKAGE },
700 		{ STOCK_SELECT, N_( "Select" ), 0, 0, GETTEXT_PACKAGE },
701 		{ STOCK_LOCK, N_( "Locked" ), 0, 0, GETTEXT_PACKAGE },
702 
703 		/* And the LEDs we use.
704 		 */
705 		{ STOCK_LED_RED, N_( "Red LED" ), 0, 0, GETTEXT_PACKAGE },
706 		{ STOCK_LED_GREEN, N_( "Green LED" ), 0, 0, GETTEXT_PACKAGE },
707 		{ STOCK_LED_BLUE, N_( "Blue LED" ), 0, 0, GETTEXT_PACKAGE },
708 		{ STOCK_LED_YELLOW, N_( "Yellow LED" ), 0, 0, GETTEXT_PACKAGE },
709 		{ STOCK_LED_CYAN, N_( "Cyan LED" ), 0, 0, GETTEXT_PACKAGE },
710 		{ STOCK_LED_OFF, N_( "Off LED" ), 0, 0, GETTEXT_PACKAGE }
711 	};
712 
713 	GtkIconSet *icon_set;
714 
715 	gtk_stock_add_static( stock_item, IM_NUMBER( stock_item ) );
716 	main_icon_factory = gtk_icon_factory_new();
717 
718 	/* Make a colour picker stock ... take the stock icon and add our own
719 	 * text (gtk defines no text for the standard version of this stock
720 	 * icon).
721 	 */
722 	icon_set = gtk_icon_factory_lookup_default( GTK_STOCK_COLOR_PICKER );
723 	gtk_icon_factory_add( main_icon_factory, STOCK_DROPPER, icon_set );
724 
725 	/* For Next Error, use JUMP_TO.
726 	 */
727 	icon_set = gtk_icon_factory_lookup_default( GTK_STOCK_JUMP_TO );
728 	gtk_icon_factory_add( main_icon_factory, STOCK_NEXT_ERROR, icon_set );
729 
730 	/* For clone, use the DND_MULTIPLE icon (close enough).
731 	 */
732 	icon_set = gtk_icon_factory_lookup_default( GTK_STOCK_DND_MULTIPLE );
733 	gtk_icon_factory_add( main_icon_factory, STOCK_DUPLICATE, icon_set );
734 
735 	/* Link to our stock .pngs.
736 	 */
737 	main_file_for_stock( main_icon_factory,
738 		STOCK_PAINTBRUSH, "stock-tool-ink-22.png" );
739 	main_file_for_stock( main_icon_factory,
740 		STOCK_LINE, "stock-tool-path-22.png" );
741 	main_file_for_stock( main_icon_factory,
742 		STOCK_TEXT, "stock-tool-text-22.png" );
743 	main_file_for_stock( main_icon_factory,
744 		STOCK_SMUDGE, "stock-tool-smudge-22.png" );
745 	main_file_for_stock( main_icon_factory,
746 		STOCK_FLOOD, "stock-tool-bucket-fill-22.png" );
747 	main_file_for_stock( main_icon_factory,
748 		STOCK_FLOOD_BLOB, "stock-tool-bucket-fill-22.png" );
749 	main_file_for_stock( main_icon_factory,
750 		STOCK_RECT, "stock-tool-rect-select-22.png" );
751 	main_file_for_stock( main_icon_factory,
752 		STOCK_MOVE, "stock-tool-move-22.png" );
753 	main_file_for_stock( main_icon_factory,
754 		STOCK_SELECT, "stock-tool-select-22.png" );
755 	main_file_for_stock( main_icon_factory,
756 		STOCK_LOCK, "stock-padlock-closed-22.png" );
757 	main_file_for_stock( main_icon_factory,
758 		STOCK_ALERT, "stock-alert-22.png" );
759 	main_file_for_stock( main_icon_factory,
760 		STOCK_LED_RED, "stock-led-red-18.png" );
761 	main_file_for_stock( main_icon_factory,
762 		STOCK_LED_GREEN, "stock-led-green-18.png" );
763 	main_file_for_stock( main_icon_factory,
764 		STOCK_LED_BLUE, "stock-led-blue-18.png" );
765 	main_file_for_stock( main_icon_factory,
766 		STOCK_LED_YELLOW, "stock-led-yellow-18.png" );
767 	main_file_for_stock( main_icon_factory,
768 		STOCK_LED_CYAN, "stock-led-cyan-18.png" );
769 	main_file_for_stock( main_icon_factory,
770 		STOCK_LED_OFF, "stock-led-off-18.png" );
771 
772 	gtk_icon_factory_add_default( main_icon_factory );
773 	g_object_unref( main_icon_factory );
774 }
775 
776 /* Init the display connection stuff.
777  */
778 static void
main_x_init(int * argc,char *** argv)779 main_x_init( int *argc, char ***argv )
780 {
781 	char buf[FILENAME_MAX];
782 
783 #ifdef DEBUG
784 	printf( "X11 init\n" );
785 #endif/*DEBUG*/
786 
787 	(void) calli_string_filename(
788 		(calli_string_fn) gtk_rc_add_default_file,
789 			"$VIPSHOME" G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S
790 			PACKAGE G_DIR_SEPARATOR_S "rc" G_DIR_SEPARATOR_S
791 			"ipgtkrc", NULL, NULL, NULL );
792 	gtk_init( argc, argv );
793 
794 	/* Set the default icon.
795 	 */
796 	im_strncpy( buf,
797 		"$VIPSHOME/share/$PACKAGE/data/vips-128.png", FILENAME_MAX );
798 	path_expand( buf );
799 	gtk_window_set_default_icon_from_file( buf, NULL );
800 
801 	/* Turn off startup notification. Startup is done when we pop our
802 	 * first window, not when we make this secret window.
803 	 */
804 	gtk_window_set_auto_startup_notification( FALSE );
805 
806 #ifdef DEBUG_UPDATES
807 	printf( "*** debug updates is on\n" );
808 	gdk_window_set_debug_updates( TRUE );
809 #endif /*DEBUG_UPDATES*/
810 
811 	main_register_icons();
812 
813 	/* Next window we make is end of startup.
814 	 */
815 	gtk_window_set_auto_startup_notification( TRUE );
816 
817 	/* Load up any saved accelerators.
818 	 */
819 	calli_string_filenamef( (calli_string_fn) gtk_accel_map_load,
820 		"%s" G_DIR_SEPARATOR_S "accel_map", get_savedir() );
821 }
822 
823 static void *
main_toobig_done_sub(const char * filename)824 main_toobig_done_sub( const char *filename )
825 {
826 	unlinkf( "%s", filename );
827 
828 	return( NULL );
829 }
830 
831 /* OK in "flush temps" yesno.
832  */
833 static void
main_toobig_done(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)834 main_toobig_done( iWindow *iwnd,
835 	void *client, iWindowNotifyFn nfn, void *sys )
836 {
837 	/* Don't "rm *", too dangerous.
838 	 */
839 	path_map_dir( PATH_TMP, "*.v",
840 		(path_map_fn) main_toobig_done_sub, NULL );
841 	path_map_dir( PATH_TMP, "*.ws",
842 		(path_map_fn) main_toobig_done_sub, NULL );
843 
844 	/* _stdenv.def:magick can generate .tif files.
845 	 */
846 	path_map_dir( PATH_TMP, "*.tif",
847 		(path_map_fn) main_toobig_done_sub, NULL );
848 
849 	/* autotrace can make some others.
850 	 */
851 	path_map_dir( PATH_TMP, "*.ppm",
852 		(path_map_fn) main_toobig_done_sub, NULL );
853 	path_map_dir( PATH_TMP, "*.svg",
854 		(path_map_fn) main_toobig_done_sub, NULL );
855 
856 	/* Tell space-free indicators to update.
857 	 */
858 	if( main_imageinfogroup )
859 		iobject_changed( IOBJECT( main_imageinfogroup ) );
860 
861 	nfn( sys, IWINDOW_YES );
862 }
863 
864 /* Test for a bunch of stuff in the TMP area. Need to do this before
865  * we load args in case there are large JPEGs there. Only bother in
866  * interactive mode: we won't be able to question the user without an
867  * X connection.
868  */
869 static void
main_check_temp(double total)870 main_check_temp( double total )
871 {
872 	if( total > 10 * 1024 * 1024 ) {
873 		char txt[256];
874 		VipsBuf buf = VIPS_BUF_STATIC( txt );
875 		char tmp[FILENAME_MAX];
876 
877 		im_strncpy( tmp, PATH_TMP, FILENAME_MAX );
878 		path_expand( tmp );
879 		vips_buf_append_size( &buf, total );
880 
881 		box_yesno( NULL,
882 			main_toobig_done, iwindow_true_cb, NULL,
883 			NULL, NULL,
884 			_( "Empty temp area" ),
885 			_( "Many files in temp area." ),
886 			_( "The temp area \"%s\" contains %s of files. "
887 			"Would you like to empty the temp area? "
888 			"This will delete any workspace backups and "
889 			"cannot be undone." ),
890 			tmp, vips_buf_all( &buf ) );
891 	}
892 }
893 
894 /* Make sure a savedir exists. Used to build the "~/.nip2-xx/tmp" etc.
895  * directory tree.
896  */
897 static void
main_mkdir(const char * dir)898 main_mkdir( const char *dir )
899 {
900 	if( !existsf( "%s" G_DIR_SEPARATOR_S "%s", get_savedir(), dir ) )
901 		if( !mkdirf( "%s" G_DIR_SEPARATOR_S "%s", get_savedir(), dir ) )
902 			error_exit( _( "unable to make %s %s: %s" ),
903 				get_savedir(), dir, g_strerror( errno ) );
904 }
905 
906 static gboolean
main_set(const char * str)907 main_set( const char *str )
908 {
909 	Symbol *sym;
910 
911 	attach_input_string( str );
912 	if( !(sym = parse_set_symbol()) )
913 		return( FALSE );
914 
915 	/* Put the input just after the '=', ready to parse a RHS into the
916 	 * symbol.
917 	 */
918 	attach_input_string( str +
919 		IM_CLIP( 0, input_state.charpos - 1, strlen( str ) ) );
920 
921 	if( !symbol_user_init( sym ) ||
922 		!parse_rhs( sym->expr, PARSE_RHS ) ) {
923 		/* Another parse error.
924 		 */
925 		expr_error_get( sym->expr );
926 
927 		/* Block changes to error_string ... symbol_destroy()
928 		 * can set this for compound objects.
929 		 */
930 		error_block();
931 		IDESTROY( sym );
932 		error_unblock();
933 
934 		return( FALSE );
935 	}
936 
937 	symbol_made( sym );
938 
939 	/* Is there a row? Make sure any modified text there can't zap our new
940 	 * text.
941 	 */
942 	if( sym->expr->row ) {
943 		Row *row = sym->expr->row;
944 
945 		heapmodel_set_modified(
946 			HEAPMODEL( row->child_rhs->itext ), FALSE );
947 	}
948 
949 	return( TRUE );
950 }
951 
952 static char prefix_buffer[FILENAME_MAX];
953 static gboolean prefix_valid = FALSE;
954 
955 /* Override the install guess from vips. Handy for testing.
956  */
957 static void
set_prefix(const char * prefix)958 set_prefix( const char *prefix )
959 {
960 	im_strncpy( prefix_buffer, prefix, FILENAME_MAX );
961 	nativeize_path( prefix_buffer );
962 	absoluteize_path( prefix_buffer );
963 	setenvf( "VIPSHOME", "%s", prefix_buffer );
964 	prefix_valid = TRUE;
965 }
966 
967 /* Guess VIPSHOME, if we can.
968  */
969 const char *
get_prefix(void)970 get_prefix( void )
971 {
972 	if( !prefix_valid ) {
973 		const char *prefix;
974 
975 		if( !(prefix = im_guess_prefix( main_argv0, "VIPSHOME" )) ) {
976 			error_top( _( "Unable to find install area." ) );
977 			error_vips();
978 
979 			return( NULL );
980 		}
981 
982 		set_prefix( prefix );
983 	}
984 
985 	return( prefix_buffer );
986 }
987 
988 /* Start here!
989  */
990 int
main(int argc,char * argv[])991 main( int argc, char *argv[] )
992 {
993 	gboolean welcome_message = FALSE;
994 	Workspacegroup *wsg;
995 	Workspace *ws;
996 	GError *error = NULL;
997 	GOptionContext *context;
998 	const char *prefix;
999 	int i;
1000 	double total = 0.0;
1001 #ifdef HAVE_GETRLIMIT
1002 	struct rlimit rlp;
1003 #endif /*HAVE_GETRLIMIT*/
1004 	char name[256];
1005 #if HAVE_FFTW || HAVE_FFTW3
1006 	iOpenFile *of;
1007 #endif /*HAVE_FFTW*/
1008 	Toolkit *kit;
1009 	char txt[MAX_STRSIZE];
1010 	VipsBuf buf = VIPS_BUF_STATIC( txt );
1011 
1012 #ifdef DEBUG_TIME
1013 	GTimer *startup_timer = g_timer_new();
1014 	printf( "DEBUG_TIME: startup timer zeroed ...\n" );
1015 #endif /*DEBUG_TIME*/
1016 
1017 	/* In startup phase.
1018 	 */
1019 	main_starting = TRUE;
1020 
1021 	/* Want numeric locale to be "C", so we have C rules for doing
1022 	 * double <-> string (ie. no "," for decimal point).
1023 	 */
1024 	setlocale( LC_ALL, "" );
1025 	setlocale( LC_NUMERIC, "C" );
1026 
1027 	/* Make sure our LC_NUMERIC setting is not trashed.
1028  	 */
1029 	gtk_disable_setlocale();
1030 
1031 #ifdef DEBUG
1032 	printf( "main: sizeof( HeapNode ) == %zd\n", sizeof( HeapNode ) );
1033 
1034 	/* Should be 3 pointers, hopefully.
1035 	 */
1036 	if( sizeof( HeapNode ) != 3 * sizeof( void * ) )
1037 		printf( "*** struct packing problem!\n" );
1038 #endif/*DEBUG*/
1039 
1040 	/* Yuk .. shouldn't really write to argv0. This can't change the
1041 	 * string length.
1042 	 *
1043 	 * On win32 we will sometimes get paths with mixed '/' and '\' which
1044 	 * confuses vips's prefix guessing. Make sure we have one or the other.
1045 	 */
1046 	nativeize_path( argv[0] );
1047 
1048 	main_argv0 = argv[0];
1049 	main_c_stack_base = &argc;
1050 
1051 	/* Pass config.h stuff down to .ws files.
1052 	 */
1053 	setenvf( "PACKAGE", "%s", PACKAGE );
1054 	setenvf( "VERSION", "%s", VERSION );
1055 
1056 #ifdef OS_WIN32
1057 {
1058         /* No HOME on windows ... make one from HOMEDRIVE and HOMEDIR (via
1059          * glib).
1060          */
1061 	const char *home;
1062 	char buf[FILENAME_MAX];
1063 
1064 	if( !(home = g_getenv( "HOME" )) )
1065 		home = g_get_home_dir();
1066 
1067 	/* We need native paths.
1068 	 */
1069 	strncpy( buf, home, FILENAME_MAX );
1070 	nativeize_path( buf );
1071 	setenvf( "HOME", "%s", buf );
1072 }
1073 #endif /*OS_WIN32*/
1074 
1075 	/* Name of the dir we store our config stuff in. This can get used by
1076 	 * Preferences.ws.
1077 	 */
1078 	setenvf( "SAVEDIR", "%s", get_savedir() );
1079 
1080 	/* Path separator on this platform.
1081 	 */
1082 	setenvf( "SEP", "%s", G_DIR_SEPARATOR_S );
1083 
1084 	/* Executable file extension (eg. ".exe" on Windows).
1085 	 */
1086 	setenvf( "EXEEXT", "%s", VIPS_EXEEXT );
1087 
1088 	/* Start up vips.
1089 	 */
1090 	if( im_init_world( main_argv0 ) )
1091 		error_exit( "unable to start VIPS" );
1092 
1093 	/* The vips8 cache is no use to us. We have our own cache which is
1094 	 * integrated with our invalidate system.
1095 	 */
1096 	vips_cache_set_max( 0 );
1097 
1098 	/* Init i18n ... get catalogues from $VIPSHOME/share/locale so we're
1099 	 * relocatable.
1100 	 */
1101 	prefix = get_prefix();
1102 	im_snprintf( name, 256,
1103 		"%s" G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S "locale",
1104 		prefix );
1105 #ifdef DEBUG
1106 	printf( "bindtextdomain: %s\n", name );
1107 #endif /*DEBUG*/
1108 	textdomain( GETTEXT_PACKAGE );
1109 	bindtextdomain( GETTEXT_PACKAGE, name );
1110 	bind_textdomain_codeset( GETTEXT_PACKAGE, "UTF-8" );
1111 
1112 	/* Set localised application name.
1113 	 */
1114 	g_set_application_name( _( PACKAGE ) );
1115 
1116 	context = g_option_context_new( _( "- image processing spreadsheet" ) );
1117 	g_option_context_add_main_entries( context,
1118 		main_option, GETTEXT_PACKAGE );
1119 
1120 	/* Don't start X here! We may be in batch mode.
1121 	 */
1122 	g_option_context_add_group( context, gtk_get_option_group( FALSE ) );
1123 	g_option_context_add_group( context, im_get_option_group() );
1124 
1125 	if( !g_option_context_parse( context, &argc, &argv, &error ) )
1126 		vfatal( &error );
1127 
1128 	g_option_context_free( context );
1129 
1130 	/* Override the install guess from vips. This won't pick up msg
1131 	 * cats sadly :( since we have to init i18n before arg parsing. Handy
1132 	 * for testing without installing.
1133 	 */
1134 	if( main_option_prefix )
1135 		set_prefix( main_option_prefix );
1136 
1137 	if( main_option_version ) {
1138 		printf( "%s-%s", PACKAGE, VERSION );
1139 		printf( "\n" );
1140 
1141 		printf( _( "linked to vips-%s" ), im_version_string() );
1142 		printf( "\n" );
1143 
1144 		exit( 0 );
1145 	}
1146 
1147 #ifdef DEBUG_FATAL
1148 	/* Set masks for debugging ... stop on any problem.
1149 	 */
1150 	g_log_set_always_fatal(
1151 		G_LOG_FLAG_RECURSION |
1152 		G_LOG_FLAG_FATAL |
1153 		G_LOG_LEVEL_ERROR |
1154 		G_LOG_LEVEL_CRITICAL |
1155 		G_LOG_LEVEL_WARNING );
1156 #else /*!DEBUG_FATAL*/
1157 #ifdef OS_WIN32
1158 	/* No logging output ... on win32, log output pops up a very annoying
1159  	 * console text box.
1160 	 */
1161 	g_log_set_handler( "GLib",
1162 		G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
1163 		main_log_null, NULL );
1164 	g_log_set_handler( "Gtk",
1165 		G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
1166 		main_log_null, NULL );
1167 	g_log_set_handler( NULL,
1168 		G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
1169 		main_log_null, NULL );
1170 #endif /*OS_WIN32*/
1171 #endif /*DEBUG_FATAL*/
1172 
1173 	main_stdin = ifile_open_read_stdin();
1174 
1175 #ifdef HAVE_GETRLIMIT
1176 	/* Make sure we have lots of file descriptors. Some platforms have cur
1177 	 * as 256 and max at 1024 to keep stdio happy.
1178 	 */
1179 	if( getrlimit( RLIMIT_NOFILE, &rlp ) == 0 ) {
1180 		rlim_t old_limit = rlp.rlim_cur;
1181 
1182 		rlp.rlim_cur = rlp.rlim_max;
1183 		if( setrlimit( RLIMIT_NOFILE, &rlp ) == 0 ) {
1184 #ifdef DEBUG
1185 			printf( "set max file descriptors to %d\n",
1186 				(int) rlp.rlim_max );
1187 #endif /*DEBUG*/
1188 		}
1189 		else if( (int) rlp.rlim_max != -1 ) {
1190 			/* -1 means can't-be-set, at least on os x, so don't
1191 			 * warn.
1192 			 */
1193 			g_warning( _( "unable to change max file descriptors\n"
1194 				"max file descriptors still set to %d" ),
1195 				(int) old_limit );
1196 		}
1197 	}
1198 	else {
1199 		g_warning( _( "unable to read max file descriptors" ) );
1200 	}
1201 #endif /*HAVE_GETRLIMIT*/
1202 
1203 	/* Make our file types.
1204 	 */
1205 	filesel_startup();
1206 
1207 	/* Set default values for paths.
1208 	 */
1209 	path_init();
1210 
1211 	/* First time we've been run? Welcome message.
1212 	 */
1213 	if( !existsf( "%s", get_savedir() ) )
1214 		welcome_message = TRUE;
1215 
1216 	/* Always make these in case some got deleted.
1217 	 */
1218 	main_mkdir( "" );
1219 	main_mkdir( "tmp" );
1220 	main_mkdir( "start" );
1221 	main_mkdir( "data" );
1222 
1223 	/* Init other stuff.
1224 	 */
1225 #ifdef HAVE_FFTW3
1226 	fftw_import_system_wisdom();
1227 #endif /*HAVE_FFTW3*/
1228 #if HAVE_FFTW || HAVE_FFTW3
1229 	if( (of = ifile_open_read( "%s" G_DIR_SEPARATOR_S "wisdom",
1230 		get_savedir() )) ) {
1231 		fftw_import_wisdom_from_file( of->fp );
1232 		ifile_close( of );
1233 	}
1234 #endif /*HAVE_FFTW*/
1235 
1236 	mainw_startup();
1237 	reduce_context = reduce_new();
1238 	main_symbol_root = symbol_root_init();
1239 	g_object_ref( G_OBJECT( main_symbol_root ) );
1240 	iobject_sink( IOBJECT( main_symbol_root ) );
1241 	model_base_init();
1242 	main_workspaceroot = workspaceroot_new( "Workspaces" );
1243 	g_object_ref( G_OBJECT( main_workspaceroot ) );
1244 	iobject_sink( IOBJECT( main_workspaceroot ) );
1245 	main_watchgroup = watchgroup_new( main_workspaceroot, "Preferences" );
1246 	g_object_ref( G_OBJECT( main_watchgroup ) );
1247 	iobject_sink( IOBJECT( main_watchgroup ) );
1248 	main_toolkitgroup = toolkitgroup_new( symbol_root );
1249 	g_object_ref( G_OBJECT( main_toolkitgroup ) );
1250 	iobject_sink( IOBJECT( main_toolkitgroup ) );
1251 	main_imageinfogroup = imageinfogroup_new();
1252 	g_object_ref( G_OBJECT( main_imageinfogroup ) );
1253 	iobject_sink( IOBJECT( main_imageinfogroup ) );
1254 
1255 	/* First pass at command-line options. Just look at the flags that
1256 	 * imply other flags, don't do any processing yet.
1257 	 */
1258 	if( main_option_script ) {
1259 		main_option_batch = TRUE;
1260 		main_option_no_load_menus = TRUE;
1261 		main_option_no_load_args = TRUE;
1262 		main_option_print_main = TRUE;
1263 	}
1264 
1265 	if( main_option_test ) {
1266 		main_option_batch = TRUE;
1267 		main_option_verbose = TRUE;
1268 	}
1269 
1270 	if( main_option_expression ) {
1271 		main_option_batch = TRUE;
1272 		main_option_no_load_menus = TRUE;
1273 		main_option_no_load_args = TRUE;
1274 		main_option_print_main = TRUE;
1275 	}
1276 
1277 	if( main_option_benchmark ) {
1278 		main_option_batch = TRUE;
1279 		main_option_no_load_menus = FALSE;
1280 	}
1281 
1282 	if( main_option_i18n ) {
1283 		/* Just start up and shutdown, no X. Output constant
1284 		 * i18n strings.
1285 		 */
1286 		main_option_batch = TRUE;
1287 		main_option_no_load_menus = FALSE;
1288 	}
1289 
1290 #ifdef DEBUG
1291 	if( main_option_batch )
1292 		printf( "non-interactive mode\n" );
1293 #endif /*DEBUG*/
1294 
1295 	/* Start the X connection. We need this before _load_all(), so that
1296 	 * we can pop up error dialogs.
1297 	 */
1298 	if( !main_option_batch )
1299 		main_x_init( &argc, &argv );
1300 
1301 #ifdef HAVE_LIBGOFFICE
1302         libgoffice_init();
1303 	go_plugins_init( NULL, NULL, NULL, NULL, TRUE,
1304 		GO_TYPE_PLUGIN_LOADER_MODULE );
1305 #endif /*HAVE_LIBGOFFICE*/
1306 
1307 	/* Load start-up stuff. Builtins, plugins, externals etc. We need to
1308 	 * do this before we load any user code so we can prevent redefinition
1309 	 * of builtins.
1310 	 */
1311 	main_load_startup();
1312 
1313 	/* Recalc to build all classes and gets prefs working.
1314 	 *
1315 	 * We have to do this in batch
1316 	 * mode since we can find dirties through dynamic lookups. Even though
1317 	 * you might think we could just follow recomps.
1318 	 */
1319 	symbol_recalculate_all_force( TRUE );
1320 
1321 #ifdef DEBUG
1322 	printf( "arg processing\n" );
1323 #endif/*DEBUG*/
1324 
1325 	/* Might make this from stdin/whatever if we have a special
1326 	 * command-line flag.
1327 	 */
1328 	wsg = NULL;
1329 	ws = NULL;
1330 
1331 	/* Second command-line pass. This time we do any actions.
1332 	 */
1333 	if( main_option_script ) {
1334 		if( !toolkit_new_from_file( main_toolkitgroup,
1335 			main_option_script ) )
1336 			main_log_add( "%s\n", error_get_sub() );
1337 	}
1338 
1339 	if( main_option_expression ) {
1340 		kit = toolkit_new( main_toolkitgroup, "_expression" );
1341 
1342 		vips_buf_appendf( &buf, "main = %s;", main_option_expression );
1343 		attach_input_string( vips_buf_all( &buf ) );
1344 		(void) parse_onedef( kit, -1 );
1345 
1346 		filemodel_set_modified( FILEMODEL( kit ), FALSE );
1347 	}
1348 
1349 	if( main_option_stdin_def ) {
1350 		if( !(kit = toolkit_new_from_openfile(
1351 			main_toolkitgroup, main_stdin )) )
1352 			main_log_add( "%s\n", error_get_sub() );
1353 	}
1354 
1355 	if( main_option_stdin_ws ) {
1356 		if( !(wsg = workspacegroup_new_from_openfile(
1357 			main_workspaceroot, main_stdin )) )
1358 			main_log_add( "%s\n", error_get_sub() );
1359 		else
1360 			/* Don't want to have "stdin" as the filename.
1361 			 */
1362 			filemodel_set_filename( FILEMODEL( wsg ), NULL );
1363 	}
1364 
1365 	/* Make a start workspace and workspacegroup to load
1366 	 * stuff into.
1367 	 */
1368 	if( !wsg ) {
1369 		wsg = workspacegroup_new_blank( main_workspaceroot, NULL );
1370 		ws = WORKSPACE( icontainer_get_nth_child(
1371 			ICONTAINER( wsg ), 0 ) );
1372 	}
1373 
1374 	/* Reset IM_CONCURRENCY if a watch changes. Need to do this after
1375 	 * parsing options so we skip in batch mode.
1376 	 */
1377 	g_signal_connect( main_watchgroup, "watch_changed",
1378 		G_CALLBACK( main_watchgroup_changed_cb ), NULL );
1379 
1380 	/* Pass PATH_TMP down to vips via TMPDIR. See im_system(), for
1381 	 * example. We need to do this after the first recomp so that prefs
1382 	 * are loaded.
1383 	 */
1384 {
1385 	char buf[FILENAME_MAX];
1386 
1387 	im_strncpy( buf, PATH_TMP, FILENAME_MAX );
1388 	path_expand( buf );
1389 	setenvf( "TMPDIR", "%s", buf );
1390 
1391 	path_rewrite_add( PATH_TMP, "$TMPDIR", TRUE );
1392 }
1393 
1394 	/* Measure amount of stuff in temp area ... need this for checking
1395 	 * temps later. We pop a dialog if there are too many, so only useful
1396 	 * in interactive mode.
1397 	 */
1398 	if( !main_option_batch )
1399 		total = directory_size( PATH_TMP );
1400 
1401 	/* Make nip's argc/argv[].
1402 	 */
1403 	kit = toolkit_new( main_toolkitgroup, "_args" );
1404 	vips_buf_rewind( &buf );
1405 	vips_buf_appendf( &buf, "argc = %d;", argc );
1406 	attach_input_string( vips_buf_all( &buf ) );
1407 	(void) parse_onedef( kit, -1 );
1408 
1409 	vips_buf_rewind( &buf );
1410 	vips_buf_appendf( &buf, "argv = [" );
1411 	for( i = 0; i < argc; i++ ) {
1412 		/* Ignore "--" args. Consider eg.
1413 		 *
1414 		 * 	./try201.nip2 -o x.v -- -12 ~/pics/shark.jpg
1415 		 *
1416 		 * if we didn't remove --, all scripts would need to.
1417 		 */
1418 		if( strcmp( argv[i], "--" ) == 0 )
1419 			continue;
1420 
1421 		if( i > 0 )
1422 			vips_buf_appendf( &buf, ", " );
1423 		vips_buf_appendf( &buf, "\"%s\"", argv[i] );
1424 	}
1425 	vips_buf_appendf( &buf, "];" );
1426 
1427 	attach_input_string( vips_buf_all( &buf ) );
1428 	if( !parse_onedef( kit, -1 ) )
1429 		main_log_add( "%s\n", error_get_sub() );
1430 
1431 	filemodel_set_modified( FILEMODEL( kit ), FALSE );
1432 
1433 	/* Double-check: we often forget to move the prefs ws to the latest
1434 	 * version.
1435 	 */
1436 #ifdef DEBUG_LEAK
1437 {
1438 	Symbol *wsr_sym = main_workspaceroot->sym;
1439 	Symbol *ws_sym = SYMBOL( icontainer_child_lookup(
1440 		ICONTAINER( wsr_sym->expr->compile ), "Preferences" ) );
1441 
1442 	if( !ws_sym )
1443 		printf( "No prefs workspace!\n" );
1444 	else {
1445 		Workspace *ws = ws_sym->ws;
1446 
1447 		if( ws->compat_major ||
1448 			ws->compat_minor )
1449 			printf( "Preferences loaded in compat mode!\n" );
1450 	}
1451 }
1452 #endif /*DEBUG_LEAK*/
1453 
1454 	if( !main_option_no_load_args ) {
1455 		/* Load args as files, if we can.
1456 		 */
1457 		for( i = 1; i < argc; i++ ) {
1458 			char buf[FILENAME_MAX];
1459 
1460 			/* We want to use the absolute, compact form of the
1461 			 * filename object so we don't get a dependency on CWD.
1462 			 */
1463 			im_strncpy( buf, argv[i], FILENAME_MAX );
1464 			path_compact( buf );
1465 
1466 			if( !main_load( ws, buf ) )
1467 				main_log_add( "%s\n", error_get_sub() );
1468 		}
1469 	}
1470 
1471 	/* In batch mode give up if there are startup errors.
1472 	 */
1473 	if( main_option_batch ) {
1474 		if( !main_log_is_empty() ) {
1475 			fprintf( stderr, _( "Startup error log:\n%s" ),
1476 				main_log_get() );
1477 			exit( 1 );
1478 		}
1479 	}
1480 
1481 	if( main_option_set ) {
1482 		int i;
1483 
1484 		for( i = 0; main_option_set[i]; i++ ) {
1485 			if( main_option_verbose )
1486 				printf( "main_set: %s\n", main_option_set[i] );
1487 
1488 			if( !main_set( main_option_set[i] ) )
1489 				main_log_add( "%s\n%s",
1490 					error_get_top(), error_get_sub() );
1491 		}
1492 	}
1493 
1494 	/* Make sure our start ws doesn't have modified set. We may have
1495 	 * loaded some images or whatever into it.
1496 	 */
1497 	workspace_set_modified( ws, FALSE );
1498 
1499 	/* If the start ws is empty (we didn't load anything into it) and we
1500 	 * loaded some other workspaces, we can junk the empty ws.
1501 	 */
1502 	if( icontainer_get_n_children( ICONTAINER( main_workspaceroot ) ) > 2 &&
1503 		workspace_is_empty( ws ) ) {
1504 		IDESTROY( wsg );
1505 		wsg = NULL;
1506 		ws = NULL;
1507 	}
1508 
1509 #ifdef DEBUG_TIME
1510 	printf( "DEBUG_TIME: main init in %gs\n",
1511 		g_timer_elapsed( startup_timer, NULL ) );
1512 #endif /*DEBUG_TIME*/
1513 
1514 	/* Are we running interactively? Start the main window and loop.
1515 	 */
1516 	if( !main_option_batch ) {
1517 		if( wsg ) {
1518 			Mainw *mainw;
1519 
1520 			mainw = mainw_new( wsg );
1521 			gtk_widget_show( GTK_WIDGET( mainw ) );
1522 		}
1523 
1524 		/* Process a few events ... we want the window to be mapped so
1525 		 * that log/welcome/clean? messages we pop appear in the right
1526 		 * place on the screen.
1527 		 */
1528 		while( g_main_context_iteration( NULL, FALSE ) )
1529 			;
1530 
1531 		if( !main_log_is_empty() ) {
1532 			error_top( _( "Startup error." ) );
1533 			error_sub( _( "Startup error log:\n%s" ),
1534 				main_log_get() );
1535 			iwindow_alert( NULL, GTK_MESSAGE_ERROR );
1536 		}
1537 
1538 		if( welcome_message ) {
1539 			char save_dir[FILENAME_MAX];
1540 			char buf[256];
1541 
1542 			im_snprintf( buf, 256,
1543 				_( "Welcome to %s-%s!" ), PACKAGE, VERSION );
1544 			im_strncpy( save_dir, get_savedir(), FILENAME_MAX );
1545 			path_expand( save_dir );
1546 			error_top( "%s", buf );
1547 			error_sub(
1548 _( "A new directory has been created to hold startup, "
1549 "data and temporary files:\n\n"
1550 "     %s\n\n"
1551 "If you've used previous versions of %s, you might want "
1552 "to copy files over from your old work area." ),
1553 				save_dir, PACKAGE );
1554 			iwindow_alert( NULL, GTK_MESSAGE_INFO );
1555 		}
1556 
1557 		main_check_temp( total );
1558 
1559 #ifdef DEBUG
1560 		printf( "starting event dispatch loop\n" );
1561 #endif/*DEBUG*/
1562 
1563 		main_starting = FALSE;
1564 
1565 		symbol_recalculate_all_force( FALSE );
1566 
1567 		gtk_main();
1568 	}
1569 
1570 	if( main_option_test ) {
1571 		/* Make sure we've had at least one recomp.
1572 		 */
1573 		symbol_recalculate_all_force( TRUE );
1574 		if( expr_error_all )
1575 			main_error_exit( "--test: errors found" );
1576 	}
1577 
1578 	/* No return from this.
1579 	 */
1580 	main_quit();
1581 
1582 	return( 0 );
1583 }
1584 
1585 #ifdef OS_WIN32
1586 /* Get non-cmd line args on win32.
1587  */
1588 static int
breakargs(char * program,char * line,char ** argv)1589 breakargs( char *program, char *line, char **argv )
1590 {
1591 	int argc = 1;
1592 
1593 	argv[0] = program;
1594 
1595 	while( *line && argc < MAX_SYSTEM - 1 ) {
1596 		while( *line && isspace( *line ) )
1597 			line++;
1598 
1599 		if( *line == '"' ) {
1600 			/* Windows-95 quoted arguments
1601 			 */
1602 			char *start = line + 1;
1603 			char *end = start;
1604 
1605 			while( *end && *end != '"' )
1606 				end++;
1607 
1608 			if( *end == '"' ) {
1609 				*end = '\0';
1610 				argv[argc++] = start;
1611 				line = end + 1;
1612 				continue;
1613 			}
1614 		}
1615 
1616 		if( *line ) {
1617 			argv[argc++] = line;
1618 			while( *line && !isspace( *line ) )
1619 				line++;
1620 
1621 			if( *line )
1622 				*line++ = '\0';
1623 		}
1624 	}
1625 
1626 	/* add trailing NULL pointer to argv
1627 	 */
1628 	argv[argc] = NULL;
1629 
1630 	return( argc );
1631 }
1632 
1633 int WINAPI
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpszCmdLine,int nShowCmd)1634 WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
1635 	LPSTR lpszCmdLine, int nShowCmd )
1636 {
1637 	char *argv[MAX_SYSTEM];
1638 	int  argc;
1639 	TCHAR program[MAXPATHLEN];
1640 
1641 	GetModuleFileName( hInstance, program, sizeof(program) );
1642 	argc = breakargs( (char *) program, lpszCmdLine, argv );
1643 
1644 	return( main( argc, argv ) );
1645 }
1646 #endif /*OS_WIN32*/
1647