1 /* Some basic util functions.
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 /*
31 #define DEBUG
32  */
33 
34 /* Handy for tracing errors.
35 #define DEBUG_ERROR
36  */
37 
38 /* Prettyprint xml save files.
39 
40  	FIXME ... slight mem leak, and save files are much larger ... but
41 	leave it on for convenience
42 
43  */
44 #define DEBUG_SAVEFILE
45 
46 #include "ip.h"
47 
48 /* Track errors messages in this thing. We keep two messages: a principal
49  * error, and extra informative text. For example "Unable to load file.",
50  * "Read failed for file blah, permission denied.".
51  */
52 static char error_top_text[MAX_STRSIZE];
53 static char error_sub_text[MAX_STRSIZE];
54 
55 VipsBuf error_top_buf = VIPS_BUF_STATIC( error_top_text );
56 VipsBuf error_sub_buf = VIPS_BUF_STATIC( error_sub_text );
57 
58 /* Useful: Error message and quit. Emergencies only ... we don't tidy up
59  * properly.
60  */
61 void
error(const char * fmt,...)62 error( const char *fmt, ... )
63 {
64 	va_list args;
65 
66 	fprintf( stderr, IP_NAME ": " );
67 
68         va_start( args, fmt );
69         (void) vfprintf( stderr, fmt, args );
70         va_end( args );
71 
72 	fprintf( stderr, "\n" );
73 
74 #ifdef DEBUG
75 	/* Make a coredump.
76 	 */
77 	abort();
78 #endif /*DEBUG*/
79 
80 	exit( 1 );
81 }
82 
83 /* Set this to block error messages. Useful if we've found an error, we want
84  * to clean up, but we don't want any of the clean-up code to touch the error
85  * buffer.
86  */
87 static int error_level = 0;
88 
89 void
error_block(void)90 error_block( void )
91 {
92 	error_level++;
93 }
94 
95 void
error_unblock(void)96 error_unblock( void )
97 {
98 	g_assert( error_level );
99 
100 	error_level--;
101 }
102 
103 /* Set an error buffer.
104  */
105 static void
error_set(VipsBuf * buf,const char * fmt,va_list ap)106 error_set( VipsBuf *buf, const char *fmt, va_list ap )
107 {
108 	if( !error_level ) {
109 		char txt[MAX_STRSIZE];
110 		VipsBuf tmp = VIPS_BUF_STATIC( txt );
111 
112 		/* The string we write may contain itself ... write to an
113 		 * intermediate, then copy to main.
114 		 */
115 		vips_buf_vappendf( &tmp, fmt, ap );
116 
117 		vips_buf_rewind( buf );
118 		(void) vips_buf_appends( buf, vips_buf_all( &tmp ) );
119 
120 #ifdef DEBUG_ERROR
121 		printf( "error: %p %s\n", buf, vips_buf_all( buf ) );
122 #endif /*DEBUG_ERROR*/
123 	}
124 }
125 
126 void
error_clear_nip(void)127 error_clear_nip( void )
128 {
129 	if( !error_level ) {
130 		vips_buf_rewind( &error_top_buf );
131 		vips_buf_rewind( &error_sub_buf );
132 
133 #ifdef DEBUG_ERROR
134 		printf( "error_clear_nip\n" );
135 #endif /*DEBUG_ERROR*/
136 	}
137 }
138 
139 void
error_clear(void)140 error_clear( void )
141 {
142 	if( !error_level ) {
143 		error_clear_nip();
144 		im_error_clear();
145 	}
146 }
147 
148 void
error_top(const char * fmt,...)149 error_top( const char *fmt, ... )
150 {
151 	va_list ap;
152 
153 	va_start( ap, fmt );
154 	error_set( &error_top_buf, fmt, ap );
155 	va_end( ap );
156 
157 	/* We could use error_clear_nip() before calling error_set(), but that
158 	 * fails if the arg to us uses the contents of either error buffer.
159 	 */
160 	if( !error_level )
161 		vips_buf_rewind( &error_sub_buf );
162 }
163 
164 void
error_sub(const char * fmt,...)165 error_sub( const char *fmt, ... )
166 {
167 	va_list ap;
168 
169 	va_start( ap, fmt );
170 	error_set( &error_sub_buf, fmt, ap );
171 	va_end( ap );
172 }
173 
174 /* Append any VIPS errors to sub buffer.
175  */
176 void
error_vips(void)177 error_vips( void )
178 {
179 	if( !error_level && strlen( im_error_buffer() ) > 0 ) {
180 		if( !vips_buf_is_empty( &error_sub_buf ) )
181 			(void) vips_buf_appendf( &error_sub_buf, "\n" );
182 		(void) vips_buf_appendf( &error_sub_buf,
183 			"%s", im_error_buffer() );
184 		im_error_clear();
185 	}
186 }
187 
188 void
error_vips_all(void)189 error_vips_all( void )
190 {
191 	error_top( _( "VIPS library error." ) );
192 	error_vips();
193 }
194 
error_get_top(void)195 const char *error_get_top( void ) { return( vips_buf_all( &error_top_buf ) ); }
error_get_sub(void)196 const char *error_get_sub( void ) { return( vips_buf_all( &error_sub_buf ) ); }
197 
198 /* Set an xml property printf() style.
199  */
200 gboolean
set_prop(xmlNode * xnode,const char * name,const char * fmt,...)201 set_prop( xmlNode *xnode, const char *name, const char *fmt, ... )
202 {
203 	va_list ap;
204 	char value[MAX_STRSIZE];
205 
206         va_start( ap, fmt );
207 	(void) im_vsnprintf( value, MAX_STRSIZE, fmt, ap );
208         va_end( ap );
209 
210 	if( !xmlSetProp( xnode, (xmlChar *) name, (xmlChar *) value ) ) {
211 		error_top( _( "Unable to set XML property." ) );
212 		error_sub( _( "Unable to set property \"%s\" "
213 			"to value \"%s\"." ),
214 			name, value );
215 		return( FALSE );
216 	}
217 
218 	return( TRUE );
219 }
220 
221 /* Set an xml property from an optionally NULL string.
222  */
223 gboolean
set_sprop(xmlNode * xnode,const char * name,const char * value)224 set_sprop( xmlNode *xnode, const char *name, const char *value )
225 {
226 	if( value && !set_prop( xnode, name, "%s", value ) )
227 		return( FALSE );
228 
229 	return( TRUE );
230 }
231 
232 /* Set an xml property from an optionally NULL string.
233  */
234 gboolean
set_iprop(xmlNode * xnode,const char * name,int value)235 set_iprop( xmlNode *xnode, const char *name, int value )
236 {
237 	return( set_prop( xnode, name, "%d", value ) );
238 }
239 
240 /* Save a list of strings. For name=="fred" and n strings in list, save as
241  * "fredn" == n, "fred0" == list[0], etc.
242  */
243 gboolean
set_slprop(xmlNode * xnode,const char * name,GSList * labels)244 set_slprop( xmlNode *xnode, const char *name, GSList *labels )
245 {
246 	if( labels ) {
247 		char buf[256];
248 		int i;
249 
250 		(void) im_snprintf( buf, 256, "%sn", name );
251 		if( !set_prop( xnode, buf, "%d", g_slist_length( labels ) ) )
252 			return( FALSE );
253 
254 		for( i = 0; labels; i++, labels = labels->next ) {
255 			const char *label = (const char *) labels->data;
256 
257 			(void) im_snprintf( buf, 256, "%s%d", name, i );
258 			if( !set_sprop( xnode, buf, label ) )
259 				return( FALSE );
260 		}
261 	}
262 
263 	return( TRUE );
264 }
265 
266 /* Set a double ... use non-localisable conversion, rather than %g.
267  */
268 gboolean
set_dprop(xmlNode * xnode,const char * name,double value)269 set_dprop( xmlNode *xnode, const char *name, double value )
270 {
271 	char buf[G_ASCII_DTOSTR_BUF_SIZE];
272 
273 	g_ascii_dtostr( buf, sizeof( buf ), value );
274 
275 	return( set_sprop( xnode, name, buf ) );
276 }
277 
278 /* Save an array of double. For name=="fred" and n doubles in array, save as
279  * "fredn" == n, "fred0" == array[0], etc.
280  */
281 gboolean
set_dlprop(xmlNode * xnode,const char * name,double * values,int n)282 set_dlprop( xmlNode *xnode, const char *name, double *values, int n )
283 {
284 	char buf[256];
285 	int i;
286 
287 	(void) im_snprintf( buf, 256, "%sn", name );
288 	if( !set_prop( xnode, buf, "%d", n ) )
289 		return( FALSE );
290 
291 	for( i = 0; i < n; i++ ) {
292 		(void) im_snprintf( buf, 256, "%s%d", name, i );
293 		if( !set_dprop( xnode, buf, values[i] ) )
294 			return( FALSE );
295 	}
296 
297 	return( TRUE );
298 }
299 
300 gboolean
get_sprop(xmlNode * xnode,const char * name,char * buf,int sz)301 get_sprop( xmlNode *xnode, const char *name, char *buf, int sz )
302 {
303 	char *value = (char *) xmlGetProp( xnode, (xmlChar *) name );
304 
305 	if( !value )
306 		return( FALSE );
307 
308 	im_strncpy( buf, value, sz );
309 	IM_FREEF( xmlFree, value );
310 
311 	return( TRUE );
312 }
313 
314 gboolean
get_spropb(xmlNode * xnode,const char * name,VipsBuf * buf)315 get_spropb( xmlNode *xnode, const char *name, VipsBuf *buf )
316 {
317 	char *value = (char *) xmlGetProp( xnode, (xmlChar *) name );
318 
319 	if( !value )
320 		return( FALSE );
321 
322 	vips_buf_appends( buf, value );
323 	IM_FREEF( xmlFree, value );
324 
325 	return( TRUE );
326 }
327 
328 gboolean
get_iprop(xmlNode * xnode,const char * name,int * out)329 get_iprop( xmlNode *xnode, const char *name, int *out )
330 {
331 	char buf[256];
332 
333 	if( !get_sprop( xnode, name, buf, 256 ) )
334 		return( FALSE );
335 
336 	*out = atoi( buf );
337 
338 	return( TRUE );
339 }
340 
341 gboolean
get_dprop(xmlNode * xnode,const char * name,double * out)342 get_dprop( xmlNode *xnode, const char *name, double *out )
343 {
344 	char buf[256];
345 
346 	if( !get_sprop( xnode, name, buf, 256 ) )
347 		return( FALSE );
348 
349 	*out = g_ascii_strtod( buf, NULL );
350 
351 	return( TRUE );
352 }
353 
354 gboolean
get_bprop(xmlNode * xnode,const char * name,gboolean * out)355 get_bprop( xmlNode *xnode, const char *name, gboolean *out )
356 {
357 	char buf[256];
358 
359 	if( !get_sprop( xnode, name, buf, 256 ) )
360 		return( FALSE );
361 
362 	*out = strcasecmp( buf, "true" ) == 0;
363 
364 	return( TRUE );
365 }
366 
367 /* Load a list of strings. For name=="fred", look for "fredn" == number of
368  * strings to load, "fred0" == list[0], etc.
369  */
370 gboolean
get_slprop(xmlNode * xnode,const char * name,GSList ** out)371 get_slprop( xmlNode *xnode, const char *name, GSList **out )
372 {
373 	char buf[256];
374 	int n, i;
375 
376 	(void) im_snprintf( buf, 256, "%sn", name );
377 	if( !get_iprop( xnode, buf, &n ) )
378 		return( FALSE );
379 
380 	*out = NULL;
381 	for( i = n - 1; i >= 0; i-- ) {
382 		(void) im_snprintf( buf, 256, "%s%d", name, i );
383 		if( !get_sprop( xnode, buf, buf, 256 ) )
384 			return( FALSE );
385 
386 		*out = g_slist_prepend( *out, g_strdup( buf ) );
387 	}
388 
389 	return( TRUE );
390 }
391 
392 /* Load an array of double. For name=="fred", look for "fredn" == length of
393  * array, "fred0" == array[0], etc.
394  */
395 gboolean
get_dlprop(xmlNode * xnode,const char * name,double ** out)396 get_dlprop( xmlNode *xnode, const char *name, double **out )
397 {
398 	char buf[256];
399 	int n, i;
400 
401 	(void) im_snprintf( buf, 256, "%sn", name );
402 	if( !get_iprop( xnode, buf, &n ) )
403 		return( FALSE );
404 
405 	if( !(*out = IARRAY( NULL, n, double )) )
406 		return( FALSE );
407 
408 	for( i = 0; i < n; i++ ) {
409 		(void) im_snprintf( buf, 256, "%s%d", name, i );
410 		if( !get_dprop( xnode, buf, *out + i ) )
411 			return( FALSE );
412 	}
413 
414 	return( TRUE );
415 }
416 
417 /* Find the first child node with a name.
418  */
419 xmlNode *
get_node(xmlNode * base,const char * name)420 get_node( xmlNode *base, const char *name )
421 {
422 	xmlNode *i;
423 
424 	for( i = base->children; i; i = i->next )
425 		if( strcmp( (char *) i->name, name ) == 0 )
426 			return( i );
427 
428 	return( NULL );
429 }
430 
431 static int rect_n_rects = 0;
432 
433 /* Allocate and free rects.
434  */
435 Rect *
rect_dup(Rect * init)436 rect_dup( Rect *init )
437 {
438 	Rect *new_rect;
439 
440 	if( !(new_rect = INEW( NULL, Rect )) )
441 		return( NULL );
442 
443 	*new_rect = *init;
444 
445 	rect_n_rects += 1;
446 
447 	return( new_rect );
448 }
449 
450 void *
rect_free(Rect * rect)451 rect_free( Rect *rect )
452 {
453 	im_free( rect );
454 	rect_n_rects -= 1;
455 
456 	return( NULL );
457 }
458 
459 /* Test two lists for eqality.
460  */
461 gboolean
slist_equal(GSList * l1,GSList * l2)462 slist_equal( GSList *l1, GSList *l2 )
463 {
464 	while( l1 && l2 ) {
465 		if( l1->data != l2->data )
466 			return( FALSE );
467 
468 		l1 = l1->next;
469 		l2 = l2->next;
470 	}
471 
472 	if( l1 || l2 )
473 		return( FALSE );
474 
475 	return( TRUE );
476 }
477 
478 /* Map over an slist.
479  */
480 void *
slist_map(GSList * list,SListMapFn fn,gpointer a)481 slist_map( GSList *list, SListMapFn fn, gpointer a )
482 {
483 	GSList *copy;
484 	GSList *i;
485 	void *result;
486 
487 	copy = g_slist_copy( list );
488 	result = NULL;
489 	for( i = copy; i && !(result = fn( i->data, a )); i = i->next )
490 		;
491 	g_slist_free( copy );
492 
493 	return( result );
494 }
495 
496 void *
slist_map2(GSList * list,SListMap2Fn fn,gpointer a,gpointer b)497 slist_map2( GSList *list, SListMap2Fn fn, gpointer a, gpointer b )
498 {
499 	GSList *copy;
500 	GSList *i;
501 	void *result;
502 
503 	copy = g_slist_copy( list );
504 	result = NULL;
505 	for( i = copy; i && !(result = fn( i->data, a, b )); i = i->next )
506 		;
507 	g_slist_free( copy );
508 
509 	return( result );
510 }
511 
512 void *
slist_map3(GSList * list,SListMap3Fn fn,gpointer a,gpointer b,gpointer c)513 slist_map3( GSList *list, SListMap3Fn fn, gpointer a, gpointer b, gpointer c )
514 {
515 	GSList *copy;
516 	GSList *i;
517 	void *result;
518 
519 	copy = g_slist_copy( list );
520 	result = NULL;
521 	for( i = copy; i && !(result = fn( i->data, a, b, c )); i = i->next )
522 		;
523 	g_slist_free( copy );
524 
525 	return( result );
526 }
527 
528 void *
slist_map4(GSList * list,SListMap4Fn fn,gpointer a,gpointer b,gpointer c,gpointer d)529 slist_map4( GSList *list, SListMap4Fn fn,
530 	gpointer a, gpointer b, gpointer c, gpointer d )
531 {
532 	GSList *copy;
533 	GSList *i;
534 	void *result;
535 
536 	copy = g_slist_copy( list );
537 	result = NULL;
538 	for( i = copy; i && !(result = fn( i->data, a, b, c, d ));
539 		i = i->next )
540 		;
541 	g_slist_free( copy );
542 
543 	return( result );
544 }
545 
546 void *
slist_map5(GSList * list,SListMap5Fn fn,gpointer a,gpointer b,gpointer c,gpointer d,gpointer e)547 slist_map5( GSList *list, SListMap5Fn fn,
548 	gpointer a, gpointer b, gpointer c, gpointer d, gpointer e )
549 {
550 	GSList *copy;
551 	GSList *i;
552 	void *result;
553 
554 	copy = g_slist_copy( list );
555 	result = NULL;
556 	for( i = copy; i && !(result = fn( i->data, a, b, c, d, e ));
557 		i = i->next )
558 		;
559 	g_slist_free( copy );
560 
561 	return( result );
562 }
563 
564 /* Map backwards.
565  */
566 void *
slist_map_rev(GSList * list,SListMapFn fn,gpointer a)567 slist_map_rev( GSList *list, SListMapFn fn, gpointer a )
568 {
569 	GSList *copy;
570 	GSList *i;
571 	void *result;
572 
573 	copy = g_slist_copy( list );
574 	copy = g_slist_reverse( copy );
575 	result = NULL;
576 	for( i = copy; i && !(result = fn( i->data, a )); i = i->next )
577 		;
578 	g_slist_free( copy );
579 
580 	return( result );
581 }
582 
583 void *
slist_map2_rev(GSList * list,SListMap2Fn fn,gpointer a,gpointer b)584 slist_map2_rev( GSList *list, SListMap2Fn fn, gpointer a, gpointer b )
585 {
586 	GSList *copy;
587 	GSList *i;
588 	void *result;
589 
590 	copy = g_slist_copy( list );
591 	copy = g_slist_reverse( copy );
592 	result = NULL;
593 	for( i = copy; i && !(result = fn( i->data, a, b )); i = i->next )
594 		;
595 	g_slist_free( copy );
596 
597 	return( result );
598 }
599 
600 void *
slist_map3_rev(GSList * list,SListMap3Fn fn,void * a,void * b,void * c)601 slist_map3_rev( GSList *list, SListMap3Fn fn, void *a, void *b, void *c )
602 {
603 	GSList *copy;
604 	GSList *i;
605 	void *result;
606 
607 	copy = g_slist_copy( list );
608 	copy = g_slist_reverse( copy );
609 	result = NULL;
610 	for( i = copy; i && !(result = fn( i->data, a, b, c )); i = i->next )
611 		;
612 	g_slist_free( copy );
613 
614 	return( result );
615 }
616 
617 void *
map_equal(void * a,void * b)618 map_equal( void *a, void *b )
619 {
620 	if( a == b )
621 		return( a );
622 
623 	return( NULL );
624 }
625 
626 void *
slist_fold(GSList * list,void * start,SListFoldFn fn,void * a)627 slist_fold( GSList *list, void *start, SListFoldFn fn, void *a )
628 {
629         void *c;
630         GSList *ths, *next;
631 
632         for( c = start, ths = list; ths; ths = next ) {
633                 next = ths->next;
634 
635                 if( !(c = fn( ths->data, c, a )) )
636 			return( NULL );
637         }
638 
639         return( c );
640 }
641 
642 void *
slist_fold2(GSList * list,void * start,SListFold2Fn fn,void * a,void * b)643 slist_fold2( GSList *list, void *start, SListFold2Fn fn, void *a, void *b )
644 {
645         void *c;
646         GSList *ths, *next;
647 
648         for( c = start, ths = list; ths; ths = next ) {
649                 next = ths->next;
650 
651                 if( !(c = fn( ths->data, c, a, b )) )
652 			return( NULL );
653         }
654 
655         return( c );
656 }
657 
658 static void
slist_free_all_cb(gpointer thing,gpointer dummy)659 slist_free_all_cb( gpointer thing, gpointer dummy )
660 {
661 	g_free( thing );
662 }
663 
664 /* Free a g_slist of things which need g_free()ing.
665  */
666 void
slist_free_all(GSList * list)667 slist_free_all( GSList *list )
668 {
669 	g_slist_foreach( list, slist_free_all_cb, NULL );
670 	g_slist_free( list );
671 }
672 
673 /* Remove all occurences of an item from a list.
674  */
675 GSList *
slist_remove_all(GSList * list,gpointer data)676 slist_remove_all( GSList *list, gpointer data )
677 {
678 	GSList *tmp;
679 	GSList *prev;
680 
681 	prev = NULL;
682 	tmp = list;
683 
684 	while( tmp ) {
685 		if( tmp->data == data ) {
686 			GSList *next = tmp->next;
687 
688 			if( prev )
689 				prev->next = next;
690 			if( list == tmp )
691 				list = next;
692 
693 			tmp->next = NULL;
694 			g_slist_free( tmp );
695 			tmp = next;
696 		}
697 		else {
698 			prev = tmp;
699 			tmp = tmp->next;
700 		}
701 	}
702 
703 	return( list );
704 }
705 
706 Queue *
queue_new(void)707 queue_new( void )
708 {
709 	Queue *q = INEW( NULL, Queue );
710 
711 	q->list = NULL;
712 	q->tail = NULL;
713 	q->length = 0;
714 
715 	return( q );
716 }
717 
718 void *
queue_head(Queue * q)719 queue_head( Queue *q )
720 {
721 	void *data;
722 
723 	g_assert( q );
724 
725 	if( !q->list )
726 		return( NULL );
727 
728 	data = q->list->data;
729 
730 	q->list = g_slist_remove( q->list, data );
731 	if( !q->list )
732 		q->tail = NULL;
733 
734 	q->length -= 1;
735 
736 	return( data );
737 }
738 
739 void
queue_add(Queue * q,void * data)740 queue_add( Queue *q, void *data )
741 {
742 	if( !q->tail ) {
743 		g_assert( !q->list );
744 
745 		q->list = q->tail = g_slist_append( q->list, data );
746 	}
747 	else {
748 		g_assert( q->list );
749 
750 		q->tail = g_slist_append( q->tail, data );
751 		q->tail = q->tail->next;
752 	}
753 
754 	q->length += 1;
755 }
756 
757 /* Not very fast! But used infrequently. TRUE if the data was in the queue and
758  * has now been removed.
759  */
760 gboolean
queue_remove(Queue * q,void * data)761 queue_remove( Queue *q, void *data )
762 {
763 	GSList *ele;
764 
765 	if( !(ele = g_slist_find( q->list, data )) )
766 		return( FALSE );
767 
768 	q->list = g_slist_remove( q->list, data );
769 
770 	if( ele == q->tail )
771 		q->tail = g_slist_last( q->list );
772 
773 	q->length -= 1;
774 
775 	return( TRUE );
776 }
777 
778 int
queue_length(Queue * q)779 queue_length( Queue *q )
780 {
781 	return( q->length );
782 }
783 
784 /* Make an info string about an image.
785  */
786 void
vips_buf_appendi(VipsBuf * buf,IMAGE * im)787 vips_buf_appendi( VipsBuf *buf, IMAGE *im )
788 {
789 	if( !im ) {
790 		vips_buf_appends( buf, _( "(no image)" ) );
791 		return;
792 	}
793 
794 	/* Coded? Special warning.
795 	 */
796 	if( im->Coding != IM_CODING_NONE )
797 		vips_buf_appendf( buf, "%s, ",
798 			NN( im_Coding2char( im->Coding ) ) );
799 
800 	/* Format string expands to (eg.)
801 	 * "2000x3000 128-bit complex, 3 bands, Lab"
802 	 */
803 	vips_buf_appendf( buf,
804 		ngettext( "%dx%d %s, %d band, %s",
805 			"%dx%d %s, %d bands, %s", im->Bands ),
806 		im->Xsize, im->Ysize, decode_bandfmt( im->BandFmt ),
807 		im->Bands, decode_type( im->Type ) );
808 }
809 
810 /* Append a string, escaping C stuff. Escape double quotes if quote is set.
811  */
812 gboolean
vips_buf_appendsc(VipsBuf * buf,gboolean quote,const char * str)813 vips_buf_appendsc( VipsBuf *buf, gboolean quote, const char *str )
814 {
815 	char buffer[FILENAME_MAX];
816 	char buffer2[FILENAME_MAX];
817 
818 	/* /2 to leave a bit of space.
819 	 */
820 	im_strncpy( buffer, str, FILENAME_MAX / 2 );
821 
822 	/* FIXME ... possible buffer overflow :-(
823 	 */
824 	my_strecpy( buffer2, buffer, quote );
825 
826 	return( vips_buf_appends( buf, buffer2 ) );
827 }
828 
829 /* Test for string a ends string b.
830  */
831 gboolean
is_postfix(const char * a,const char * b)832 is_postfix( const char *a, const char *b )
833 {
834 	int n = strlen( a );
835 	int m = strlen( b );
836 
837 	if( m < n )
838 		return( FALSE );
839 
840 	return( !strcmp( a, b + m - n ) );
841 }
842 
843 /* Test for string a ends string b, case independent.
844  */
845 gboolean
is_casepostfix(const char * a,const char * b)846 is_casepostfix( const char *a, const char *b )
847 {
848 	int n = strlen( a );
849 	int m = strlen( b );
850 
851 	if( m < n )
852 		return( FALSE );
853 
854 	return( !strcasecmp( a, b + m - n ) );
855 }
856 
857 /* Test for string a starts string b.
858  */
859 gboolean
is_prefix(const char * a,const char * b)860 is_prefix( const char *a, const char *b )
861 {
862 	int n = strlen( a );
863 	int m = strlen( b );
864 	int i;
865 
866 	if( m < n )
867 		return( FALSE );
868 	for( i = 0; i < n; i++ )
869 		if( a[i] != b[i] )
870 			return( FALSE );
871 
872 	return( TRUE );
873 }
874 
875 /* Test for string a starts string b ... case insensitive.
876  */
877 gboolean
is_caseprefix(const char * a,const char * b)878 is_caseprefix( const char *a, const char *b )
879 {
880 	int n = strlen( a );
881 	int m = strlen( b );
882 	int i;
883 
884 	if( m < n )
885 		return( FALSE );
886 	for( i = 0; i < n; i++ )
887 		if( toupper( a[i] ) != toupper( b[i] ) )
888 			return( FALSE );
889 
890 	return( TRUE );
891 }
892 
893 /* Like strstr(), but case-insensitive.
894  */
895 char *
my_strcasestr(const char * haystack,const char * needle)896 my_strcasestr( const char *haystack, const char *needle )
897 {
898 	int i;
899 	int hlen = strlen( haystack );
900 	int nlen = strlen( needle );
901 
902 	for( i = 0; i <= hlen - nlen; i++ )
903 		if( is_caseprefix( needle, haystack + i ) )
904 			return( (char *) (haystack + i) );
905 
906 	return( NULL );
907 }
908 
909 /* Copy a string, interpreting (a few) C-language escape codes.
910 
911 	FIXME ... yuk! dangerous and not that useful, needs a proper function
912 	to do this
913 
914  */
915 char *
my_strccpy(char * output,const char * input)916 my_strccpy( char *output, const char *input )
917 {
918 	const char *p;
919 	char *q;
920 
921 	for( p = input, q = output; *p; p++, q++ )
922 		if( p[0] == '\\' ) {
923 			switch( p[1] ) {
924 			case 'n':
925 				q[0] = '\n';
926 				break;
927 
928 			case 't':
929 				q[0] = '\t';
930 				break;
931 
932 			case 'r':
933 				q[0] = '\r';
934 				break;
935 
936 			case '"':
937 				q[0] = '\"';
938 				break;
939 
940 			case '\'':
941 				q[0] = '\'';
942 				break;
943 
944 			case '\\':
945 				q[0] = '\\';
946 				break;
947 
948 			default:
949 				/* Leave uninterpreted.
950 				 */
951 				q[0] = p[0];
952 				q[1] = p[1];
953 				q++;
954 				break;
955 			}
956 
957 			p++;
958 		}
959 		else
960 			q[0] = p[0];
961 	q[0] = '\0';
962 
963 	return( output );
964 }
965 
966 /* Copy a string, expanding escape characters into C-language escape codes.
967  * Escape double quotes if quote is set.
968  */
969 char *
my_strecpy(char * output,const char * input,gboolean quote)970 my_strecpy( char *output, const char *input, gboolean quote )
971 {
972 	const char *p;
973 	char *q;
974 
975 	for( p = input, q = output; *p; p++, q++ )
976 		switch( p[0] ) {
977 		case '\n':
978 			q[0] = '\\';
979 			q[1] = 'n';
980 			q++;
981 			break;
982 
983 		case '\t':
984 			q[0] = '\\';
985 			q[1] = 't';
986 			q++;
987 			break;
988 
989 		case '\r':
990 			q[0] = '\\';
991 			q[1] = 'r';
992 			q++;
993 			break;
994 
995 		case '\"':
996 			if( quote ) {
997 				q[0] = '\\';
998 				q[1] = '\"';
999 				q++;
1000 			}
1001 			else
1002 				q[0] = p[0];
1003 			break;
1004 
1005 		case '\'':
1006 			q[0] = '\\';
1007 			q[1] = '\'';
1008 			q++;
1009 			break;
1010 
1011 		case '\\':
1012 			q[0] = '\\';
1013 			q[1] = '\\';
1014 			q++;
1015 			break;
1016 
1017 		default:
1018 			q[0] = p[0];
1019 			break;
1020 		}
1021 	q[0] = '\0';
1022 
1023 	return( output );
1024 }
1025 
1026 /* Is a character in a string?
1027  */
1028 static int
instr(char c,const char * spn)1029 instr( char c, const char *spn )
1030 {
1031 	const char *p;
1032 
1033 	for( p = spn; *p; p++ )
1034 		if( *p == c )
1035 			return( 1 );
1036 
1037 	return( 0 );
1038 }
1039 
1040 /* Doh ... not everyone has strrspn(), define one. Return a pointer to the
1041  * start of the trailing segment of p which contains only chars in spn.
1042  */
1043 const char *
my_strrspn(const char * p,const char * spn)1044 my_strrspn( const char *p, const char *spn )
1045 {
1046 	const char *p1;
1047 
1048 	for( p1 = p + strlen( p ) - 1; p1 >= p && instr( *p1, spn ); p1-- )
1049 		;
1050 	p1++;
1051 
1052 	return( p1 );
1053 }
1054 
1055 /* Find a pointer to the start of the trailing segment of p which contains
1056  * only chars not in spn.
1057  */
1058 const char *
my_strrcspn(const char * p,const char * spn)1059 my_strrcspn( const char *p, const char *spn )
1060 {
1061 	const char *p1;
1062 
1063 	for( p1 = p + strlen( p ) - 1; p1 >= p && !instr( *p1, spn ); p1-- )
1064 		;
1065 	p1++;
1066 
1067 	return( p1 );
1068 }
1069 
1070 /* Find the rightmost occurence of string a in string b.
1071  */
1072 const char *
findrightmost(const char * a,const char * b)1073 findrightmost( const char *a, const char *b )
1074 {
1075 	int la = strlen( a );
1076 	int lb = strlen( b );
1077 	int i;
1078 
1079 	if( lb < la )
1080 		return( NULL );
1081 
1082 	for( i = lb - la; i > 0; i-- )
1083 		if( strncmp( a, &b[i], la ) == 0 )
1084 			return( &b[i] );
1085 
1086 	return( NULL );
1087 }
1088 
1089 /* Useful transformation: strip off a set of suffixes (eg. ".v", ".icon",
1090  * ".hr"), add a single new suffix (eg. ".hr.v").
1091  */
1092 void
change_suffix(const char * name,char * out,const char * new,const char ** olds,int nolds)1093 change_suffix( const char *name, char *out,
1094 	const char *new, const char **olds, int nolds )
1095 {
1096 	char *p;
1097 	int i;
1098 
1099 	/* Copy start string.
1100 	 */
1101 	strcpy( out, name );
1102 
1103 	/* Drop all matching suffixes.
1104 	 */
1105 	while( (p = strrchr( out, '.' )) ) {
1106 		/* Found suffix - test against list of alternatives. Ignore
1107 		 * case.
1108 		 */
1109 		for( i = 0; i < nolds; i++ )
1110 			if( strcasecmp( p, olds[i] ) == 0 ) {
1111 				*p = '\0';
1112 				break;
1113 			}
1114 
1115 		/* Found match? If not, break from loop.
1116 		 */
1117 		if( *p )
1118 			break;
1119 	}
1120 
1121 	/* Add new suffix.
1122 	 */
1123 	strcat( out, new );
1124 }
1125 
1126 /* Drop leading and trim trailing non-alphanum characters. NULL if nothing
1127  * left. The result can be a variable name.
1128  */
1129 char *
trim_nonalpha(char * text)1130 trim_nonalpha( char *text )
1131 {
1132 	char *p, *q;
1133 
1134 	/* Skip any initial non-alpha characters.
1135 	 */
1136 	for( q = text; *q && !isalpha( (int)(*q) ); q++ )
1137 		;
1138 
1139 	/* Find next non-alphanumeric character.
1140 	 */
1141 	for( p = q; *p && isalnum( (int)(*p) ); p++ )
1142 		;
1143 	*p = '\0';
1144 
1145 	if( strlen( q ) == 0 )
1146 		return( NULL );
1147 	else
1148 		return( q );
1149 }
1150 
1151 /* Drop leading and trim trailing whitespace characters. NULL if nothing
1152  * left.
1153  */
1154 char *
trim_white(char * text)1155 trim_white( char *text )
1156 {
1157 	char *p, *q;
1158 
1159 	/* Skip any initial whitespace characters.
1160 	 */
1161 	for( q = text; *q && isspace( (int)(*q) ); q++ )
1162 		;
1163 
1164 	/* Find rightmost non-space char.
1165 	 */
1166 	for( p = q + strlen( q ) - 1; p > q && isspace( (int)(*p) ); p-- )
1167 		;
1168 	p[1] = '\0';
1169 
1170 	if( strlen( q ) == 0 )
1171 		return( NULL );
1172 	else
1173 		return( q );
1174 }
1175 
1176 /* Get a pointer to a band element in a region.
1177  */
1178 void *
get_element(REGION * ireg,int x,int y,int b)1179 get_element( REGION *ireg, int x, int y, int b )
1180 {
1181 	IMAGE *im = ireg->im;
1182 
1183 	/* Return a pointer to this on error.
1184 	 */
1185 	static PEL empty[50] = { 0 };
1186 
1187 	PEL *data;
1188 	int es = IM_IMAGE_SIZEOF_ELEMENT( im );
1189 	Rect iarea;
1190 
1191 	/* Make sure we can read from this descriptor.
1192 	 */
1193 	if( im_pincheck( im ) )
1194 		/* Help!
1195 		 */
1196 		return( empty );
1197 
1198 	/* Ask for the area we need.
1199 	 */
1200 	iarea.left = x;
1201 	iarea.top = y;
1202 	iarea.width = 1;
1203 	iarea.height = 1;
1204 	if( im_prepare( ireg, &iarea ) )
1205 		return( empty );
1206 
1207 	/* Find a pointer to the start of the data.
1208 	 */
1209 	data = (PEL *) IM_REGION_ADDR( ireg, x, y ) + b * es;
1210 
1211 	return( (void *) data );
1212 }
1213 
1214 /* Decode band formats in a friendly way.
1215  */
1216 static const char *bandfmt_names[] = {
1217 	N_( "8-bit unsigned integer" ),	/* IM_BANDFMT_UCHAR */
1218 	N_( "8-bit signed integer" ),	/* IM_BANDFMT_CHAR */
1219 	N_( "16-bit unsigned integer" ),/* IM_BANDFMT_USHORT */
1220 	N_( "16-bit signed integer" ), 	/* IM_BANDFMT_SHORT */
1221 	N_( "32-bit unsigned integer" ),/* IM_BANDFMT_UINT */
1222 	N_( "32-bit signed integer" ), 	/* IM_BANDFMT_INT */
1223 	N_( "32-bit float" ), 		/* IM_BANDFMT_FLOAT */
1224 	N_( "64-bit complex" ), 	/* IM_BANDFMT_COMPLEX */
1225 	N_( "64-bit float" ), 		/* IM_BANDFMT_DOUBLE */
1226 	N_( "128-bit complex" )		/* IM_BANDFMT_DPCOMPLEX */
1227 };
1228 static const int nbandfmt_names = IM_NUMBER( bandfmt_names );
1229 
1230 const char *
decode_bandfmt(int f)1231 decode_bandfmt( int f )
1232 {
1233 	if( f > nbandfmt_names - 1 || f < 0 )
1234 		return( _( "<unknown format>" ) );
1235 	else
1236 		return( _( bandfmt_names[f] ) );
1237 }
1238 
1239 /* Decode type names in a way consistent with the menus.
1240  */
1241 static const char *type_names[] = {
1242 	"multiband",		/* IM_TYPE_MULTIBAND	0 */
1243 	"mono",			/* IM_TYPE_B_W		1 */
1244 	"luminance",		/* LUMINACE		2 */
1245 	"xray",			/* XRAY			3 */
1246 	"infrared",		/* IR			4 */
1247 	"Yuv",			/* YUV			5 */
1248 	"red_only",		/* RED_ONLY		6 */
1249 	"green_only",		/* GREEN_ONLY		7 */
1250 	"blue_only",		/* BLUE_ONLY		8 */
1251 	"power_spectrum",	/* POWER_SPECTRUM	9 */
1252 	"histogram",		/* IM_TYPE_HISTOGRAM	10 */
1253 	"lookup_table",		/* LUT			11 */
1254 	"XYZ",			/* IM_TYPE_XYZ		12 */
1255 	"Lab",			/* IM_TYPE_LAB		13 */
1256 	"CMC",			/* CMC			14 */
1257 	"CMYK",			/* IM_TYPE_CMYK		15 */
1258 	"LabQ",			/* IM_TYPE_LABQ		16 */
1259 	"RGB",			/* IM_TYPE_RGB		17 */
1260 	"UCS",			/* IM_TYPE_UCS		18 */
1261 	"LCh",			/* IM_TYPE_LCH		19 */
1262 	"<undefined>",		/* ??			20 */
1263 	"LabS",			/* IM_TYPE_LABS		21 */
1264 	"sRGB",			/* IM_TYPE_sRGB		22 */
1265 	"Yxy",			/* IM_TYPE_YXY		23 */
1266 	"Fourier",		/* IM_TYPE_FOURIER	24 */
1267 	"RGB16",		/* IM_TYPE_RGB16	25 */
1268 	"GREY16",		/* IM_TYPE_GREY16	26 */
1269 	"Array",		/* VIPS_INTERPRETATION_ARRAY = 27 */
1270 	"scRGB"			/* VIPS_INTERPRETATION_scRGB = 28 */
1271 };
1272 static const int ntype_names = IM_NUMBER( type_names );
1273 
1274 const char *
decode_type(int t)1275 decode_type( int t )
1276 {
1277 	if( t > ntype_names - 1 || t < 0 )
1278 		return( _( "<unknown type>" ) );
1279 	else
1280 		return( type_names[t] );
1281 }
1282 
1283 /* Make an info string about a file.
1284  */
1285 void
get_image_info(VipsBuf * buf,const char * name)1286 get_image_info( VipsBuf *buf, const char *name )
1287 {
1288 	char name2[FILENAME_MAX];
1289 	struct stat st;
1290 
1291 	expand_variables( name, name2 );
1292 	vips_buf_appendf( buf, "%s, ", im_skip_dir( name ) );
1293 
1294 	/* Read size and file/dir.
1295 	 */
1296 	if( stat( name2, &st ) == -1 ) {
1297 		vips_buf_appendf( buf, "%s", g_strerror( errno ) );
1298 		return;
1299 	}
1300 
1301 	if( S_ISDIR( st.st_mode ) )
1302 		vips_buf_appends( buf, _( "directory" ) );
1303 	else if( S_ISREG( st.st_mode ) ) {
1304 		IMAGE *im;
1305 
1306 		/* Spot workspace files from the filename. These are XML files
1307 		 * and if imagemagick sees them it'll try to load them as SVG
1308 		 * or somethiing awful like that.
1309 		 */
1310 		if( is_file_type( &filesel_wfile_type, name2 ) ) {
1311 			vips_buf_appends( buf, _( "workspace" ) );
1312 		}
1313 		else if( (im = im_open( name2, "r" )) ) {
1314 			vips_buf_appendi( buf, im );
1315 			im_close( im );
1316 		}
1317 		else
1318 			/* No idea wtf this is, just put the size in.
1319 			 */
1320 			vips_buf_append_size( buf, st.st_size );
1321 	}
1322 }
1323 
1324 /* A char that can be part of an environment variable name? A-Za-z0-9_
1325  */
1326 static gboolean
isvariable(int ch)1327 isvariable( int ch )
1328 {
1329 	return( isalnum( ch ) || ch == '_' );
1330 }
1331 
1332 /* Expand environment variables from in to out. Return true if we performed an
1333  * expansion, false for no variables there.
1334  */
1335 static gboolean
expand_once(char * in,char * out)1336 expand_once( char *in, char *out )
1337 {
1338 	char *p, *q;
1339 	gboolean have_substituted = FALSE;
1340 
1341 	/* Scan and copy.
1342 	 */
1343 	for( p = in, q = out; (*q = *p) && (q - out) < FILENAME_MAX; p++, q++ )
1344 		/* Did we just copy a '$'?
1345 		 */
1346 		if( *p == '$' ) {
1347 			char vname[FILENAME_MAX];
1348 			char *r;
1349 			const char *subst;
1350 			const char *s;
1351 
1352 			/* Extract the variable name.
1353 			 */
1354 			p++;
1355 			for( r = vname;
1356 				isvariable( (int)(*r = *p) ) &&
1357 					(r - vname) < FILENAME_MAX;
1358 				p++, r++ )
1359 				;
1360 			*r = '\0';
1361 			p--;
1362 
1363 			/* Look up variable.
1364 			 */
1365 			subst = g_getenv( vname );
1366 
1367 			/* Copy variable contents.
1368 			 */
1369 			if( subst ) {
1370 				for( s = subst;
1371 					(*q = *s) && (q - out) < FILENAME_MAX;
1372 					s++, q++ )
1373 					;
1374 			}
1375 			q--;
1376 
1377 			/* Remember we have performed a substitution.
1378 			 */
1379 			have_substituted = TRUE;
1380 		}
1381 		/* Or a '~' at the start of the string?
1382 		 */
1383 		else if( *p == '~' && p == in ) {
1384 			const char *subst = g_getenv( "HOME" );
1385 			const char *r;
1386 
1387 			/* Copy variable contents.
1388 			 */
1389 			if( subst ) {
1390 				for( r = subst;
1391 					(*q = *r) && (q - out) < FILENAME_MAX;
1392 					r++, q++ )
1393 					;
1394 			}
1395 			q--;
1396 
1397 			/* Remember we have performed a substitution.
1398 			 */
1399 			have_substituted = TRUE;
1400 		}
1401 
1402 	return( have_substituted );
1403 }
1404 
1405 /* Expand all variables! Don't touch in, assume out[] is at least
1406  * FILENAME_MAX bytes. in and out must not point to the same place!
1407  */
1408 void
expand_variables(const char * in,char * out)1409 expand_variables( const char *in, char *out )
1410 {
1411 	char buf[FILENAME_MAX];
1412 	char *p1 = (char *) in;		/* Discard const, but safe */
1413 	char *p2 = out;
1414 
1415 	g_assert( in != out );
1416 
1417 	/* Expand any environment variables in component.
1418 	 */
1419 	while( expand_once( p1, p2 ) )
1420 		/* We have expanded --- swap the buffers and try
1421 		 * again.
1422 		 */
1423 		if( p2 == out ) {
1424 			p1 = out;
1425 			p2 = buf;
1426 		}
1427 		else {
1428 			p1 = buf;
1429 			p2 = out;
1430 		}
1431 }
1432 
1433 static void
swap_chars(char * buf,char from,char to)1434 swap_chars( char *buf, char from, char to )
1435 {
1436 	int i;
1437 
1438 	for( i = 0; buf[i]; i++ )
1439 		if( buf[i] == from )
1440 			buf[i] = to;
1441 }
1442 
1443 /* If we use '/' seps, swap all '\' for '/' ... likewise vice versa. Only in
1444  * the filename part, though. We don't want to junk '\,', for example.
1445  */
1446 void
nativeize_path(char * buf)1447 nativeize_path( char *buf )
1448 {
1449 	char filename[FILENAME_MAX];
1450 	char mode[FILENAME_MAX];
1451 
1452 	im_filename_split( buf, filename, mode );
1453 
1454 	if( G_DIR_SEPARATOR == '/' )
1455 		swap_chars( filename, '\\', '/' );
1456 	else
1457 		swap_chars( filename, '/', '\\' );
1458 
1459 	if( strcmp( mode, "" ) != 0 )
1460 		im_snprintf( buf, FILENAME_MAX, "%s:%s", filename, mode );
1461 	else
1462 		im_snprintf( buf, FILENAME_MAX, "%s", filename );
1463 }
1464 
1465 /* Change all occurences of "from" into "to". This will loop if "to" contains
1466  * "from", beware.
1467  */
1468 static void
swap_string(char * buffer,const char * from,const char * to)1469 swap_string( char *buffer, const char *from, const char *to )
1470 {
1471 	char *p;
1472 
1473 	while( (p = strstr( buffer, from )) ) {
1474 		int off = p - buffer;
1475 		char buf2[FILENAME_MAX];
1476 
1477 		im_strncpy( buf2, buffer, FILENAME_MAX );
1478 		buf2[off] = '\0';
1479 		im_snprintf( buffer, FILENAME_MAX,
1480 			"%s%s%s", buf2, to, buf2 + off + strlen( from ) );
1481 	}
1482 }
1483 
1484 /* Remove "." and ".." from an absolute path (if we can).
1485  */
1486 void
canonicalize_path(char * path)1487 canonicalize_path( char *path )
1488 {
1489 	gboolean found;
1490 
1491 	g_assert( is_absolute( path ) );
1492 
1493 	/* Any "/./" can go.
1494 	 */
1495 	swap_string( path,
1496 		G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S );
1497 
1498 	/* Any "//" can go.
1499 	 */
1500 	swap_string( path,
1501 		G_DIR_SEPARATOR_S G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S );
1502 
1503 	/* Repeatedly search for "/[^/]+/../" and remove it.
1504 
1505 		FIXME ... yuk, should search backwards from the end of the
1506 		string
1507 
1508 	 */
1509 	do {
1510 		char *p;
1511 
1512 		found = FALSE;
1513 
1514 		for( p = path; (p = strchr( p, G_DIR_SEPARATOR )); p++ ) {
1515 			char *q;
1516 
1517 			q = strchr( p + 1, G_DIR_SEPARATOR );
1518 
1519 			if( q && is_prefix( G_DIR_SEPARATOR_S "..", q ) ) {
1520 				memmove( p, q + 3, strlen( q + 3 ) + 1 );
1521 				found = TRUE;
1522 				break;
1523 			}
1524 		}
1525 	} while( found );
1526 
1527 	/* We may have the empty string ... that's just '/'.
1528 	 */
1529 	if( strcmp( path, "" ) == 0 )
1530 		strcpy( path, G_DIR_SEPARATOR_S );
1531 }
1532 
1533 /* Absoluteize a path. Must be FILENAME_MAX chars available.
1534  */
1535 void
absoluteize_path(char * path)1536 absoluteize_path( char *path )
1537 {
1538 	if( !is_absolute( path ) ) {
1539 		char buf[FILENAME_MAX];
1540 		char *cwd;
1541 
1542 		im_strncpy( buf, path, FILENAME_MAX );
1543 		cwd = g_get_current_dir();
1544 		im_snprintf( path, FILENAME_MAX,
1545 			"%s%s%s", cwd, G_DIR_SEPARATOR_S, buf );
1546 		g_free( cwd );
1547 		canonicalize_path( path );
1548 	}
1549 }
1550 
1551 /* Call a void*-valued function, building a string arg. We expand env.
1552  * variables, but that's all.
1553  */
1554 void *
callv_string(callv_string_fn fn,const char * arg,void * a,void * b,void * c)1555 callv_string( callv_string_fn fn, const char *arg, void *a, void *b, void *c )
1556 {
1557 	char buf[FILENAME_MAX];
1558 
1559 	expand_variables( arg, buf );
1560 
1561 	return( fn( buf, a, b, c ) );
1562 }
1563 
1564 void *
callv_stringva(callv_string_fn fn,const char * fmt,va_list ap,void * a,void * b,void * c)1565 callv_stringva( callv_string_fn fn,
1566 	const char *fmt, va_list ap, void *a, void *b, void *c )
1567 {
1568 	char buf[FILENAME_MAX];
1569 
1570         (void) im_vsnprintf( buf, FILENAME_MAX, fmt, ap );
1571 
1572 	return( callv_string( fn, buf, a, b, c ) );
1573 }
1574 
1575 void *
callv_stringf(callv_string_fn fn,const char * fmt,...)1576 callv_stringf( callv_string_fn fn, const char *fmt, ... )
1577 {
1578 	va_list ap;
1579 	void *res;
1580 
1581         va_start( ap, fmt );
1582         res = callv_stringva( fn, fmt, ap, NULL, NULL, NULL );
1583         va_end( ap );
1584 
1585 	return( res );
1586 }
1587 
1588 /* Call a function, building a filename arg. Nativize and absoluteize too.
1589  */
1590 void *
callv_string_filename(callv_string_fn fn,const char * filename,void * a,void * b,void * c)1591 callv_string_filename( callv_string_fn fn,
1592 	const char *filename, void *a, void *b, void *c )
1593 {
1594 	char buf[FILENAME_MAX];
1595 
1596 	expand_variables( filename, buf );
1597         nativeize_path( buf );
1598 	absoluteize_path( buf );
1599 
1600 	return( fn( buf, a, b, c ) );
1601 }
1602 
1603 void *
callv_string_filenameva(callv_string_fn fn,const char * fmt,va_list ap,void * a,void * b,void * c)1604 callv_string_filenameva( callv_string_fn fn,
1605 	const char *fmt, va_list ap, void *a, void *b, void *c )
1606 {
1607 	char buf[FILENAME_MAX];
1608 
1609         (void) im_vsnprintf( buf, FILENAME_MAX, fmt, ap );
1610 
1611 	return( callv_string_filename( fn, buf, a, b, c ) );
1612 }
1613 
1614 void *
callv_string_filenamef(callv_string_fn fn,const char * fmt,...)1615 callv_string_filenamef( callv_string_fn fn, const char *fmt, ... )
1616 {
1617 	va_list ap;
1618 	void *res;
1619 
1620         va_start( ap, fmt );
1621         res = callv_string_filenameva( fn, fmt, ap, NULL, NULL, NULL );
1622         va_end( ap );
1623 
1624 	return( res );
1625 }
1626 
1627 /* Call an int-valued function, building a string arg. We expand env.
1628  * variables, but that's all.
1629  */
1630 int
calli_string(calli_string_fn fn,const char * arg,void * a,void * b,void * c)1631 calli_string( calli_string_fn fn, const char *arg, void *a, void *b, void *c )
1632 {
1633 	char buf[FILENAME_MAX];
1634 
1635 	expand_variables( arg, buf );
1636 
1637 	return( fn( buf, a, b, c ) );
1638 }
1639 
1640 int
calli_stringva(calli_string_fn fn,const char * fmt,va_list ap,void * a,void * b,void * c)1641 calli_stringva( calli_string_fn fn,
1642 	const char *fmt, va_list ap, void *a, void *b, void *c )
1643 {
1644 	char buf[FILENAME_MAX];
1645 
1646         (void) im_vsnprintf( buf, FILENAME_MAX, fmt, ap );
1647 
1648 	return( calli_string( fn, buf, a, b, c ) );
1649 }
1650 
1651 int
calli_stringf(calli_string_fn fn,const char * fmt,...)1652 calli_stringf( calli_string_fn fn, const char *fmt, ... )
1653 {
1654 	va_list ap;
1655 	int res;
1656 
1657         va_start( ap, fmt );
1658         res = calli_stringva( fn, fmt, ap, NULL, NULL, NULL );
1659         va_end( ap );
1660 
1661 	return( res );
1662 }
1663 
1664 /* Call a function, building a filename arg. Nativize and absoluteize too.
1665  */
1666 int
calli_string_filename(calli_string_fn fn,const char * filename,void * a,void * b,void * c)1667 calli_string_filename( calli_string_fn fn,
1668 	const char *filename, void *a, void *b, void *c )
1669 {
1670 	char buf[FILENAME_MAX];
1671 
1672 	expand_variables( filename, buf );
1673         nativeize_path( buf );
1674 	absoluteize_path( buf );
1675 
1676 	return( fn( buf, a, b, c ) );
1677 }
1678 
1679 int
calli_string_filenameva(calli_string_fn fn,const char * fmt,va_list ap,void * a,void * b,void * c)1680 calli_string_filenameva( calli_string_fn fn,
1681 	const char *fmt, va_list ap, void *a, void *b, void *c )
1682 {
1683 	char buf[FILENAME_MAX];
1684 
1685         (void) im_vsnprintf( buf, FILENAME_MAX, fmt, ap );
1686 
1687 	return( calli_string_filename( fn, buf, a, b, c ) );
1688 }
1689 
1690 int
calli_string_filenamef(calli_string_fn fn,const char * fmt,...)1691 calli_string_filenamef( calli_string_fn fn, const char *fmt, ... )
1692 {
1693 	va_list ap;
1694 	int res;
1695 
1696         va_start( ap, fmt );
1697         res = calli_string_filenameva( fn, fmt, ap, NULL, NULL, NULL );
1698         va_end( ap );
1699 
1700 	return( res );
1701 }
1702 
1703 /* Convert a filename to utf8 ... g_free the result.
1704  */
1705 char *
f2utf8(const char * filename)1706 f2utf8( const char *filename )
1707 {
1708 	char *utf8;
1709 
1710 	if( !(utf8 = g_filename_to_utf8( filename, -1, NULL, NULL, NULL )) )
1711 		utf8 = g_strdup( _( "<charset conversion error>" ) );
1712 
1713 	return( utf8 );
1714 }
1715 
1716 void
setenvf(const char * name,const char * fmt,...)1717 setenvf( const char *name, const char *fmt, ... )
1718 {
1719 	va_list ap;
1720 	char buf[FILENAME_MAX];
1721 
1722         va_start( ap, fmt );
1723         (void) im_vsnprintf( buf, FILENAME_MAX, fmt, ap );
1724         va_end( ap );
1725 
1726 	g_setenv( name, buf, TRUE );
1727 }
1728 
1729 int
check(const char * filename)1730 check( const char *filename )
1731 {
1732 	/* Need to work on filenames containing %.
1733 	 */
1734 	return( im_existsf( "%s", filename ) );
1735 }
1736 
1737 /* File exists?
1738  */
1739 gboolean
existsf(const char * name,...)1740 existsf( const char *name, ... )
1741 {
1742 	va_list ap;
1743 	gboolean res;
1744 
1745         va_start( ap, name );
1746         res = calli_string_filenameva(
1747 		(calli_string_fn) check, name, ap, NULL, NULL, NULL );
1748         va_end( ap );
1749 
1750 	return( res );
1751 }
1752 
1753 int
isdir_sub(const char * filename)1754 isdir_sub( const char *filename )
1755 {
1756 	struct stat st;
1757 
1758 	/* Read size and file/dir.
1759 	 */
1760 	if( stat( filename, &st ) == -1 )
1761 		return( FALSE );
1762 	if( !S_ISDIR( st.st_mode ) )
1763 		return( FALSE );
1764 
1765 	return( TRUE );
1766 }
1767 
1768 gboolean
isdir(const char * filename,...)1769 isdir( const char *filename, ... )
1770 {
1771 	va_list ap;
1772 	gboolean res;
1773 
1774         va_start( ap, filename );
1775         res = calli_string_filenameva(
1776 		(calli_string_fn) isdir_sub, filename, ap, NULL, NULL, NULL );
1777         va_end( ap );
1778 
1779 	return( res );
1780 }
1781 
1782 
1783 static void *
mtime_sub(const char * filename,time_t * time)1784 mtime_sub( const char *filename, time_t *time )
1785 {
1786 	struct stat st;
1787 
1788 	if( stat( filename, &st ) == -1 )
1789 		return( NULL );
1790 #ifdef HAVE_GETEUID
1791 	if( st.st_uid != geteuid() )
1792 		return( NULL );
1793 #endif /*HAVE_GETEUID*/
1794 	*time = st.st_mtime;
1795 
1796 	return( NULL );
1797 }
1798 
1799 time_t
mtime(const char * filename,...)1800 mtime( const char *filename, ... )
1801 {
1802 	va_list ap;
1803 	time_t time;
1804 
1805 	time = 0;
1806         va_start( ap, filename );
1807         (void) callv_string_filenameva(
1808 		(callv_string_fn) mtime_sub, filename, ap, &time, NULL, NULL );
1809         va_end( ap );
1810 
1811 	return( time );
1812 }
1813 
1814 gboolean
mkdirf(const char * name,...)1815 mkdirf( const char *name, ... )
1816 {
1817 	va_list ap;
1818 	gboolean res;
1819 
1820         va_start( ap, name );
1821         res = calli_string_filenameva(
1822 		(calli_string_fn) g_mkdir,
1823 			name, ap, GINT_TO_POINTER( 0755 ), NULL, NULL ) == 0;
1824         va_end( ap );
1825 
1826 	return( res );
1827 }
1828 
1829 /* system(), with printf() args and $xxx expansion.
1830  */
1831 int
systemf(const char * fmt,...)1832 systemf( const char *fmt, ... )
1833 {
1834 	va_list ap;
1835 	int res;
1836 
1837         va_start( ap, fmt );
1838         res = calli_stringva(
1839 		(calli_string_fn) system, fmt, ap, NULL, NULL, NULL );
1840         va_end( ap );
1841 
1842 	return( res );
1843 }
1844 
1845 gboolean
touchf(const char * fmt,...)1846 touchf( const char *fmt, ... )
1847 {
1848 	va_list ap;
1849 	int fd;
1850 
1851         va_start( ap, fmt );
1852         fd = calli_string_filenameva(
1853 		(calli_string_fn) creat,
1854 			fmt, ap, GINT_TO_POINTER( S_IRUSR | S_IWUSR ),
1855 				NULL, NULL );
1856         va_end( ap );
1857 	(void) close( fd );
1858 
1859 	return( fd != -1 );
1860 }
1861 
1862 int
unlinkf(const char * fmt,...)1863 unlinkf( const char *fmt, ... )
1864 {
1865 	va_list ap;
1866 	int res;
1867 
1868         va_start( ap, fmt );
1869         res = calli_string_filenameva(
1870 		(calli_string_fn) unlink, fmt, ap, NULL, NULL, NULL );
1871         va_end( ap );
1872 
1873 	return( res );
1874 }
1875 
1876 /* Relative or absolute dir path? Have to expand env vars to see.
1877  */
1878 gboolean
is_absolute(const char * fname)1879 is_absolute( const char *fname )
1880 {
1881 	char buf[FILENAME_MAX];
1882 
1883 	expand_variables( fname, buf );
1884 	nativeize_path( buf );
1885 
1886 	/* We can't use g_path_is_absolute(), we might be given a Windows path
1887 	 * including a drive specifier, and g_path_is_absolute() on unix does
1888 	 * not know about Windows paths.
1889 	 *
1890 	 * We should probably look out for whitespace.
1891 	 */
1892 	if( buf[0] == '/' ||
1893 		(buf[0] != '\0' && buf[1] == ':') )
1894 		return( TRUE );
1895 	else
1896 		return( FALSE );
1897 }
1898 
1899 /* OK filename? Ban ':' characters, they may confuse im_open(). Except on
1900  * winders :-(
1901  */
1902 gboolean
is_valid_filename(const char * name)1903 is_valid_filename( const char *name )
1904 {
1905 	const char *p;
1906 
1907 	if( strlen( name ) > FILENAME_MAX ) {
1908 		error_top( _( "Bad filename." ) );
1909 		error_sub( _( "Filename is too long." ) );
1910 		return( FALSE );
1911 	}
1912 	if( (p = im_skip_dir( name )) &&
1913 		strspn( p, WHITESPACE ) == strlen( p ) ) {
1914 		error_top( _( "Bad filename." ) );
1915 		error_sub( _( "Filename contains only blank characters." ) );
1916 		return( FALSE );
1917 	}
1918 
1919 	return( TRUE );
1920 }
1921 
1922 /* im_strdup(), with NULL supplied.
1923  */
im_strdupn(const char * txt)1924 char *im_strdupn( const char *txt ) { return( im_strdup( NULL, txt ) ); }
1925 
1926 /* Free an iOpenFile.
1927  */
1928 void
ifile_close(iOpenFile * of)1929 ifile_close( iOpenFile *of )
1930 {
1931 	IM_FREEF( fclose, of->fp );
1932 	IM_FREE( of->fname );
1933 	IM_FREE( of->fname_real );
1934 	IM_FREE( of );
1935 }
1936 
1937 /* Make an iOpenFile*.
1938  */
1939 static iOpenFile *
ifile_build(const char * fname)1940 ifile_build( const char *fname )
1941 {
1942 	iOpenFile *of;
1943 
1944 	if( !(of = INEW( NULL, iOpenFile )) )
1945 		return( NULL );
1946 
1947 	of->fp = NULL;
1948 	of->fname = NULL;
1949 	of->fname_real = NULL;
1950 	of->last_errno = 0;
1951 
1952 	IM_SETSTR( of->fname, fname );
1953 	if( !of->fname ) {
1954 		ifile_close( of );
1955 		return( NULL );
1956 	}
1957 
1958 	return( of );
1959 }
1960 
1961 /* Find and open for read.
1962  */
1963 iOpenFile *
ifile_open_read(const char * name,...)1964 ifile_open_read( const char *name, ... )
1965 {
1966 	va_list ap;
1967 	char buf[FILENAME_MAX];
1968 	iOpenFile *of;
1969 
1970         va_start( ap, name );
1971         (void) im_vsnprintf( buf, FILENAME_MAX, name, ap );
1972         va_end( ap );
1973         of = ifile_build( buf );
1974 
1975 	if( !of )
1976 		return( NULL );
1977 	if( !(of->fname_real = path_find_file( of->fname )) ) {
1978 		error_top( _( "Unable to open." ) );
1979 		error_sub( _( "Unable to open file \"%s\" for reading.\n%s." ),
1980 			of->fname, g_strerror( errno ) );
1981 		ifile_close( of );
1982 		return( NULL );
1983 	}
1984 
1985 	if( !(of->fp = (FILE *) callv_string_filename( (callv_string_fn) fopen,
1986 		of->fname_real, "r", NULL, NULL )) ) {
1987 		error_top( _( "Unable to open." ) );
1988 		error_sub( _( "Unable to open file \"%s\" for reading.\n%s." ),
1989 			of->fname_real, g_strerror( errno ) );
1990 		ifile_close( of );
1991 		return( NULL );
1992 	}
1993 
1994 	of->read = TRUE;
1995 
1996 	return( of );
1997 }
1998 
1999 /* Open stdin for read.
2000  */
2001 iOpenFile *
ifile_open_read_stdin()2002 ifile_open_read_stdin()
2003 {
2004 	iOpenFile *of;
2005 
2006 	if( !(of = ifile_build( "stdin" )) )
2007 		return( NULL );
2008 	IM_SETSTR( of->fname_real, of->fname );
2009 	if( !of->fname_real ) {
2010 		ifile_close( of );
2011 		return( NULL );
2012 	}
2013 	of->fp = stdin;
2014 	of->read = TRUE;
2015 
2016 	return( of );
2017 }
2018 
2019 /* Find and open for write.
2020  */
2021 iOpenFile *
ifile_open_write(const char * name,...)2022 ifile_open_write( const char *name, ... )
2023 {
2024 	va_list ap;
2025 	char buf[FILENAME_MAX];
2026 	iOpenFile *of;
2027 
2028         va_start( ap, name );
2029         (void) im_vsnprintf( buf, FILENAME_MAX, name, ap );
2030         va_end( ap );
2031         of = ifile_build( buf );
2032 
2033 	if( !of )
2034 		return( NULL );
2035 	IM_SETSTR( of->fname_real, of->fname );
2036 	if( !of->fname_real ) {
2037 		ifile_close( of );
2038 		return( NULL );
2039 	}
2040 	if( !(of->fp = (FILE *) callv_string_filename( (callv_string_fn) fopen,
2041 		of->fname_real, "w", NULL, NULL )) ) {
2042 		error_top( _( "Unable to open." ) );
2043 		error_sub( _( "Unable to open file \"%s\" for writing.\n%s." ),
2044 			of->fname_real, g_strerror( errno ) );
2045 		ifile_close( of );
2046 		return( NULL );
2047 	}
2048 
2049 	of->read = FALSE;
2050 
2051 	return( of );
2052 }
2053 
2054 /* fprintf() to a file, checking result.
2055  */
2056 gboolean
ifile_write(iOpenFile * of,const char * fmt,...)2057 ifile_write( iOpenFile *of, const char *fmt, ... )
2058 {
2059 	va_list ap;
2060 
2061         va_start( ap, fmt );
2062 	if( vfprintf( of->fp, fmt, ap ) == EOF ) {
2063 		of->last_errno = errno;
2064 		error_top( _( "Unable to write." ) );
2065 		error_sub( _( "Unable to write to file \"%s\".\n%s." ),
2066 			of->fname_real, g_strerror( of->last_errno ) );
2067 		return( FALSE );
2068 	}
2069         va_end( ap );
2070 
2071 	return( TRUE );
2072 }
2073 
2074 /* Save a string ... if non-NULL. Eg.
2075  *	fred="boink!"
2076  */
2077 gboolean
ifile_write_var(iOpenFile * of,const char * name,const char * value)2078 ifile_write_var( iOpenFile *of, const char *name, const char *value )
2079 {
2080 	if( value )
2081 		return( ifile_write( of, " %s=\"%s\"", name, value ) );
2082 
2083 	return( TRUE );
2084 }
2085 
2086 /* Load up a file as a string.
2087  */
2088 char *
ifile_read(iOpenFile * of)2089 ifile_read( iOpenFile *of )
2090 {
2091 	long len;
2092 	size_t len2;
2093 	char *str;
2094 
2095 	/* Find length.
2096 	 */
2097 	fseek( of->fp, 0L, 2 );
2098 	len = ftell( of->fp );
2099 	rewind( of->fp );
2100 	if( len < 0 || len > 1024 * 1024 ) {
2101 		error_top( _( "Unable to read." ) );
2102 		error_sub( _( "File \"%s\" too large." ), of->fname_real );
2103 		return( NULL );
2104 	}
2105 
2106 	/* Allocate memory and fill.
2107 	 */
2108 	if( !(str = imalloc( NULL, len + 1 )) )
2109 		return( NULL );
2110 
2111 	/* We can't check len2 against len, since we may be reading a text
2112 	 * file on Windows, in which case this fread will change CRLF to LF
2113 	 * and len2 will be less than len.
2114 	 */
2115 	len2 = fread( str, sizeof( char ), (size_t) len, of->fp );
2116 
2117 	str[len2] = '\0';
2118 
2119 	return( str );
2120 }
2121 
2122 /* Load a file into a buffer. Useful for OpenFiles we can't seek in, like
2123  * stdin.
2124  */
2125 char *
ifile_read_buffer(iOpenFile * of,char * buffer,size_t max)2126 ifile_read_buffer( iOpenFile *of, char *buffer, size_t max )
2127 {
2128 	size_t len;
2129 
2130 	/* -1 off max to leave space for the '\0'.
2131 	 */
2132 	len = fread( buffer, sizeof( char ), max - 1, of->fp );
2133 	if( !feof( of->fp ) ) {
2134 		/* File too large for buffer.
2135 		 */
2136 		of->last_errno = errno;
2137 		error_top( _( "Unable to read." ) );
2138 		error_sub( _( "Unable to read from file \"%s\".\n%s." ),
2139 			of->fname_real, g_strerror( of->last_errno ) );
2140 		return( NULL );
2141 	}
2142 	buffer[len] = '\0';
2143 
2144 	return( buffer );
2145 }
2146 
2147 /* Return '\0' for EOF, -1 for error.
2148  */
2149 int
ifile_getc(iOpenFile * of)2150 ifile_getc( iOpenFile *of )
2151 {
2152 	int ch;
2153 
2154 	ch = fgetc( of->fp );
2155 
2156 	if( ch == EOF && feof( of->fp ) )
2157 		return( 0 );
2158 	else if( ch == EOF )
2159 		return( -1 );
2160 	else
2161 		return( ch );
2162 }
2163 
2164 off_t
statf(const char * fmt,...)2165 statf( const char *fmt, ... )
2166 {
2167 	va_list ap;
2168 	struct stat st;
2169 	int result;
2170 
2171         va_start( ap, fmt );
2172         result = calli_string_filenameva(
2173 		(calli_string_fn) stat, fmt, ap, &st, NULL, NULL );
2174         va_end( ap );
2175 
2176 	if( result == -1 || S_ISDIR( st.st_mode ) )
2177 		return( 0 );
2178 	else
2179 		return( st.st_size );
2180 }
2181 
2182 static void *
directory_size_sub(const char * filename,double * total)2183 directory_size_sub( const char *filename, double *total )
2184 {
2185 	*total += statf( "%s", filename );
2186 
2187 	return( NULL );
2188 }
2189 
2190 /* Find the amount of 'stuff' in a directory. Result in bytes. Don't look in
2191  * sub-dirs.
2192  */
2193 double
directory_size(const char * dirname)2194 directory_size( const char *dirname )
2195 {
2196 	double total;
2197 
2198 	total = 0;
2199 	path_map_dir( dirname, "*",
2200 		(path_map_fn) directory_size_sub, &total );
2201 
2202 	return( total );
2203 }
2204 
2205 /* Escape "%" characters in a string.
2206  */
2207 char *
escape_percent(const char * in,char * out,int len)2208 escape_percent( const char *in, char *out, int len )
2209 {
2210 	const char *p;
2211 	char *q;
2212 
2213 	for( p = in, q = out; *p && q - out < len - 3; p++, q++ )
2214 		if( *p == '%' ) {
2215 			q[0] = '%';
2216 			q[1] = '%';
2217 			q++;
2218 		}
2219 		else
2220 			*q = *p;
2221 
2222 	*q = '\0';
2223 
2224 	return( out );
2225 }
2226 
2227 char *
escape_markup(const char * in,char * out,int len)2228 escape_markup( const char *in, char *out, int len )
2229 {
2230 	const char *p;
2231 	char *q;
2232 
2233 	for( p = in, q = out; *p && q - out < len - 5; p++, q++ )
2234 		if( *p == '<' ) {
2235 			strcpy( q, "&lt;" );
2236 			q += 3;
2237 		}
2238 		else if( *p == '>' ) {
2239 			strcpy( q, "&gt;" );
2240 			q += 3;
2241 		}
2242 		else if( *p == '&' ) {
2243 			strcpy( q, "&amp;" );
2244 			q += 4;
2245 		}
2246 		else
2247 			*q = *p;
2248 
2249 	*q = '\0';
2250 
2251 	return( out );
2252 }
2253 
2254 /* VIPS filenames can have embedded modes. Mode strings are punctuated with
2255  * ',' and ':' chars. So strings in modes must have these chars escaped.
2256  */
2257 char *
escape_mode(const char * in,char * out,int len)2258 escape_mode( const char *in, char *out, int len )
2259 {
2260 	const char *p;
2261 	char *q;
2262 
2263 	for( p = in, q = out; *p && q - out < len - 5; p++, q++ ) {
2264 		if( *p == ':' || *p == ',' )
2265 			*q++ = '\\';
2266 
2267 		*q = *p;
2268 	}
2269 
2270 	*q = '\0';
2271 
2272 	return( out );
2273 }
2274 
2275 /* Return a string of n characters. Buffer is zapped each time!
2276  */
2277 const char *
rpt(char ch,int n)2278 rpt( char ch, int n )
2279 {
2280 	int i;
2281 	static char buf[200];
2282 
2283 	n = IM_MIN( 190, n );
2284 
2285 	for( i = 0; i < n; i++ )
2286 		buf[i] = ch;
2287 	buf[i] = '\0';
2288 
2289 	return( buf );
2290 }
2291 
2292 /* Return a string of n spaces. Buffer is zapped each time!
2293  */
2294 const char *
spc(int n)2295 spc( int n )
2296 {
2297 	return( rpt( ' ', n ) );
2298 }
2299 
2300 /* Like strtok(), but better. Give a string and a list of break characters;
2301  * write a '\0' into the string over the first break character and return a
2302  * pointer to the next non-break character. If there are no break characters,
2303  * then return a pointer to the end of the string. If passed a pointer to an
2304  * empty string or NULL, then return NULL.
2305  */
2306 char *
break_token(char * str,const char * brk)2307 break_token( char *str, const char *brk )
2308 {
2309 	char *p;
2310 
2311 	/* Is the string empty? If yes, return NULL immediately.
2312 	 */
2313 	if( !str || !*str )
2314 		return( NULL );
2315 
2316 	/* Skip initial break characters.
2317 	 */
2318 	p = str + strspn( str, brk );
2319 
2320 	/* Search for the first break character after the token.
2321 	 */
2322 	p += strcspn( p, brk );
2323 
2324 	/* Is there string left?
2325 	 */
2326 	if( *p ) {
2327 		/* Write in an end-of-string mark and return the start of the
2328 		 * next token.
2329 		 */
2330 		*p++ = '\0';
2331 		p += strspn( p, brk );
2332 	}
2333 
2334 	return( p );
2335 }
2336 
2337 /* Turn a number to a string. 0 is "A", 1 is "B", 25 is "Z", 26 is "AA", 27 is
2338  * "AB", etc.
2339  */
2340 void
number_to_string(int n,char * buf)2341 number_to_string( int n, char *buf )
2342 {
2343 	do {
2344 		int rem = n % 26;
2345 
2346 		*buf++ = (char) (rem + (int) 'A');
2347 		n /= 26;
2348 	} while( n > 0 );
2349 
2350 	*buf ='\0';
2351 }
2352 
2353 /* Find the space remaining in a directory, in bytes. A double for >32bit
2354  * problem avoidance. <0 for error.
2355  */
2356 #ifdef HAVE_SYS_STATVFS_H
2357 double
find_space(const char * name)2358 find_space( const char *name )
2359 {
2360 	struct statvfs st;
2361 	double sz;
2362 
2363 	if( calli_string_filename( (calli_string_fn) statvfs,
2364 		(gpointer) name, &st, NULL, NULL ) )
2365 		/* Set to error value.
2366 		 */
2367 		sz = -1;
2368 	else
2369 		sz = IM_MAX( 0, (double) st.f_frsize * st.f_bavail );
2370 
2371 	return( sz );
2372 }
2373 #elif (HAVE_SYS_VFS_H || HAVE_SYS_MOUNT_H)
2374 double
find_space(const char * name)2375 find_space( const char *name )
2376 {
2377 	struct statfs st;
2378 	double sz;
2379 
2380 	if( calli_string_filename( (calli_string_fn) statfs,
2381 		(gpointer) name, &st, NULL, NULL ) )
2382 		sz = -1;
2383 	else
2384 		sz = IM_MAX( 0, (double) st.f_bsize * st.f_bavail );
2385 
2386 	return( sz );
2387 }
2388 #elif defined OS_WIN32
2389 double
find_space(const char * name)2390 find_space( const char *name )
2391 {
2392 	ULARGE_INTEGER avail;
2393 	ULARGE_INTEGER total;
2394 	ULARGE_INTEGER free;
2395 	double sz;
2396 	char name2[FILENAME_MAX];
2397 
2398 	expand_variables( name, name2 );
2399 
2400 	/* Truncate to just the drive letter.
2401 	 */
2402 	if( name2[1] == ':' )
2403 		name2[3] = 0;
2404 
2405 	if( !GetDiskFreeSpaceEx( name2, &avail, &total, &free ) )
2406 		sz = -1;
2407 	else
2408 		sz = IM_MAX( 0, (double) free.QuadPart );
2409 
2410 	return( sz );
2411 }
2412 #else
2413 double
find_space(const char * name)2414 find_space( const char *name )
2415 {
2416 	return( -1 );
2417 }
2418 #endif /*HAVE_SYS_STATVFS_H*/
2419 
2420 /* Make a name for a temp file. Add the specified extension.
2421  */
2422 gboolean
temp_name(char * name,const char * type)2423 temp_name( char *name, const char *type )
2424 {
2425 	/* Some mkstemp() require files to actually exist before they don't
2426 	 * reuse the filename :-( add an extra field.
2427 	 */
2428 	static int n = 0;
2429 
2430 	const char *dir;
2431 	int fd;
2432 	char buf[FILENAME_MAX];
2433 
2434 	dir = PATH_TMP;
2435 	if( !existsf( "%s", dir ) )
2436 		dir = G_DIR_SEPARATOR_S;
2437 
2438 	im_snprintf( name, FILENAME_MAX, "%s" G_DIR_SEPARATOR_S
2439 		"untitled-" PACKAGE "-%d-XXXXXXX",
2440 		dir, n++ );
2441 	expand_variables( name, buf );
2442 
2443 	fd = g_mkstemp( buf );
2444 	if( fd == -1 ) {
2445 		error_top( _( "Unable to create temporary file." ) );
2446 		error_sub( _( "Unable to make file \"%s\"\n%s" ),
2447 			buf, g_strerror( errno ) );
2448 		return( FALSE );
2449 	}
2450 	close( fd );
2451 	unlinkf( "%s", buf );
2452 
2453 	im_snprintf( name, FILENAME_MAX, "%s.%s", buf, type );
2454 
2455 	return( TRUE );
2456 }
2457 
2458 /* Max/min of an area.
2459  */
2460 int
findmaxmin(IMAGE * in,int left,int top,int width,int height,double * min,double * max)2461 findmaxmin( IMAGE *in,
2462 	int left, int top, int width, int height, double *min, double *max )
2463 {
2464 	DOUBLEMASK *msk;
2465 	IMAGE *t1;
2466 
2467 	if( !(t1 = im_open( "temp", "p" )) )
2468 		return( -1 );
2469 	if( im_extract_area( in, t1, left, top, width, height ) ||
2470 		!(msk = im_stats( t1 )) )
2471 		return( -1 );
2472 	im_close( t1 );
2473 
2474 	*min = msk->coeff[0];
2475 	*max = msk->coeff[1];
2476 
2477 	im_free_dmask( msk );
2478 
2479 #ifdef DEBUG
2480 	printf( "findmaxmin: left = %d, top = %d, width = %d, height = %d\n",
2481 		left, top, width, height );
2482 	printf( "findmaxmin: max = %g, min = %g\n", *max, *min );
2483 #endif /*DEBUG*/
2484 
2485 	return( 0 );
2486 }
2487 
2488 gboolean
char_to_bool(char * str,void * out)2489 char_to_bool( char *str, void *out )
2490 {
2491 	gboolean *t = (gboolean *) out;
2492 
2493 	if( strcasecmp( "TRUE", str ) == 0 )
2494 		*t = TRUE;
2495 	else
2496 		*t = FALSE;
2497 
2498 	return( TRUE );
2499 }
2500 
2501 char *
bool_to_char(gboolean b)2502 bool_to_char( gboolean b )
2503 {
2504 	if( b )
2505 		return( "true" );
2506 	else
2507 		return( "false" );
2508 }
2509 
2510 /* Increment a name ... strip any trailing numbers, add one, put numbers back.
2511  * Start at 1 if no number there. buf should be at least namelen chars. Keep
2512  * leading zeros, if any.
2513  */
2514 void
increment_name(char * buf)2515 increment_name( char *buf )
2516 {
2517 	char *p;
2518 	int n;
2519 	char fmt[256];
2520 
2521 	/* If there's no number, p will point at the '\0'.
2522 	 */
2523         p = (char *) my_strrspn( buf, NUMERIC );
2524 	if( *p ) {
2525                 n = atoi( p );
2526 		im_snprintf( fmt, 256, "%%0%dd", (int) strlen( p ) );
2527 	}
2528 	else {
2529 		strcpy( fmt, "%d" );
2530 		n = 0;
2531 	}
2532 
2533         im_snprintf( p, MAX_STRSIZE - (p - buf), fmt, n + 1 );
2534 }
2535 
2536 /* Increment filename. Eg. "/home/jim/fred_00_tn.tif" becomes
2537  * "/home/jim/fred_01_tn.tif"
2538  */
2539 void
increment_filename(char * filename)2540 increment_filename( char *filename )
2541 {
2542         char buf[FILENAME_MAX];
2543         char suf[FILENAME_MAX];
2544         char tail[FILENAME_MAX];
2545         char *file, *p;
2546 
2547 	im_strncpy( buf, filename, FILENAME_MAX );
2548 
2549 	/* Save and replace the suffix around an increment_name.
2550 	 */
2551 	file = (char *) im_skip_dir( buf );
2552 	if( !(p = strrchr( file, '.' )) )
2553 		p = file + strlen( file );
2554 	im_strncpy( suf, p, FILENAME_MAX );
2555 	*p = '\0';
2556 
2557 	/* Also save any chars to the right of the number component (if any) of
2558 	 * the filename.
2559 	 */
2560 	p = (char *) my_strrcspn( file, NUMERIC );
2561 
2562 	/* No numbers there? Take nothing as the tail and put the number at
2563 	 * the end.
2564 	 */
2565 	if( p == file )
2566 		p = file + strlen( file );
2567 
2568 	im_strncpy( tail, p, FILENAME_MAX );
2569 	*p = '\0';
2570 
2571 	increment_name( buf );
2572 
2573 	strcpy( filename, buf );
2574 	strcat( filename, tail );
2575 	strcat( filename, suf );
2576 }
2577 
2578 /* Extract the first line of a string in to buf, extract no more than len
2579  * chars.
2580  */
2581 int
extract_first_line(char * buf,char * str,int len)2582 extract_first_line( char *buf, char *str, int len )
2583 {
2584         char *p;
2585         int n;
2586 
2587         /* Find next '\n' or '\0'.
2588          */
2589         if( (p = strchr( str, '\n' )) )
2590                 n = p - str;
2591         else
2592                 n = strlen( str );
2593         n = IM_MIN( len - 1, n );
2594 
2595         /* Copy those characters and make sure we have a '\0'.
2596          */
2597         strncpy( buf, str, n );
2598         buf[n] = '\0';
2599 
2600         return( n );
2601 }
2602 
2603 /* Make a valid ip name from a filename.
2604  */
2605 void
name_from_filename(const char * in,char * out)2606 name_from_filename( const char *in, char *out )
2607 {
2608 	const char *p;
2609 
2610 	/* Skip leading path prefix, and any non-alpha.
2611 	 * Don't use isalnum(), since we don't want leading digits.
2612 	 */
2613 	p = im_skip_dir( in );
2614 	while( *p && !(isalpha( *p ) || *p == '_') )
2615 		p += 1;
2616 
2617 	if( !*p )
2618 		strcpy( out, "untitled" );
2619 	else {
2620 		char *q;
2621 
2622 		/* Filter non-identifier chars. Stop at the first '.'
2623 		 * character, so we don't get the suffix in there too.
2624 		 */
2625 		for( q = out; *p && *p != '.'; p++ )
2626 			if( isalnum( *p ) || *p == '_' || *p == '\'' )
2627 				*q++ = *p;
2628 		*q = '\0';
2629 	}
2630 }
2631 
2632 /* Do any leak checking we can.
2633  */
2634 void
util_check_all_destroyed(void)2635 util_check_all_destroyed( void )
2636 {
2637 	if( rect_n_rects )
2638 		printf( "rect_n_rects == %d\n", rect_n_rects );
2639 }
2640 
2641 /* Like im_malloc(), but set our error stuff.
2642  */
2643 void *
imalloc(IMAGE * im,size_t len)2644 imalloc( IMAGE *im, size_t len )
2645 {
2646 	void *mem;
2647 
2648 	if( !(mem = im_malloc( im, len )) ) {
2649 		char txt[256];
2650 		VipsBuf buf = VIPS_BUF_STATIC( txt );
2651 
2652 		vips_buf_append_size( &buf, len );
2653 		error_top( _( "Out of memory." ) );
2654 		error_sub( _( "Request for %s of RAM triggered memory "
2655 			"allocation failure." ), vips_buf_all( &buf ) );
2656 		error_vips();
2657 
2658 		return( NULL );
2659 	}
2660 
2661 	return( mem );
2662 }
2663 
2664 /* Add a filename to a recent list. If there are more than MAX_RECENT items,
2665  * drop the last one off. If this is a dupe, move it to the head of the list.
2666  */
2667 GSList *
recent_add(GSList * recent,const char * filename)2668 recent_add( GSList *recent, const char *filename )
2669 {
2670 	char absolute[FILENAME_MAX];
2671 	int n;
2672 	GSList *p;
2673 
2674 	im_strncpy( absolute, filename, FILENAME_MAX );
2675 	absoluteize_path( absolute );
2676 
2677 	for( p = recent; p; p = p->next ) {
2678 		const char *stored = p->data;
2679 
2680 		if( strcmp( absolute, stored ) == 0 ) {
2681 			recent = g_slist_remove( recent, stored );
2682 			recent = g_slist_prepend( recent, (void *) stored );
2683 
2684 			return( recent );
2685 		}
2686 	}
2687 
2688 	recent = g_slist_prepend( recent, g_strdup( absolute ) );
2689 
2690 	if( (n = g_slist_length( recent )) > MAX_RECENT ) {
2691 		const char *item;
2692 
2693 		item = g_slist_nth_data( recent, n - 1 );
2694 		recent = g_slist_remove( recent, item );
2695 		g_free( (char *) item );
2696 	}
2697 
2698 	return( recent );
2699 }
2700 
2701 GSList *
recent_load(const char * filename)2702 recent_load( const char *filename )
2703 {
2704 	iOpenFile *of;
2705 	GSList *recent;
2706 
2707 	recent = NULL;
2708 
2709 	if( (of = ifile_open_read( "%s" G_DIR_SEPARATOR_S "%s",
2710 		get_savedir(), filename )) ) {
2711 		char buf[256];
2712 
2713 		while( fgets( buf, 256, of->fp ) ) {
2714 			int n;
2715 
2716 			if( (n = strlen( buf )) > 0 ) {
2717 				if( buf[n - 1] == '\n' )
2718 					buf[n - 1] = '\0';
2719 				recent = recent_add( recent, buf );
2720 			}
2721 		}
2722 
2723 		ifile_close( of );
2724 	}
2725 
2726 	return( recent );
2727 }
2728 
2729 void
recent_free(GSList * recent)2730 recent_free( GSList *recent )
2731 {
2732 	GSList *p;
2733 
2734 	for( p = recent; p; p = p->next ) {
2735 		const char *item = (const char *) p->data;
2736 
2737 		g_free( (char *) item );
2738 	}
2739 
2740 	g_slist_free( recent );
2741 }
2742 
2743 static void *
recent_save_sub(const char * filename,GSList ** old_recent)2744 recent_save_sub( const char *filename, GSList **old_recent )
2745 {
2746 	*old_recent = recent_add( *old_recent, filename );
2747 
2748 	return( NULL );
2749 }
2750 
2751 static void *
recent_save_sub2(const char * filename,iOpenFile * of)2752 recent_save_sub2( const char *filename, iOpenFile *of )
2753 {
2754 	fprintf( of->fp, "%s\n", filename );
2755 
2756 	return( NULL );
2757 }
2758 
2759 void
recent_save(GSList * recent,const char * filename)2760 recent_save( GSList *recent, const char *filename )
2761 {
2762 	iOpenFile *of;
2763 	GSList *old_recent;
2764 
2765 	/* If there are several nips running, we could be saving over a file
2766 	 * that's been modified since we loaded it. Try to make this less
2767 	 * awful by merging our recent list over the one in the file.
2768 	 */
2769 	old_recent = recent_load( filename );
2770 	slist_map_rev( recent,
2771 		(SListMapFn) recent_save_sub, &old_recent );
2772 
2773 	if( (of = ifile_open_write( "%s" G_DIR_SEPARATOR_S "%s",
2774 		get_savedir(), filename )) ) {
2775 		slist_map_rev( old_recent,
2776 			(SListMapFn) recent_save_sub2, of );
2777 		ifile_close( of );
2778 	}
2779 
2780 	recent_free( old_recent );
2781 }
2782 
2783 /* Return the name of the save dir we use ... eg. "$HOME/.nip2-7.10.8",
2784  * or maybe "C:\Documents and Settings\john\Application Data"
2785  */
2786 const char *
get_savedir(void)2787 get_savedir( void )
2788 {
2789 #ifdef OS_WIN32
2790 	/* If APPDATA is not defined, default to HOME, we know that will
2791 	 * exist (since we make it if necessary in main()).
2792 	 */
2793 	if( g_getenv( "APPDATA" ) && existsf( "%s", g_getenv( "APPDATA" ) ) )
2794 		return( "$APPDATA" G_DIR_SEPARATOR_S IP_NAME );
2795 	else
2796 		return( "$HOME" G_DIR_SEPARATOR_S "." IP_NAME );
2797 #elif OS_DARWIN
2798 	/* Darwin ... in ~/Library
2799 	 */
2800 	return( "$HOME" G_DIR_SEPARATOR_S "Library" G_DIR_SEPARATOR_S IP_NAME );
2801 #else
2802 	/* *nix-style system .. .dot file in home area.
2803 	 */
2804 	return( "$HOME" G_DIR_SEPARATOR_S "." IP_NAME );
2805 #endif /*OS_WIN32*/
2806 }
2807 
2808 /* Turn an slist into a null-terminated array.
2809  */
2810 void **
slist_to_array(GSList * list)2811 slist_to_array( GSList *list )
2812 {
2813 	void **array;
2814 	int i;
2815 
2816 	array = g_new( void *, g_slist_length( list ) + 1 );
2817 	for( i = 0; list ; list = list->next, i++ )
2818 		array[i] = list->data;
2819 	array[i] = NULL;
2820 
2821 	return( array );
2822 }
2823 
2824 /* Length of a NULL-terminated array.
2825  */
2826 int
array_len(void ** array)2827 array_len( void **array )
2828 {
2829 	int i;
2830 
2831 	for( i = 0; array[i]; i++ )
2832 		;
2833 
2834 	return( i );
2835 }
2836