1 /*
2  * gEDA - GNU Electronic Design Automation
3  * This file is a part of gerbv.
4  *
5  *   Copyright (C) 2000-2003 Stefan Petersen (spe@stacken.kth.se)
6  *   Copyright (c) 2008 Dan McMahill
7  *
8  * $Id$
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
23  */
24 
25 /*! \file project.c
26     \brief Routines for loading and saving project files.
27     \ingroup gerbv
28  */
29 
30 
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34 
35 #ifdef HAVE_STDLIB_H
36 #include <stdlib.h>
37 #endif
38 
39 #include <ctype.h>
40 #include <stdio.h>
41 
42 #ifdef HAVE_STRING_H
43 #include <string.h>
44 #endif
45 
46 #ifdef HAVE_SYS_TYPES_H
47 #include <sys/types.h>
48 #endif
49 
50 #ifdef HAVE_SYS_STAT_H
51 #include <sys/stat.h>
52 #endif
53 
54 #ifdef HAVE_UNISTD_H
55 #include <unistd.h>
56 #endif
57 
58 #include <errno.h>
59 #include <math.h>
60 
61 #include "common.h"
62 #include "gerbv.h"
63 #include "gerb_file.h"
64 #include "lrealpath.h"
65 #include "project.h"
66 #include "scheme-private.h"
67 #include "main.h"
68 #include "interface.h"
69 #include "render.h"
70 
71 
72 /*
73  * update this if the project file format changes.
74  *
75  * The format *must* be major.minor[A-Z]
76  *
77  * Do *not* update this simply because we have a new gerbv
78  * version.
79  *
80  * If you bump this version, then you must also bump the
81  * version of gerbv.  For example, supplse the file version
82  * is 2.0A and gerbv has 2.4B in configure.ac.  If you change
83  * the file format version, you should change both the version
84  * here *and* configure.ac to 2.4C.
85  */
86 
87 #define GERBV_PROJECT_FILE_VERSION "2.0A"
88 
89 /* default version for project files that do not specify a version.
90  * This is assumed for all older project files.
91  */
92 #define GERBV_DEFAULT_PROJECT_FILE_VERSION "1.9A"
93 
94 /*
95  * List of versions that we can load with this version of
96  * gerbv
97  */
98 static const char * known_versions[] = {
99   "1.9A",
100   "2.0A",
101   NULL
102 };
103 
104 /* DEBUG printing.  #define DEBUG 1 in config.h to use this fcn. */
105 #define dprintf if(DEBUG) printf
106 
107 static project_list_t *project_list_top = NULL;
108 
109 static const int alpha_def_value = 177*257;
110 
111 /* When a project file is loaded, this variable is set to the
112  * version of the project file.  That can be used by various
113  * functions which may need to do something different.
114  */
115 static int current_file_version = 0;
116 
117 
118 /*
119  * Converts a string like "2.1A" "2.12C" or "3.2ZA" to the int
120  * we use internally
121  */
122 static int
version_str_to_int(const char * str)123 version_str_to_int( const char * str)
124 {
125   int r = 0;
126   gchar *dup, *tmps, *ptr;
127 
128   if(str == NULL) {
129     return -1;
130   } else {
131     dprintf("%s(\"%s\")\n", __FUNCTION__, str);
132 
133 
134     /*
135      * Extract out the major number (versions are strings like 2.1A)
136      * and we want the "2" here.
137      */
138     tmps = g_strdup(str);
139     ptr = tmps;
140     while(*ptr != '\0' && *ptr != '.') { ptr++; }
141     if( *ptr == '\0' ) {
142       /* this should not have happened */
143       return -1;
144     }
145 
146     *ptr = '\0';
147     r = 10000 * atoi(tmps);
148     dprintf("%s():  Converted \"%s\" to r = %d\n", __FUNCTION__, tmps, r);
149 
150     g_free(tmps);
151 
152     /*
153      * Extract out the minor number (versions are strings like 2.1A)
154      * and we want the "1" here.
155      */
156     dup = g_strdup(str);
157     tmps = dup;
158     ptr = tmps;
159 
160     while(*ptr != '\0' && *ptr != '.') { ptr++; }
161     if( *ptr == '\0' ) {
162       /* this should not have happened */
163       return -1;
164     }
165     ptr++;
166     tmps = ptr;
167 
168     while(*ptr != '\0' && isdigit( (int) *ptr)) { ptr++; }
169     if( *ptr == '\0' ) {
170       /* this should not have happened */
171       return -1;
172     }
173 
174     *ptr = '\0';
175     r += 100 * atoi(tmps);
176     dprintf("%s():  Converted \"%s\" to r = %d\n", __FUNCTION__, tmps, r);
177 
178     g_free(dup);
179 
180     /*
181      * Extract out the revision letter(s) (versions are strings like 2.1A)
182      * and we want the "A" here.
183      */
184 
185     dup = g_strdup(str);
186     tmps = dup;
187     ptr = tmps;
188 
189     while(*ptr != '\0' && (isdigit( (int) *ptr) || *ptr == '.') ) { ptr++; }
190     if( *ptr == '\0' ) {
191       /* this should not have happened */
192       return -1;
193     }
194     tmps = ptr;
195 
196     dprintf("%s():  Processing \"%s\"\n", __FUNCTION__, tmps);
197 
198     if( strlen(tmps) == 1) {
199       r += *tmps - 'A' + 1;
200       dprintf( "%s():  Converted \"%s\" to r = %d\n", __FUNCTION__, tmps, r);
201     } else if( strlen(tmps) == 2 ) {
202       if( *tmps == 'Z' ) {
203 	r += 26;
204 	tmps++;
205 	r += *tmps - 'A' + 1;
206       } else {
207 	/* this should not have happened */
208 	return -1;
209       }
210     } else {
211       /* this should not have happened */
212       return -1;
213     }
214 
215     g_free(dup);
216 
217   }
218   return r;
219 
220 }
221 
222 /*
223  * convert the internal int we use for version numbers
224  * to the string that users can deal with
225  */
226 static char *
version_int_to_str(int ver)227 version_int_to_str( int ver )
228 {
229   int major, minor, teeny;
230   char l[3];
231   char *str;
232 
233   l[0] = '\0';
234   l[1] = '\0';
235   l[2] = '\0';
236 
237   major = ver / 10000;
238   minor = (ver - 10000*major) / 100;
239   teeny = (ver - 10000*major - 100*minor);
240   if(teeny >= 1 && teeny <= 26) {
241     l[0] = 'A' + teeny - 1;
242   }  else if(teeny > 26 && teeny <= 52) {
243     l[0] = 'Z';
244     l[1] = 'A' + teeny - 26 - 1;
245   }
246 
247   str = g_strdup_printf("%d.%d%s", major, minor, l);
248   return str;
249 }
250 
251 static int
check_vector_and_length(scheme * sc,pointer value,unsigned int length,const char * item)252 check_vector_and_length(scheme *sc, pointer value,
253 		unsigned int length, const char *item)
254 {
255     if (!sc->vptr->is_vector(value)) {
256 	GERB_MESSAGE(_("'%s' parameter not a vector"), item);
257 
258 	return 1;
259     }
260 
261     if (sc->vptr->vector_length(value) != length) {
262 	GERB_MESSAGE(_("'%s' vector of incorrect length"), item);
263 
264 	return 2;
265     }
266 
267     return 0;
268 }
269 
270 static void
get_color(scheme * sc,pointer value,int * color)271 get_color(scheme *sc, pointer value, int *color)
272 {
273     int i;
274     pointer elem;
275 
276     if (check_vector_and_length(sc, value, 3, "color"))
277 	    return;
278 
279     for (i = 0; i < 3; i++) {
280 	elem = sc->vptr->vector_elem(value, i);
281 	if (sc->vptr->is_integer(elem) && sc->vptr->is_number(elem))
282 	    color[i] = sc->vptr->ivalue(elem);
283 	else {
284 	    color[i] = -1;
285 	    GERB_MESSAGE(_("Illegal color in projectfile"));
286 	}
287     }
288 
289     return;
290 } /* get_color */
291 
292 static void
get_alpha(scheme * sc,pointer value,int * alpha)293 get_alpha(scheme *sc, pointer value, int *alpha)
294 {
295     pointer elem;
296 
297     if (check_vector_and_length(sc, value, 1, "alpha"))
298 	return;
299 
300     elem = sc->vptr->vector_elem(value, 0);
301     if (sc->vptr->is_integer(elem) && sc->vptr->is_number(elem)) {
302         *alpha = sc->vptr->ivalue(elem);
303         return;
304     }
305 
306     GERB_MESSAGE(_("Illegal alpha value in projectfile"));
307 } /* get_alpha */
308 
309 static void
get_double(scheme * sc,pointer value,char * item,double * x,double def)310 get_double(scheme *sc, pointer value, char *item, double *x, double def)
311 {
312     pointer elem;
313 
314     if (check_vector_and_length(sc, value, 1, item))
315 	return;
316 
317     elem = sc->vptr->vector_elem(value, 0);
318     if (sc->vptr->is_real(elem) && sc->vptr->is_number(elem)) {
319 	*x = sc->vptr->rvalue(elem);
320     } else {
321 	*x = def;
322 	GERB_MESSAGE(_("Illegal %s in projectfile"), item);
323     }
324 } /* get_double */
325 
326 static void
get_double_pair(scheme * sc,pointer value,char * item,double * x,double * y,double def)327 get_double_pair(scheme *sc, pointer value, char *item,
328 	double *x, double *y, double def)
329 {
330     pointer elem;
331 
332     if (check_vector_and_length(sc, value, 2, item))
333 	return;
334 
335     elem = sc->vptr->vector_elem(value, 0);
336     if (sc->vptr->is_real(elem) && sc->vptr->is_number(elem)) {
337 	*x = sc->vptr->rvalue(elem);
338     } else {
339 	*x = def;
340 	GERB_MESSAGE(_("Illegal %s in projectfile"), item);
341     }
342 
343     elem = sc->vptr->vector_elem(value, 1);
344     if (sc->vptr->is_real(elem) && sc->vptr->is_number(elem)) {
345 	*y = sc->vptr->rvalue(elem);
346     } else {
347 	*y = def;
348 	GERB_MESSAGE(_("Illegal %s in projectfile"), item);
349     }
350 } /* get_double_pair */
351 
352 static void
get_bool_pair(scheme * sc,pointer value,char * item,char * x,char * y,char def)353 get_bool_pair(scheme *sc, pointer value, char *item,
354 		char *x, char *y, char def)
355 {
356     pointer elem;
357 
358     if (check_vector_and_length(sc, value, 2, item))
359 	return;
360 
361     elem = sc->vptr->vector_elem(value, 0);
362     if (elem == sc->F) {
363 	*x = 0;
364     } else if (elem == sc->T) {
365 	*x = 1;
366     } else {
367 	*x = def;
368 	GERB_MESSAGE(_("Illegal %s in projectfile"), item);
369     }
370 
371     elem = sc->vptr->vector_elem(value, 1);
372     if (elem == sc->F) {
373 	*y = 0;
374     } else if (elem == sc->T) {
375 	*y = 1;
376     } else {
377 	*y = def;
378 	GERB_MESSAGE(_("Illegal %s in projectfile"), item);
379     }
380 } /* get_bool_pair */
381 
382 /* ----------------------------------------------------------------------
383  * Figure out the canonical name of the executed program
384  * and fix up the defaults for various paths.  This is largely
385  * taken from InitPaths() in main.c from pcb.
386  */
387 static char *bindir = NULL;
388 static char *exec_prefix = NULL;
389 static char *pkgdatadir = NULL;
390 static gchar *scmdatadir = NULL;
391 
392 /* this really should not be needed but it could
393  * be hooked in to appease malloc debuggers as
394  * we don't otherwise free these variables.  However,
395  * they only get malloc-ed once ever so this
396  * is a fixed leak of a small size.
397  */
398 #if 0
399 void
400 destroy_paths ()
401 {
402   if (bindir != NULL) {
403     free (bindir);
404     bindir = NULL;
405   }
406 
407   if (exec_prefix != NULL) {
408     free (exec_prefix);
409     exec_prefix = NULL;
410   }
411 
412   if (pkgdatadir != NULL) {
413     free (pkgdatadir);
414     pkgdatadir = NULL;
415   }
416 
417   if (scmdatadir != NULL) {
418     g_free (scmdatadir);
419     scmdatadir = NULL;
420   }
421 
422 
423 }
424 #endif
425 
426 static void
init_paths(char * argv0)427 init_paths (char *argv0)
428 {
429   size_t l;
430   int i;
431   int haspath;
432   char *t1, *t2;
433   int found_bindir = 0;
434 
435   /* Only do this stuff once */
436   if (bindir != NULL )
437     return;
438 
439   /* see if argv0 has enough of a path to let lrealpath give the
440    * real path.  This should be the case if you invoke gerbv with
441    * something like /usr/local/bin/gerbv or ./gerbv or ./foo/gerbv
442    * but if you just use gerbv and it exists in your path, you'll
443    * just get back gerbv again.
444    */
445 
446   haspath = 0;
447   for (i = 0; i < strlen (argv0) ; i++)
448     {
449       if (argv0[i] == GERBV_DIR_SEPARATOR_C)
450         haspath = 1;
451     }
452 
453   dprintf("%s (%s): haspath = %d\n", __FUNCTION__, argv0, haspath);
454   if (haspath)
455     {
456       bindir = strdup (lrealpath (argv0));
457       found_bindir = 1;
458     }
459   else
460     {
461       char *path, *p, *tmps;
462       struct stat sb;
463       int r;
464 
465       tmps = getenv ("PATH");
466 
467       if (tmps != NULL)
468         {
469           path = strdup (tmps);
470 
471           /* search through the font path for a font file */
472           for (p = strtok (path, GERBV_PATH_DELIMETER); p && *p;
473                p = strtok (NULL, GERBV_PATH_DELIMETER))
474             {
475 	      dprintf ("Looking for %s in %s\n", argv0, p);
476               if ( (tmps = malloc ( (strlen (argv0) + strlen (p) + 2) * sizeof (char))) == NULL )
477                 {
478                   fprintf (stderr, "malloc failed in %s()\n", __FUNCTION__);
479                   exit (1);
480                 }
481               sprintf (tmps, "%s%s%s", p, GERBV_DIR_SEPARATOR_S, argv0);
482               r = stat (tmps, &sb);
483               if (r == 0)
484                 {
485 		  dprintf ("Found it:  \"%s\"\n", tmps);
486                   bindir = lrealpath (tmps);
487                   found_bindir = 1;
488                   free (tmps);
489                   break;
490                 }
491               free (tmps);
492             }
493           free (path);
494         }
495     }
496   dprintf ("%s():  bindir = \"%s\"\n", __FUNCTION__, bindir);
497 
498 
499   if (found_bindir)
500     {
501       /* strip off the executible name leaving only the path */
502       t2 = NULL;
503       t1 = strchr (bindir, GERBV_DIR_SEPARATOR_C);
504       while (t1 != NULL && *t1 != '\0')
505         {
506           t2 = t1;
507           t1 = strchr (t2 + 1, GERBV_DIR_SEPARATOR_C);
508         }
509       if (t2 != NULL)
510         *t2 = '\0';
511       dprintf ("After stripping off the executible name, we found\n");
512       dprintf ("bindir = \"%s\"\n", bindir);
513 
514     }
515   else
516     {
517       /* we have failed to find out anything from argv[0] so fall back to the original
518        * install prefix
519        */
520        bindir = strdup (BINDIR);
521     }
522 
523   /* now find the path to exec_prefix */
524   l = strlen (bindir) + 1 + strlen (BINDIR_TO_EXECPREFIX) + 1;
525   if ( (exec_prefix = (char *) malloc (l * sizeof (char) )) == NULL )
526     {
527       fprintf (stderr, "malloc failed in %s()\n", __FUNCTION__);
528       exit (1);
529     }
530   sprintf (exec_prefix, "%s%s%s", bindir, GERBV_DIR_SEPARATOR_S,
531            BINDIR_TO_EXECPREFIX);
532 
533   /* now find the path to PKGDATADIR */
534   l = strlen (bindir) + 1 + strlen (BINDIR_TO_PKGDATADIR) + 1;
535   if ( (pkgdatadir = (char *) malloc (l * sizeof (char) )) == NULL )
536     {
537       fprintf (stderr, "malloc failed in %s()\n", __FUNCTION__);
538       exit (1);
539     }
540   sprintf (pkgdatadir, "%s%s%s", bindir, GERBV_DIR_SEPARATOR_S,
541            BINDIR_TO_PKGDATADIR);
542 
543   scmdatadir = g_strdup_printf ("%s%s%s", pkgdatadir, GERBV_DIR_SEPARATOR_S, SCMSUBDIR);
544 
545   dprintf ("%s():  bindir      = %s\n", __FUNCTION__, bindir);
546   dprintf ("%s():  exec_prefix = %s\n", __FUNCTION__, exec_prefix);
547   dprintf ("%s():  pkgdatadir  = %s\n", __FUNCTION__, pkgdatadir);
548   dprintf ("%s():  scmdatadir  = %s\n", __FUNCTION__, scmdatadir);
549 
550 }
551 
552 
553 static char *
get_value_string(scheme * sc,pointer value)554 get_value_string(scheme *sc, pointer value)
555 {
556     if (!sc->vptr->is_string(value))
557 	return NULL;
558 
559     return sc->vptr->string_value(value);
560 } /* get_value_string */
561 
562 
563 /** Conversion of '\' into '/' and vice versa for compatibility under WIN32
564     platforms. */
565 static char *
convert_path_separators(char * path,int conv_flag)566 convert_path_separators(char* path, int conv_flag)
567 {
568 #if defined (__MINGW32__)
569     char     *hit_in_path;
570 
571     switch (conv_flag) {
572 
573     case MINGW_UNIX:
574         while ((hit_in_path = strchr(path, '\\'))) {
575             *hit_in_path = '/';
576         }
577         break;
578     case UNIX_MINGW:
579 	while ((hit_in_path = strchr(path, '/'))) {
580             *hit_in_path = '\\';
581         }
582         break;
583     }
584 #endif
585 
586     return path;
587 }/* convert_path_separators */
588 
589 
590 static pointer
define_layer(scheme * sc,pointer args)591 define_layer(scheme *sc, pointer args)
592 {
593     pointer car_el, cdr_el, name, value;
594     project_list_t *plist;
595     const char *str;
596     int layerno;
597 
598     dprintf("--> entering %s: %s\n", __FILE__, __func__);
599 
600     if (!sc->vptr->is_pair(args)) {
601 	GERB_MESSAGE(_("%s(): too few arguments"), __func__);
602 
603 	return sc->F;
604     }
605 
606     car_el = sc->vptr->pair_car(args);
607     cdr_el = sc->vptr->pair_cdr(args);
608 
609     if (!sc->vptr->is_integer(car_el) || !sc->vptr->is_number(car_el)) {
610 	GERB_MESSAGE(_("%s(): layer number missing/incorrect"), __func__);
611 
612 	return sc->F;
613     }
614 
615     layerno = sc->vptr->ivalue(car_el);
616     dprintf("    layerno = %d\n", layerno);
617 
618     car_el = sc->vptr->pair_car(cdr_el);
619     cdr_el = sc->vptr->pair_cdr(cdr_el);
620 
621     plist = g_new0(project_list_t, 1);
622     plist->next = project_list_top;
623     project_list_top = plist;
624     plist->layerno = layerno;
625     plist->visible = 1;
626     plist->n_attr = 0;
627     plist->attr_list = NULL;
628     plist->translate_x = plist->translate_y = 0.0;
629     plist->scale_x = plist->scale_y = 1.0;
630     plist->mirror_x = plist->mirror_y = 0;
631 
632     /* Set default alpha value, if alpha value is not in project file */
633     plist->alpha = alpha_def_value;
634 
635     while (sc->vptr->is_pair(car_el)) {
636 
637 	name = sc->vptr->pair_car(car_el);
638 	value =  sc->vptr->pair_cdr(car_el);
639 
640 	if (!sc->vptr->is_symbol(name)) {
641 	    GERB_MESSAGE(_("%s(): non-symbol found, ignoring"), __func__);
642 	    goto end_name_value_parse;
643 	}
644 
645 	str = sc->vptr->symname(name);
646 	if (strcmp(str, "color") == 0) {
647 	    get_color(sc, value, plist->rgb);
648 	} else if (strcmp(str, "alpha") == 0) {
649 	    get_alpha(sc, value, &plist->alpha);
650 	} else if (strcmp(str, "translate") == 0) {
651 	    get_double_pair(sc, value, "translate",
652 		    &plist->translate_x, &plist->translate_y, 0.0);
653 	} else if (strcmp(str, "rotation") == 0) {
654 	    get_double(sc, value, "rotation", &plist->rotation, 0.0);
655 	} else if (strcmp(str, "scale") == 0) {
656 	    get_double_pair(sc, value, "scale",
657 		    &plist->scale_x, &plist->scale_y, 1.0);
658 	} else if (strcmp(str, "mirror") == 0) {
659 	    get_bool_pair(sc, value, "mirror",
660 		    &plist->mirror_x, &plist->mirror_y, 0);
661 	} else if (strcmp(str, "filename") == 0) {
662             plist->filename = g_strdup(get_value_string(sc, value));
663 	    plist->filename = convert_path_separators(plist->filename,
664 			    UNIX_MINGW);
665             plist->is_pnp = 0;
666 	} else if (strcmp(str, "pick_and_place") == 0) {
667 	    plist->filename = g_strdup(get_value_string(sc, value));
668 	    plist->filename = convert_path_separators(plist->filename,
669 			    UNIX_MINGW);
670 	    plist->is_pnp = 1;
671 	} else if (strcmp(str, "inverted") == 0) {
672 	    if (value == sc->F) {
673 		plist->inverted = 0;
674 	    } else if (value == sc->T) {
675 		plist->inverted = 1;
676 	    } else {
677 		GERB_MESSAGE(_("Argument to inverted must be #t or #f"));
678 	    }
679 	} else if (strcmp(str, "visible") == 0) {
680 	    if (value == sc->F) {
681 		plist->visible = 0;
682 	    } else if (value == sc->T) {
683 		plist->visible = 1;
684 	    } else {
685 		GERB_MESSAGE(_("Argument to visible must be #t or #f"));
686 	    }
687        	} else if (strcmp(str, "attribs") == 0) {
688 	    pointer attr_car_el, attr_cdr_el;
689 	    pointer attr_name, attr_type, attr_value;
690 	    char *type;
691 
692 	    dprintf ("Parsing file attributes\n");
693 
694 	    attr_car_el = sc->vptr->pair_car (value);
695 	    attr_cdr_el = sc->vptr->pair_cdr (value);
696 	    while (sc->vptr->is_pair(attr_car_el)) {
697 		int p = plist->n_attr;
698 		plist->n_attr++;
699 		plist->attr_list = (gerbv_HID_Attribute *)
700 		    realloc (plist->attr_list,
701 			     plist->n_attr * sizeof (gerbv_HID_Attribute));
702 		if (plist->attr_list == NULL ) {
703 		    fprintf (stderr, _("%s():  realloc failed\n"), __FUNCTION__);
704 		    exit (1);
705 		}
706 
707 		/* car */
708 		attr_name = sc->vptr->pair_car(attr_car_el);
709 
710 		/* cadr */
711 		attr_type =  sc->vptr->pair_cdr (attr_car_el);
712 		attr_type =  sc->vptr->pair_car (attr_type);
713 
714 		/* caddr */
715 		attr_value =  sc->vptr->pair_cdr (attr_car_el);
716 		attr_value =  sc->vptr->pair_cdr (attr_value);
717 		attr_value =  sc->vptr->pair_car (attr_value);
718 
719 		dprintf ("  attribute %s, type is %s, value is ",
720 			 sc->vptr->symname(attr_name),
721 			 sc->vptr->symname(attr_type));
722 
723 		plist->attr_list[p].name = strdup (sc->vptr->symname (attr_name));
724 
725 		type = sc->vptr->symname (attr_type);
726 
727 		if (strcmp (type, "label") == 0) {
728 		    dprintf ("%s", sc->vptr->string_value (attr_value));
729 		    plist->attr_list[p].type = HID_Label;
730 		    plist->attr_list[p].default_val.str_value =
731 			strdup (sc->vptr->string_value (attr_value));
732 
733 		} else if (strcmp (type, "integer") == 0) {
734 		    dprintf ("%ld", sc->vptr->ivalue (attr_value));
735 		    plist->attr_list[p].type = HID_Integer;
736 		    plist->attr_list[p].default_val.int_value =
737 			sc->vptr->ivalue (attr_value);
738 
739 		} else if (strcmp (type, "real") == 0) {
740 		    dprintf ("%g", sc->vptr->rvalue (attr_value));
741 		    plist->attr_list[p].type = HID_Real;
742 		    plist->attr_list[p].default_val.real_value =
743 			sc->vptr->rvalue (attr_value);
744 
745 		} else if (strcmp (type, "string") == 0) {
746 		    dprintf ("%s", sc->vptr->string_value (attr_value));
747 		    plist->attr_list[p].type = HID_String;
748 		    plist->attr_list[p].default_val.str_value =
749 			strdup (sc->vptr->string_value (attr_value));
750 
751 		} else if (strcmp (type, "boolean") == 0) {
752 		    dprintf ("%ld", sc->vptr->ivalue (attr_value));
753 		    plist->attr_list[p].type = HID_Boolean;
754 		    plist->attr_list[p].default_val.int_value =
755 			sc->vptr->ivalue (attr_value);
756 
757 		} else if (strcmp (type, "enum") == 0) {
758 		    dprintf ("%ld", sc->vptr->ivalue (attr_value));
759 		    plist->attr_list[p].type = HID_Enum;
760 		    plist->attr_list[p].default_val.int_value =
761 			sc->vptr->ivalue (attr_value);
762 
763 		} else if (strcmp (type, "mixed") == 0) {
764 		    plist->attr_list[p].type = HID_Mixed;
765 		    plist->attr_list[p].default_val.str_value = NULL;
766 		    fprintf (stderr, _("%s():  WARNING:  HID_Mixed is not yet supported\n"),
767 			     __FUNCTION__);
768 
769 		} else if (strcmp (type, "path") == 0) {
770 		    dprintf ("%s", sc->vptr->string_value (attr_value));
771 		    plist->attr_list[p].type = HID_Path;
772 		    plist->attr_list[p].default_val.str_value =
773 			strdup (sc->vptr->string_value (attr_value));
774 		} else {
775 		    fprintf (stderr, _("%s():  Unknown attribute type: \"%s\"\n"),
776 			     __FUNCTION__, type);
777 		}
778 		dprintf ("\n");
779 
780 		attr_car_el = sc->vptr->pair_car(attr_cdr_el);
781 		attr_cdr_el = sc->vptr->pair_cdr(attr_cdr_el);
782 	    }
783 	} else {
784             GERB_MESSAGE(_("Ignoring \"%s\" in project file"), str);
785 	}
786 
787 end_name_value_parse:
788 	car_el = sc->vptr->pair_car(cdr_el);
789 	cdr_el = sc->vptr->pair_cdr(cdr_el);
790     }
791 
792     return sc->NIL;
793 } /* define_layer */
794 
795 static pointer
set_render_type(scheme * sc,pointer args)796 set_render_type(scheme *sc, pointer args)
797 {
798     pointer car_el;
799     int r;
800 
801     dprintf("--> entering project.c:%s()\n", __FUNCTION__);
802 
803     if (!sc->vptr->is_pair(args)){
804 	GERB_MESSAGE(_("set-render-type!: Too few arguments"));
805 	return sc->F;
806     }
807 
808     car_el = sc->vptr->pair_car(args);
809 
810     r = sc->vptr->ivalue (car_el);
811     dprintf ("%s():  Setting render type to %d\n", __FUNCTION__, r);
812     interface_set_render_type (r);
813 
814     return sc->NIL;
815 } /* set_render_type */
816 
817 static pointer
gerbv_file_version(scheme * sc,pointer args)818 gerbv_file_version(scheme *sc, pointer args)
819 {
820     pointer car_el;
821     int r;
822     char *vstr;
823     char *tmps;
824 
825     dprintf("--> entering project.c:%s()\n", __FUNCTION__);
826 
827     if (!sc->vptr->is_pair(args)){
828 	GERB_MESSAGE(_("gerbv-file-version!: Too few arguments"));
829 	return sc->F;
830     }
831 
832     car_el = sc->vptr->pair_car(args);
833     vstr = get_value_string(sc, car_el);
834 
835     /* find our internal integer code */
836     r = version_str_to_int( vstr );
837 
838     if( r == -1) {
839       r = version_str_to_int( GERBV_DEFAULT_PROJECT_FILE_VERSION );
840       GERB_MESSAGE(_("The project file you are attempting to load has specified that it\n"
841 		   "uses project file version \"%s\" but this string is not\n"
842 		   "a valid version.  Gerbv will attempt to load the file using\n"
843 		   "version \"%s\".  You may experience unexpected results."),
844 		   vstr, version_int_to_str( r ));
845       vstr = GERBV_DEFAULT_PROJECT_FILE_VERSION;
846     }
847     if( DEBUG ) {
848       tmps = version_int_to_str( r );
849       printf (_("%s():  Read a project file version of %s (%d)\n"), __FUNCTION__, vstr, r);
850       printf (_("      Translated back to \"%s\"\n"), tmps);
851       g_free (tmps);
852     }
853 
854     dprintf ("%s():  Read a project file version of %s (%d)\n", __FUNCTION__, vstr, r);
855 
856     if ( r > version_str_to_int( GERBV_PROJECT_FILE_VERSION )) {
857         /* The project file we're trying to load is too new for this version of gerbv */
858 	GERB_MESSAGE(_("The project file you are attempting to load is version \"%s\"\n"
859 	    "but this copy of gerbv is only capable of loading project files\n"
860 	    "using version \"%s\" or older.  You may experience unexpected results."),
861 		     vstr, GERBV_PROJECT_FILE_VERSION);
862     } else {
863       int i = 0;
864       int vok = 0;
865 
866       while( known_versions[i] != NULL ) {
867 	if( strcmp( known_versions[i], vstr) == 0 ) {
868 	  vok = 1;
869 	}
870 	i++;
871       }
872 
873       if( ! vok ) {
874 	/* The project file we're trying to load is not too new
875 	 * but it is unknown to us
876 	 */
877 	GERB_MESSAGE(_("The project file you are attempting to load is version \"%s\"\n"
878 		     "which is an unknown version.\n"
879 		     "You may experience unexpected results."),
880 		     vstr);
881 
882       }
883     }
884 
885     /*
886      * store the version of the file we're currently loading.  This variable is used
887      * by the different functions called by the project file to do anything which is
888      * version specific.
889      */
890     current_file_version = r;
891 
892     return sc->NIL;
893 } /* gerbv_file_version */
894 
895 /** Checks whether the supplied file look like a gerbv project by
896  * reading the first line and checking if it contains gerbv-file-version
897  *
898  * Returns 0 on success -1 on open error
899   */
900 int
project_is_gerbv_project(const char * filename,gboolean * ret)901 project_is_gerbv_project(const char *filename, gboolean *ret)
902 {
903 	FILE *fd;
904 	*ret = FALSE;
905 	char *buf;
906 	const gsize buf_size = 200;
907 
908 	fd = fopen(filename, "rb");
909 	if (fd == NULL) {
910 		GERB_MESSAGE(_("Failed to open \"%s\" for reading: %s"),
911 				filename, strerror(errno));
912 		return -1;
913 	}
914 
915 	buf = (char *) g_malloc(buf_size);
916 	if (buf == NULL)
917 		GERB_FATAL_ERROR("malloc buf failed while checking for "
918 				"Gerbv project in %s()", __FUNCTION__);
919 
920 	if (fgets(buf, buf_size, fd) != NULL)
921 		*ret = (g_strrstr(buf, "gerbv-file-version") != NULL);
922 
923 	fclose(fd);
924 	g_free(buf);
925 
926 	return 0;
927 }
928 
929 /** Reads the content of a project file.
930   *  Global:\n
931   *    Background color,\n
932   *    global path,\n
933   *    corresponding pick and place file: labelled 'picknplace'\n
934   *   Per layer:\n
935   *    layer color,\n
936   *    layer filename
937   */
938 project_list_t *
read_project_file(char const * filename)939 read_project_file(char const* filename)
940 {
941     struct stat stat_info;
942     scheme *sc;
943     FILE *fd;
944     /* always let the environment variable win so one can force
945      * a particular init.scm.  Then we use the default installed
946      * directory based on where the binary has been installed to
947      * (including the possibility of relocation).  Then use the
948      * default compiled in directory.  After that try the directory
949      * where the binary lives and finally the current directory.
950      */
951     char *initdirs[] = {"$GERBV_SCHEMEINIT","", BACKEND_DIR,
952                         mainProject->execpath, ".",
953 			NULL};
954     char *initfile;
955 
956 
957     /*
958      * Figure out some directories so we can find init.scm
959      */
960     init_paths(mainProject->execname);
961     initdirs[1] = scmdatadir;
962 
963 #if defined(DEBUG)
964     if (DEBUG > 0)
965       {
966 	int i=0;
967 
968 	while(initdirs[i] != NULL) {
969 	  printf("%s():  initdirs[%d] = \"%s\"\n", __FUNCTION__, i, initdirs[i]);
970 	  i++;
971 	}
972       }
973 #endif
974 
975     /*
976      * set the current version of the project file to 1 day before we started adding
977      * versioning to the files.  While the file is being loaded, this will
978      * be set to the correct version on newer files and ignored on older files
979      */
980     current_file_version =
981 		version_str_to_int(GERBV_DEFAULT_PROJECT_FILE_VERSION);
982 
983     if (stat(filename, &stat_info) || !S_ISREG(stat_info.st_mode)) {
984 	GERB_MESSAGE(_("Failed to read %s"), filename);
985 
986 	return NULL;
987     }
988 
989     sc = scheme_init_new();
990     scheme_set_output_port_file(sc, stdout);
991 
992     if(!sc){
993 	GERB_FATAL_ERROR(_("Couldn't init scheme"));
994 	exit(1);
995     }
996 
997     errno = 0;
998     initfile = gerb_find_file("init.scm", initdirs);
999     if (initfile == NULL) {
1000 	scheme_deinit(sc);
1001 	GERB_MESSAGE(_("Problem loading init.scm (%s)"), strerror(errno));
1002 	return NULL;
1003     }
1004     dprintf("%s():  initfile = \"%s\"\n", __FUNCTION__, initfile);
1005 
1006     if ((fd = fopen(initfile, "r")) == NULL) {
1007 	scheme_deinit(sc);
1008 	GERB_MESSAGE(_("Couldn't open %s (%s)"), initfile, strerror(errno));
1009 	return NULL;
1010     }
1011 
1012     /* Force gerbv to input decimals as dots */
1013     setlocale(LC_NUMERIC, "C");
1014 
1015     sc->vptr->load_file(sc, fd);
1016     fclose(fd);
1017 
1018     sc->vptr->scheme_define(sc, sc->global_env,
1019 			    sc->vptr->mk_symbol(sc, "define-layer!"),
1020 			    sc->vptr->mk_foreign_func(sc, define_layer));
1021 
1022     sc->vptr->scheme_define(sc, sc->global_env,
1023 			    sc->vptr->mk_symbol(sc, "set-render-type!"),
1024 			    sc->vptr->mk_foreign_func(sc, set_render_type));
1025 
1026     sc->vptr->scheme_define(sc, sc->global_env,
1027 			    sc->vptr->mk_symbol(sc, "gerbv-file-version!"),
1028 			    sc->vptr->mk_foreign_func(sc, gerbv_file_version));
1029 
1030     if ((fd = fopen(filename, "r")) == NULL) {
1031 	setlocale(LC_NUMERIC, "");	/* Default locale */
1032 	scheme_deinit(sc);
1033 	GERB_MESSAGE(_("Couldn't open project file %s (%s)"), filename,
1034 		     strerror(errno));
1035 
1036 	return NULL;
1037     }
1038 
1039     project_list_top = NULL;
1040 
1041     scheme_load_file(sc, fd);
1042     fclose(fd);
1043 
1044     setlocale(LC_NUMERIC, "");	/* Default locale */
1045     scheme_deinit(sc);
1046 
1047     return project_list_top;
1048 } /* read_project */
1049 
1050 
1051 void
project_destroy_project_list(project_list_t * projectList)1052 project_destroy_project_list (project_list_t *projectList){
1053 	project_list_t *tempP,*tempP2;
1054 
1055 	for (tempP = projectList; tempP != NULL; ){
1056 		tempP2 = tempP->next;
1057 
1058 		g_free (tempP->filename);
1059 		gerbv_attribute_destroy_HID_attribute (tempP->attr_list, tempP->n_attr);
1060 		tempP->attr_list = NULL;
1061 		tempP = tempP2;
1062 	}
1063 }
1064 
1065 /*
1066  * Writes a description of a project to a file
1067  * that can be parsed by read_project above
1068  */
1069 int
write_project_file(gerbv_project_t * gerbvProject,char const * filename,project_list_t * project)1070 write_project_file(gerbv_project_t *gerbvProject, char const* filename, project_list_t *project)
1071 {
1072     FILE *fd;
1073     project_list_t *p = project;
1074     int n_attr = 0;
1075     gerbv_HID_Attribute *attr_list = NULL;
1076     const float min_val = GERBV_PRECISION_LINEAR_INCH;
1077     int i;
1078 
1079     if ((fd = fopen(filename, "w")) == NULL) {
1080 	    GERB_MESSAGE(_("Couldn't save project %s"), filename);
1081 	    return -1;
1082     }
1083 
1084     /* Force gerbv to input decimals as dots */
1085     setlocale(LC_NUMERIC, "C");
1086 
1087     fprintf(fd, "(gerbv-file-version! \"%s\")\n", GERBV_PROJECT_FILE_VERSION);
1088 
1089     while (p) {
1090 	fprintf(fd, "(define-layer! %d ", p->layerno);
1091 
1092 	fprintf(fd, "(cons 'filename \"%s\")\n",
1093 		convert_path_separators(p->filename, MINGW_UNIX));
1094 
1095 	if (p->inverted)
1096 	    fprintf(fd, "\t(cons 'inverted #t)\n");
1097 
1098 	if (p->layerno >= 0) {
1099 	    fprintf(fd, "\t(cons 'visible #%c)\n", p->visible? 't': 'f');
1100 	}
1101 
1102 	fprintf(fd, "\t(cons 'color #(%d %d %d))\n",
1103 		p->rgb[0], p->rgb[1], p->rgb[2]);
1104 
1105 	if (p->layerno >= 0) {
1106 	    if (p->alpha != alpha_def_value)
1107 		fprintf(fd, "\t(cons 'alpha #(%d))\n", p->alpha);
1108 
1109 	    /* Check if there is transformation. Write if so. */
1110 	    if ((fabs(p->translate_x) > min_val)
1111 		    || (fabs(p->translate_y) > min_val)) {
1112 		fprintf(fd, "\t(cons 'translate #(%f %f))\n",
1113 			p->translate_x, p->translate_y);
1114 	    }
1115 	    if (fabs(p->rotation) > GERBV_PRECISION_ANGLE_RAD) {
1116 		fprintf(fd, "\t(cons 'rotation #(%f))\n", p->rotation);
1117 	    }
1118 	    if ((fabs(p->scale_x - 1.0) > min_val)
1119 		    || (fabs(p->scale_y - 1.0) > min_val)) {
1120 		fprintf(fd, "\t(cons 'scale #(%f %f))\n",
1121 				p->scale_x, p->scale_y);
1122 	    }
1123 	    if (p->mirror_x || p->mirror_y) {
1124 		fprintf(fd, "\t(cons 'mirror #(#%c #%c))\n",
1125 			p->mirror_x? 't': 'f', p->mirror_y? 't': 'f');
1126 	    }
1127 	}
1128 	/* now write out the attribute list which specifies the
1129 	 * file format
1130 	 */
1131 	if (p->layerno < 0) {
1132 	    attr_list = NULL;
1133 	    n_attr = 0;
1134 	} else {
1135 	    attr_list = gerbvProject->file[p->layerno]->image->info->attr_list;
1136 	    n_attr =  gerbvProject->file[p->layerno]->image->info->n_attr;
1137 	}
1138 
1139 	if (n_attr > 0) {
1140 	    fprintf(fd, "\t(cons 'attribs (list\n");
1141 	}
1142 	for (i = 0; i < n_attr ; i++) {
1143 	    switch (attr_list[i].type) {
1144 	    case HID_Label:
1145 		  fprintf(fd, "\t\t(list '%s 'Label \"%s\")\n", attr_list[i].name,
1146 			  attr_list[i].default_val.str_value);
1147 		  break;
1148 
1149 	      case HID_Integer:
1150 		  fprintf(fd, "\t\t(list '%s 'Integer %d)\n", attr_list[i].name,
1151 			  attr_list[i].default_val.int_value);
1152 		  break;
1153 
1154 	      case HID_Real:
1155 		  fprintf(fd, "\t\t(list '%s 'Real %g)\n", attr_list[i].name,
1156 			  attr_list[i].default_val.real_value);
1157 		  break;
1158 
1159 	      case HID_String:
1160 		  fprintf(fd, "\t\t(list '%s 'String \"%s\")\n", attr_list[i].name,
1161 			  attr_list[i].default_val.str_value);
1162 		  break;
1163 
1164 	      case HID_Boolean:
1165 		  fprintf(fd, "\t\t(list '%s 'Boolean %d)\n", attr_list[i].name,
1166 			  attr_list[i].default_val.int_value);
1167 		  break;
1168 
1169 	      case HID_Enum:
1170 		  fprintf(fd, "\t\t(list '%s 'Enum %d)\n", attr_list[i].name,
1171 			  attr_list[i].default_val.int_value);
1172 		  break;
1173 
1174 	      case HID_Mixed:
1175 		  dprintf ("HID_Mixed\n");
1176 		  fprintf (stderr, _("%s():  WARNING:  HID_Mixed is not yet supported.\n"),
1177 			   __FUNCTION__);
1178 		  break;
1179 
1180 	      case HID_Path:
1181 		  fprintf(fd, "\t\t(list '%s 'Path \"%s\")\n", attr_list[i].name,
1182 			  attr_list[i].default_val.str_value);
1183 		  break;
1184 
1185 	      default:
1186 		  fprintf (stderr, _("%s: unknown type of HID attribute (%d)\n"),
1187 			   __FUNCTION__, attr_list[i].type);
1188 		  break;
1189 	      }
1190 	}
1191 	if (n_attr > 0) {
1192 	    fprintf (fd, "\t))\n");
1193 	}
1194 
1195 	fprintf(fd, ")\n");
1196 	p = p->next;
1197     }
1198 
1199     fprintf (fd, "(set-render-type! %d)\n", screenRenderInfo.renderType);
1200 
1201     setlocale(LC_NUMERIC, "");	/* Default locale */
1202 
1203     fclose(fd);
1204 
1205     return 0;
1206 } /* write_project */
1207