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