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, "<" );
2236 q += 3;
2237 }
2238 else if( *p == '>' ) {
2239 strcpy( q, ">" );
2240 q += 3;
2241 }
2242 else if( *p == '&' ) {
2243 strcpy( q, "&" );
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