1 /*
2  * This file is part of XForms.
3  *
4  *  XForms is free software; you can redistribute it and/or modify it
5  *  under the terms of the GNU Lesser General Public License as
6  *  published by the Free Software Foundation; either version 2.1, or
7  *  (at your option) any later version.
8  *
9  *  XForms is distributed in the hope that it will be useful, but
10  *  WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public License
15  *  along with XForms.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 
23 #include "fd_main.h"
24 
25 #include <string.h>
26 #include <stdlib.h>
27 #include <limits.h>
28 #include <float.h>
29 #include <ctype.h>
30 #include <stdarg.h>
31 
32 
33 static struct
34 {
35     FILE   * fp;
36     char   * fname;
37     size_t   line_no;
38     char   * line;
39     char   * pos;
40     int      merge;
41 } ff = { NULL, NULL, 0, NULL, NULL, 0 };
42 
43 
44 
45 /***************************************
46  * Returns a pointer to the first position in a string
47  * that's not a white-space character
48  ***************************************/
49 
50 static char *
ff_skip_spaces(const char * cp)51 ff_skip_spaces( const char * cp )
52 {
53     while ( *cp && isspace( ( unsigned char ) *cp ) )
54         cp++;
55 
56     return ( char * ) cp;
57 }
58 
59 
60 /***************************************
61  ***************************************/
62 
63 char *
ff_get_filename_copy(void)64 ff_get_filename_copy( void )
65 {
66     if ( ! ff.fname || ! ff.fp )
67         return NULL;
68 
69     return fl_strdup( ff.fname );
70 }
71 
72 
73 /***************************************
74  ***************************************/
75 
76 static int
ff_is_comment(void)77 ff_is_comment( void )
78 {
79     ff.pos = ff_skip_spaces( ff.pos );
80 
81     return    ! *ff.pos
82            || *ff.pos == ';'
83            || *ff.pos == '#'
84            || *ff.pos == '-'
85            || *ff.pos == '=';
86 }
87 
88 
89 /***************************************
90  ***************************************/
91 
92 void
ff_close(void)93 ff_close( void )
94 {
95     if ( ff.fp )
96     {
97         fclose( ff.fp );
98         ff.fp = NULL;
99     }
100 
101     fli_safe_free( ff.fname );
102     fli_safe_free( ff.line );
103 
104     ff.pos = NULL;
105     ff.line_no = 0;
106     ff.merge = 0;
107 }
108 
109 
110 /***************************************
111  ***************************************/
112 
113 int
ff_err(const char * message)114 ff_err( const char * message )
115 {
116     if ( message )
117     {
118         if ( ! fdopt.conv_only )
119             fl_show_alert_f( 0, "Error:\f%s\n%s:%lu.%lu",
120                              message, ff.fname, ( unsigned long ) ff.line_no,
121                              ff.line ?
122                              ( unsigned long ) ( ff.pos - ff.line ) : 0 );
123         else
124             M_err( "Error", "%s at %s:%lu.%lu",
125                message, ff.fname, ( unsigned long ) ff.line_no,
126                    ff.line ? ( unsigned long ) ( ff.pos - ff.line ) : 0 );
127     }
128 
129     ff_close( );
130     return FF_READ_FAILURE;
131 }
132 
133 
134 /***************************************
135  ***************************************/
136 
137 static int
ff_get_line(void)138 ff_get_line( void )
139 {
140     if ( ff.fp )
141         do
142         {
143             fli_safe_free( ff.line );
144 
145             if ( ! ( ff.line = fli_read_line( ff.fp ) ) )
146             {
147                 if ( feof( ff.fp ) )
148                 {
149                     ff.line_no++;
150                     return 0;
151                 }
152                 return ff_err( "Error while reading from file" );
153             }
154 
155             ff.line_no++;
156             ff.pos = ff.line;
157         } while ( ff_is_comment( ) );
158 
159     return 0;
160 }
161 
162 
163 /***************************************
164  ***************************************/
165 
166 int
ff_get_fd_file(const char * str,int merge)167 ff_get_fd_file( const char  * str,
168                 int           merge )
169 {
170     ff_close( );
171 
172     ff.merge = merge;
173 
174     fl_use_fselector( LOAD_FSELECTOR );
175 
176     /* Get the filename if necessary */
177 
178     if ( ! str || ! *str )
179     {
180         str = fl_show_fselector( merge ? "Filename to merge forms from" :
181                                  "Filename to load forms from",
182                                  "", "*.fd", "" );
183 
184         if ( ! str || ! *str )
185             return -1;
186     }
187 
188     /* Append ".fd" if required. */
189 
190     ff.fname = append_fd_suffix( str );
191 
192     /* Open the file for reading */
193 
194     if ( ! ( ff.fp = fopen( ff.fname, "r" ) ) )
195     {
196         if ( ! fdopt.conv_only )
197             fl_show_alert( "Can't open file for reading", ff.fname, "", 0 );
198         else
199             M_err( "ff_get_fd_file", "Can't open '%s' for reading", ff.fname );
200         ff_close( );
201         return -1;
202     }
203 
204     if ( ff_get_line( ) < 0 )
205     {
206         if ( ! fdopt.conv_only )
207             fl_show_alert( "Nothing to be read from", ff.fname, "", 0 );
208         else
209             M_err( "ff_get_fd_file", "Nothing to be read from '%s'",
210                    ff.fname );
211         ff_close( );
212         return -1;
213     }
214 
215     return 0;
216 }
217 
218 
219 /***************************************
220  * Checks if an input text matches text read from the file
221  * - with multiple white-spaces treated as a single space
222  ***************************************/
223 
224 static const char *
ff_match_text(const char * txt)225 ff_match_text( const char *txt )
226 {
227     char *src = ff.pos;
228 
229     txt = ff_skip_spaces( txt );
230 
231     while ( *src && *txt )
232     {
233         if (    *src != *txt
234              && ! (    isspace( ( unsigned char ) *src )
235                     && isspace( ( unsigned char ) *txt ) ) )
236             return NULL;
237 
238         if ( isspace( ( unsigned char ) *src ) )
239         {
240             src = ff_skip_spaces( src );
241             txt = ff_skip_spaces( txt );
242         }
243         else
244         {
245             src++;
246             txt++;
247         }
248     }
249 
250     txt = ff_skip_spaces( txt );
251     if ( *txt )
252         return NULL;
253 
254     ff.pos = src;
255 
256     return txt;
257 }
258 
259 
260 /***************************************
261  ***************************************/
262 
263 static int
ff_match_long(long * p)264 ff_match_long( long * p )
265 {
266     long val;
267     char *ep;
268 
269     val = strtol( ff.pos, &ep, 10 );
270 
271     if ( ep == ff.pos )
272         return -1;
273 
274     if ( *ep != '\0' && ! isspace( ( unsigned char ) *ep ) )
275         return -1;
276 
277     if ( ( val == LONG_MAX || val == LONG_MIN ) && errno == ERANGE )
278         return -1;
279 
280     ff.pos = ep;
281 
282     *p = val;
283     return 0;
284 }
285 
286 
287 /***************************************
288  ***************************************/
289 
290 static int
ff_match_ulong(unsigned long * p)291 ff_match_ulong( unsigned long * p )
292 {
293     unsigned long val;
294     char *ep;
295 
296     if ( *ff.pos == '-' )
297         return -1;
298 
299     val = strtoul( ff.pos, &ep, 10 );
300 
301     if (    ep == ff.pos
302          || ( *ep != '\0' && ! isspace( ( unsigned char ) *ep ) )
303          || ( val == ULONG_MAX && errno == ERANGE ) )
304         return -1;
305 
306     ff.pos = ep;
307 
308     *p = val;
309     return 0;
310 }
311 
312 
313 /***************************************
314  ***************************************/
315 
316 static int
ff_match_int(int * p)317 ff_match_int( int * p )
318 {
319     long val;
320     char *old_pos = ff.pos;
321 
322     if ( ff_match_long( &val ) < 0 )
323         return -1;
324 
325     if ( ( val > INT_MAX || val < INT_MIN ) )
326     {
327         ff.pos = old_pos;
328         return -1;
329     }
330 
331     *p = val;
332     return 0;
333 }
334 
335 
336 /***************************************
337  ***************************************/
338 
339 static int
ff_match_uint(unsigned int * p)340 ff_match_uint( unsigned int * p )
341 {
342     unsigned long val;
343     char *old_pos = ff.pos;
344 
345     if ( ff_match_ulong( &val ) < 0 )
346         return -1;
347 
348     if ( val > UINT_MAX )
349     {
350         ff.pos = old_pos;
351         return -1;
352     }
353 
354     *p = val;
355     return 0;
356 }
357 
358 
359 /***************************************
360  ***************************************/
361 
362 static int
ff_match_double(double * p)363 ff_match_double( double * p )
364 {
365     double val;
366     char *ep;
367 
368     val = strtod( ff.pos, &ep );
369 
370     if (    ep == ff.pos
371          || ( *ep != '\0' && ! isspace( ( unsigned char ) *ep ) )
372          || ( ( val == HUGE_VAL || val == - HUGE_VAL ) && errno == ERANGE ) )
373         return -1;
374 
375     ff.pos = ep;
376 
377     *p = val;
378     return 0;
379 }
380 
381 
382 /***************************************
383  ***************************************/
384 
385 static int
ff_match_float(float * p)386 ff_match_float( float * p )
387 {
388     double val;
389     char *old_pos = ff.pos;
390 
391     if ( ff_match_double( &val ) < 0 || val < - FLT_MAX || val > FLT_MAX)
392     {
393         ff.pos = old_pos;
394         return -1;
395     }
396 
397     *p = val;
398     return 0;
399 }
400 
401 
402 /***************************************
403  ***************************************/
404 
405 static int
ff_match_coord(FL_Coord * p,int need_positive)406 ff_match_coord( FL_Coord * p,
407                 int        need_positive )
408 {
409     int val;
410     char *old_pos = ff.pos;
411 
412     if ( ff_match_int( &val ) < 0 || ( need_positive && val < 0 ) )
413     {
414         ff.pos = old_pos;
415         return -1;
416     }
417 
418     *p = val;
419     return 0;
420 }
421 
422 
423 /***************************************
424  ***************************************/
425 
426 static int
ff_match_string(char ** p)427 ff_match_string( char ** p )
428 {
429     /* Backtrack to start of line or last ':' */
430 
431     while ( ff.pos > ff.line && isspace( ( unsigned char ) *--ff.pos ) )
432         /* empty */ ;
433 
434     /* If we're at a ':' skip the next space if if exists */
435 
436     if (    ff.pos > ff.line
437          && *ff.pos == ':'
438          && isspace( ( unsigned char ) *++ff.pos ) )
439         ff.pos++;
440 
441     *p = ff.pos + strlen( ff.pos ) - 1;
442     if ( **p == '\n' )
443         **p = '\0';
444 
445     *p = fl_strdup( ff.pos );
446 
447     while ( *ff.pos )
448         ff.pos++;
449 
450     return 0;
451 }
452 
453 
454 /***************************************
455  ***************************************/
456 
457 static int
ff_match_trimmed_string(char ** p)458 ff_match_trimmed_string( char ** p )
459 {
460     char *ep = ff.pos + strlen( ff.pos ) - 1,
461          *fp = ep + 1;
462     char old_c;
463 
464     if ( ! *ff.pos )
465     {
466         *p = fl_strdup( ff.pos );
467         return 0;
468     }
469 
470     *p = NULL;
471 
472     while ( ep > ff.pos && isspace( ( unsigned char ) *ep ) )
473         ep--;
474 
475     old_c = *ep;
476     *++ep = '\0';
477 
478     *p = fl_strdup( ff.pos );
479     *ep = old_c;
480     ff.pos = fp;
481 
482     return 0;
483 }
484 
485 
486 /***************************************
487  ***************************************/
488 
489 static int
ff_match_spaceless_string(char ** p)490 ff_match_spaceless_string( char ** p )
491 {
492     char *ep = ff.pos;
493 
494     while ( *ep && ! isspace( ( unsigned char ) *ep ) )
495         ep++;
496 
497     if ( ep == ff.pos )
498         *p = fl_strdup( "" );
499     else if ( ( *p = fl_malloc( ep - ff.pos + 2 ) ) )
500     {
501         fli_sstrcpy( *p, ff.pos, ep - ff.pos + 1 );
502         ff.pos = ep;
503     }
504 
505     return p ? 0 : -1;
506 }
507 
508 
509 /***************************************
510  ***************************************/
511 
512 static int
ff_match_var(char ** p)513 ff_match_var( char ** p )
514 {
515     char *ep = ff.pos;
516     char old_c;
517 
518     if ( ! *ep )
519     {
520         *p = fl_strdup( ff.pos );
521         return -1;
522     }
523 
524     *p = NULL;
525 
526     if (    ! isascii( ( unsigned char ) *ep )
527          || ! ( isalpha( ( unsigned char ) *ep ) || *ep == '_' ) )
528     {
529         *p = fl_strdup( "" );
530         return -1;
531     }
532 
533     while (    *++ep
534             && isascii( ( unsigned char ) *ep )
535             && ( isalnum( ( unsigned char ) *ep ) || *ep == '_' ) )
536         /* empty */ ;
537 
538     if ( *ep && ! isspace( ( unsigned char ) *ep ) )
539     {
540         *p = fl_strdup( "" );
541         return -1;
542     }
543 
544     /* Currently variable, function etc. names can't be longer... */
545 
546     if ( ep - ff.pos >= MAX_VAR_LEN )
547     {
548         *p = fl_strdup( "" );
549         return -1;
550     }
551 
552     old_c = *ep;
553     *ep = '\0';
554 
555     *p = fl_strdup( ff.pos );
556 
557     *ep = old_c;
558     ff.pos = ep + 1;
559 
560     return 0;
561 }
562 
563 
564 /***************************************
565  ***************************************/
566 
567 static int
ff_match_objclass(int * p)568 ff_match_objclass( int * p )
569 {
570     char *class_name;
571     int class;
572     char * old_pos = ff.pos;
573 
574 
575     if ( ff_match_spaceless_string( &class_name ) < 0 )
576         return -1;
577 
578     if ( ! *class_name || ( class = class_val( class_name ) ) == -1 )
579     {
580         ff.pos = old_pos;
581         fl_free( class_name );
582         return -1;
583     }
584 
585     *p = class;
586     fl_free( class_name );
587     return 0;
588 }
589 
590 
591 /***************************************
592  ***************************************/
593 
594 static int
ff_match_boxtype(int * p)595 ff_match_boxtype( int * p )
596 {
597     char *boxtype_name;
598     char *old_pos = ff.pos;
599     int boxtype;
600 
601     if ( ff_match_spaceless_string( &boxtype_name ) < 0 )
602         return -1;
603 
604     if ( ! *boxtype_name || ( boxtype = boxtype_val( boxtype_name ) ) == -1 )
605     {
606         ff.pos = old_pos;
607         fl_free( boxtype_name );
608         return -1;
609     }
610 
611     *p = boxtype;
612     fl_free( boxtype_name );
613     return 0;
614 }
615 
616 
617 /***************************************
618  ***************************************/
619 
620 static int
ff_match_color(FL_COLOR * p)621 ff_match_color( FL_COLOR * p )
622 {
623     char *color_name;
624     char *old_pos = ff.pos;
625     FL_COLOR color;
626 
627     if ( ff_match_spaceless_string( &color_name ) < 0 )
628         return -1;
629 
630     if (    ! *color_name
631          || (    ( color = fli_query_namedcolor( color_name ) ) > FL_MAX_COLORS
632               && color != FL_NoColor ) )
633     {
634         ff.pos = old_pos;
635         fl_free( color_name );
636         return -1;
637     }
638 
639     *p = color;
640     if ( *p == 0x8fffffff )
641         *p = FL_NoColor;
642 
643     fl_free( color_name );
644     return 0;
645 }
646 
647 
648 /***************************************
649  * align may consist of two values, separated by a '|' or '+'
650  ***************************************/
651 
652 static int
ff_match_align(int * p)653 ff_match_align( int * p )
654 {
655     char *align_name;
656     char *old_pos = ff.pos;
657     char *sp = strchr( ff.pos, '|' );
658     int align;
659 
660     if ( ! sp )
661         sp = strchr( ff.pos, '+' );
662 
663     if (    ! sp
664          || ( sp > ff.pos
665               && ! isspace( ( unsigned char ) sp[ -1 ] )
666               && ! isspace( ( unsigned char ) sp[ 1 ] ) ) )
667     {
668         if ( ff_match_spaceless_string( &align_name ) < 0 )
669             return -1;
670     }
671     else
672     {
673         char *a1,
674              *a2,
675              o = *sp;
676 
677         *sp = '\0';
678         if ( ff_match_spaceless_string( &a1 ) < 0 || ! *a1 )
679         {
680             fl_free( a1 );
681             ff.pos = old_pos;
682             *sp = o;
683             return -1;
684         }
685 
686         *sp = o;
687         ff.pos = sp + 1;
688         ff.pos = ff_skip_spaces( ff.pos );
689 
690         if ( ff_match_spaceless_string( &a2 ) < 0 || ! *a2 )
691         {
692             fl_free( a1 );
693             fl_free( a2 );
694             ff.pos = old_pos;
695             return -1;
696         }
697 
698         align_name = fl_malloc( strlen( a1 ) + strlen( a2 ) + 2 );
699         if ( align_name )
700             sprintf( align_name, "%s|%s", a1, a2 );
701 
702         fl_free( a1 );
703         fl_free( a2 );
704     }
705 
706     if (    ! align_name
707          || ! *align_name
708          || ( align = align_val( align_name ) ) == -1 )
709     {
710         ff.pos = old_pos;
711         fl_free( align_name );
712         return -1;
713     }
714 
715     *p =  align;
716     fl_free( align_name );
717     return 0;
718 }
719 
720 
721 /***************************************
722  * lstyle may consist of two values, separated by '|' or '+'
723  ***************************************/
724 
725 static int
ff_match_lstyle(int * p)726 ff_match_lstyle( int * p )
727 {
728     char *lstyle_name = NULL;
729     int lstyle;
730     char *old_pos = ff.pos;
731     char *sp = strchr( ff.pos, '|' );
732 
733     if ( ! sp )
734         sp = strchr( ff.pos, '+' );
735 
736     if (    ! sp
737          || ( sp > ff.pos
738               && ! isspace( ( unsigned char ) sp[ -1 ] )
739               && ! isspace( ( unsigned char ) sp[ 1 ] ) ) )
740     {
741         if ( ff_match_spaceless_string( &lstyle_name ) < 0 )
742             return -1;
743     }
744     else
745     {
746         char *l1,
747              *l2,
748              *old_pos = ff.pos,
749              o = *sp;
750 
751         *sp = '\0';
752         if ( ff_match_spaceless_string( &l1 ) < 0 )
753         {
754             fl_free( l1 );
755             *sp = o;
756             return -1;
757         }
758 
759         *sp = o;
760         ff.pos = sp + 1;
761         ff.pos = ff_skip_spaces( ff.pos );
762 
763         if ( ff_match_spaceless_string( &l2 ) < 0 || ! *l2 )
764         {
765             ff.pos = old_pos;
766             fl_free( l1 );
767             fl_free( l2 );
768             return -1;
769         }
770 
771         lstyle_name = fl_malloc( strlen( l1 ) + strlen( l2 ) + 2 );
772         if ( lstyle_name )
773             sprintf( lstyle_name, "%s|%s", l1, l2 );
774 
775         fl_free( l1 );
776         fl_free( l2 );
777     }
778 
779     if (    ! lstyle_name
780          || ! *lstyle_name
781          || ( lstyle = style_val( lstyle_name ) ) == -1 )
782     {
783         ff.pos = old_pos;
784         fl_free( lstyle_name );
785         return -1;
786     }
787 
788     *p = lstyle;
789     fl_free( lstyle_name );
790     return 0;
791 }
792 
793 
794 /***************************************
795  ***************************************/
796 
797 static int
ff_match_lsize(int * p)798 ff_match_lsize( int * p )
799 {
800     char *lsize_name;
801     char *old_pos = ff.pos;
802     int lsize;
803 
804     if ( ff_match_spaceless_string( &lsize_name ) < 0 )
805         return -1;
806 
807     if ( ! *lsize_name || ( lsize = lsize_val( lsize_name ) ) == -1 )
808     {
809         fl_free( lsize_name );
810         ff.pos = old_pos;
811         return -1;
812     }
813 
814     *p = lsize;
815     fl_free( lsize_name );
816     return 0;
817 }
818 
819 
820 /***************************************
821  ***************************************/
822 
823 static int
ff_match_resize(int * p)824 ff_match_resize( int * p )
825 {
826     char *resize_name;
827     char *old_pos = ff.pos;
828     int resize;
829 
830     if ( ff_match_spaceless_string( &resize_name ) < 0 )
831         return -1;
832 
833     if ( ! *resize_name || ( resize = resize_val( resize_name ) ) == -1 )
834     {
835         fl_free( resize_name );
836         ff.pos = old_pos;
837         return -1;
838     }
839 
840     *p = resize;
841     fl_free( resize_name );
842     return 0;
843 }
844 
845 
846 /***************************************
847  ***************************************/
848 
849 static int
ff_match_gravity(int * p)850 ff_match_gravity( int * p )
851 {
852     char *gravity_name;
853     char *old_pos = ff.pos;
854     int gravity;
855 
856     if ( ff_match_spaceless_string( &gravity_name ) < 0 )
857         return -1;
858 
859     if ( ! *gravity_name || ( gravity = gravity_val( gravity_name ) ) == -1 )
860     {
861         ff.pos = old_pos;
862         fl_free( gravity_name );
863         return -1;
864     }
865 
866     *p = gravity;
867     fl_free( gravity_name );
868     return 0;
869 }
870 
871 
872 /***************************************
873  ***************************************/
874 
875 static int
ff_match_unit(int * p)876 ff_match_unit( int * p )
877 {
878     char *unit_name;
879     char *old_pos = ff.pos;
880     int unit;
881 
882     if ( ff_match_spaceless_string( &unit_name ) < 0 )
883         return -1;
884 
885     if ( ! *unit_name || ( unit = unit_val( unit_name ) ) == -1 )
886     {
887         ff.pos = old_pos;
888         fl_free( unit_name );
889         return -1;
890     }
891 
892     *p = unit;
893     fl_free( unit_name );
894     return 0;
895 }
896 
897 /***************************************
898  ***************************************/
899 
900 static int
ff_match_key(char ** p)901 ff_match_key( char ** p )
902 {
903     char *ep = ff.pos;
904     char *np;
905     char old_c;
906 
907     *p = NULL;
908 
909     while ( *ep && *ep != ':' )
910         ep++;
911 
912     if ( ! *ep )
913         return -1;
914 
915     np = ep-- + 1;
916 
917     while ( ep > ff.pos && isspace( ( unsigned char ) *ep ) )
918         ep--;
919 
920     if ( ep == ff.pos )
921         return -1;
922 
923     old_c = *++ep;
924     *ep = '\0';
925 
926     *p = fl_strdup( ff.pos );
927 
928     *ep = old_c;
929     ff.pos = np;
930 
931     return 0;
932 }
933 
934 
935 /***************************************
936  ***************************************/
937 
938 static int
ff_match_type(char ** p)939 ff_match_type( char ** p )
940 {
941     return ff_match_var( p );
942 }
943 
944 
945 /***************************************
946  * Function for reading data from .fd files in a fscanf()-like way.
947  *
948  * The format string may contain the following:
949  *  a) text which must match the text in the string at that position
950  *  b) %l   match long  (requires long *)
951  *  b) %d   match int (requires int *)
952  *  c) %u   match unsigned int (requires int *)
953  *  d) %D   match FL_Coord (requires FL_Coord *)
954  *  e) %U   match FL_Coord with positive value (requires FL_Coord *)
955  *  f) %s   match string (trimmed of spaces at start and end) (requires char **)
956  *  g) %S   match string (with all spaces) (requires char **)
957  *  h) %h   match string, stopping at the first space (requires char **)
958  *  i) %f   match single-precision floating point value (requires float *)
959  *  j) %D   match double floating point value (requires double *)
960  *  k) %o   match object class (requires int *)
961  *  l) %t   match type (requires char **)
962  *  m) %b   match boxtype (requires int *)
963  *  n) %c   match color (requires FL_COLOR *)
964  *  o) %a   match align (requires int *)
965  *  p) %p   match lstyle (requires int *)
966  *  q) %q   match lsize (requires int *)
967  *  r) %r   match resize (requires int *)
968  *  s) %g   match gravity (requires int *)
969  *  t) %x   match unit (requires int *)
970  *  u) %v   match C variable  (requires char **)
971  *  v) %k   match a key (word(s) with a final colon) (requires char **)
972  *
973  * In case a string gets returned a copy must be made before the next
974  * call of this function.
975  * The function returns the number of items matched or a negative
976  * value on failure (in that case an error message is output).
977  ***************************************/
978 
979 int
ff_read(const char * format,...)980 ff_read( const char * format,
981          ... )
982 {
983     va_list ap;
984     char *fmt;
985     const char *fp;
986     int cnt = 0;
987     char last = '\0';
988 
989     if ( ! ff.line )
990         return -1;
991 
992     format = ff_skip_spaces( format );
993 
994     if ( ! format || ! *format )
995     {
996         M_err( "ff_read", "Invalid argument(s)" );
997         return FF_READ_FAILURE;
998     }
999 
1000     fp = fmt = fl_strdup( format );
1001 
1002     va_start( ap, format );
1003 
1004     while ( *fp )
1005     {
1006         if ( *fp != '%' )
1007         {
1008             if ( ! ( fp = ff_match_text( fp ) ) )
1009             {
1010                 va_end( ap );
1011                 return FF_READ_FAILURE;
1012             }
1013 
1014             last = '\0';
1015         }
1016         else
1017         {
1018             int r;
1019 
1020             switch ( *++fp )
1021             {
1022                 case 'l' :                    /* long int */
1023                     r = ff_match_long( va_arg( ap, long * ) );
1024                     break;
1025 
1026                 case 'd' :                    /* int */
1027                     r = ff_match_int( va_arg( ap, int * ) );
1028                     break;
1029 
1030                 case 'u' :                    /* unsigned int */
1031                     r = ff_match_uint( va_arg( ap, unsigned int * ) );
1032                     break;
1033 
1034                 case 'D' :                    /* FL_Coord ('U' for positive) */
1035                 case 'U' :
1036                     r = ff_match_coord( va_arg( ap, FL_Coord * ), *fp == 'U' );
1037                     break;
1038 
1039                 case 's' :                    /* trimmed string */
1040                     r = ff_match_trimmed_string( va_arg( ap, char ** ) );
1041                     break;
1042 
1043                 case 'S' :                    /* string (with spaces) */
1044                     r = ff_match_string( va_arg( ap, char ** ) );
1045                     break;
1046 
1047                 case 'h' :               /* string (without embedded spaces) */
1048                     r = ff_match_spaceless_string( va_arg( ap, char ** ) );
1049                     break;
1050 
1051                 case 'f' :                    /* float */
1052                     r = ff_match_float( va_arg( ap, float * ) );
1053                     break;
1054 
1055                 case 'F' :                    /* double */
1056                     r = ff_match_double( va_arg( ap, double * ) );
1057                     break;
1058 
1059                 case 'o' :                    /* object class */
1060                     r = ff_match_objclass( va_arg( ap, int * ) );
1061                     break;
1062 
1063                 case 't' :                    /* object type */
1064                     r = ff_match_type( va_arg( ap, char ** ) );
1065                     break;
1066 
1067                 case 'b' :                    /* box type */
1068                     r = ff_match_boxtype( va_arg( ap, int * ) );
1069                     break;
1070 
1071                 case 'c' :                    /* color */
1072                     r = ff_match_color( va_arg( ap, FL_COLOR * ) );
1073                     break;
1074 
1075                 case 'a' :                    /* alignment value */
1076                     r = ff_match_align( va_arg( ap, int * ) );
1077                     break;
1078 
1079                 case 'p' :                    /* lstyle value */
1080                     r = ff_match_lstyle( va_arg( ap, int * ) );
1081                     break;
1082 
1083                 case 'q' :                    /* lsize value */
1084                     r = ff_match_lsize( va_arg( ap, int * ) );
1085                     break;
1086 
1087                 case 'r' :                    /* resize value */
1088                     r = ff_match_resize( va_arg( ap, int * ) );
1089                     break;
1090 
1091                 case 'g' :                    /* gravity value */
1092                     r = ff_match_gravity( va_arg( ap, int * ) );
1093                     break;
1094 
1095                 case 'x' :                    /* unit value */
1096                     r = ff_match_unit( va_arg( ap, int * ) );
1097                     break;
1098 
1099                 case 'v' :                    /* C variable name */
1100                     r = ff_match_var( va_arg( ap, char ** ) );
1101                     break;
1102 
1103                 case 'k' :                    /* key with trailing colon */
1104                     r = ff_match_key( va_arg( ap, char ** ) );
1105                     break;
1106 
1107                 default :                     /* error, wrong format */
1108                     va_end( ap );
1109                     fl_free( fmt );
1110                     M_err( "ff_read", "Invalid argument(s)" );
1111                     return FF_READ_FAILURE;
1112             }
1113 
1114             last = *fp;
1115 
1116             if ( r < 0 )
1117                 break;
1118 
1119             cnt++;
1120             fp++;
1121         }
1122 
1123         ff.pos = ff_skip_spaces( ff.pos );
1124         fp = ff_skip_spaces( fp );
1125     }
1126 
1127     va_end( ap );
1128     fl_free( fmt );
1129 
1130     /* If we're at the end of the line read in the next - except when the
1131        last request was for a key, in that case the next one will be for
1132        a value and it's allowed that no value exists even when there's a
1133        key... */
1134 
1135     if ( last != 'k' && ! *ff.pos )
1136         ff_get_line( );
1137 
1138     return cnt;
1139 }
1140 
1141 
1142 /*
1143  * Local variables:
1144  * tab-width: 4
1145  * indent-tabs-mode: nil
1146  * End:
1147  */
1148