Added the use of PCBDATA environment variable to make gsch2pcb relocatable
[geda-gaf/peter-b.git] / utils / src / gsch2pcb.c
blob32334859abe14cf8eac8b457248abf52ed0905e3
1 /* $Id$ */
3 /* gsch2pcb
5 | Bill Wilson billw@wt.net
7 | This program is free software which I release under the GNU General Public
8 | License. You may redistribute and/or modify this program under the terms
9 | of that license as published by the Free Software Foundation; either
10 | version 2 of the License, or (at your option) any later version.
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details. Version 2 is in the
16 | COPYRIGHT file in the top level directory of this distribution.
18 | To get a copy of the GNU General Puplic License, write to the Free Software
19 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
27 #include <glib.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <unistd.h>
34 #include <sys/stat.h>
36 #if !GLIB_CHECK_VERSION(2,0,0)
37 #include "glib12-compat.c"
38 #endif
40 #ifdef HAVE_LIBDMALLOC
41 #include <dmalloc.h>
42 #endif
44 #define GSC2PCB_VERSION "1.6"
46 #define DEFAULT_PCB_INC "pcb.inc"
48 #define SEP_STRING "--------\n"
50 typedef struct
52 gchar *refdes,
53 *value,
54 *description,
55 *changed_description,
56 *changed_value;
57 gchar *flags,
58 *tail;
59 gint x,
61 gchar *pkg_name_fix;
62 gchar res_char;
64 gboolean still_exists,
65 new_format,
66 hi_res_format,
67 quoted_flags,
68 omit_PKG;
70 PcbElement;
73 typedef struct
75 gchar *part_number,
76 *element_name;
78 ElementMap;
80 static GList *pcb_element_list,
81 *element_directory_list,
82 *extra_gnetlist_list;
84 static gchar *schematics,
85 *basename;
87 static gchar *m4_command,
88 *m4_pcbdir,
89 *default_m4_pcbdir,
90 *m4_files,
91 *m4_override_file;
93 static gboolean use_m4 = TRUE;
95 static gchar *empty_footprint_name;
97 static gint verbose,
98 n_deleted,
99 n_added_m4,
100 n_added_ef,
101 n_fixed,
102 n_PKG_removed_new,
103 n_PKG_removed_old,
104 n_preserved,
105 n_changed_value,
106 n_not_found,
107 n_unknown,
108 n_none,
109 n_empty;
111 static gboolean remove_unfound_elements = TRUE,
112 quiet_mode = FALSE,
113 force_element_files,
114 preserve,
115 fix_elements,
116 bak_done,
117 need_PKG_purge;
120 static void
121 create_m4_override_file()
123 FILE *f;
125 m4_override_file = "gnet-gsch2pcb-tmp.scm";
126 f = fopen(m4_override_file, "wb");
127 if (!f)
129 m4_override_file = NULL;
130 return;
132 if (m4_command)
133 fprintf(f, "(define m4-command \"%s\")\n", m4_command);
134 if (m4_pcbdir)
135 fprintf(f, "(define m4-pcbdir \"%s\")\n", m4_pcbdir);
136 if (m4_files)
137 fprintf(f, "(define m4-files \"%s\")\n", m4_files);
138 fprintf(f, "(define gsch2pcb:use-m4 %s)\n", use_m4 == TRUE ? "#t" : "#f");
140 fclose(f);
141 if (verbose)
143 printf("Default m4-pcbdir: %s\n", default_m4_pcbdir);
144 printf("--------\ngnet-gsch2pcb-tmp.scm override file:\n");
145 if (m4_command)
146 printf(" (define m4-command \"%s\")\n", m4_command);
147 if (m4_pcbdir)
148 printf(" (define m4-pcbdir \"%s\")\n", m4_pcbdir);
149 if (m4_files)
150 printf(" (define m4-files \"%s\")\n", m4_files);
151 printf(" (define gsch2pcb:use-m4 %s)\n", use_m4 == TRUE ? "#t" : "#f");
155 /* Run gnetlist to generate a netlist and a PCB board file. gnetlist
156 | has exit status of 0 even if it's given an invalid arg, so do some
157 | stat() hoops to decide if gnetlist successfully generated the PCB
158 | board file (only gnetlist >= 20030901 recognizes -m).
160 static void
161 run_gnetlist(gchar *pins_file, gchar *net_file, gchar *pcb_file, gchar *basename, gchar *args)
163 gchar *command,
164 *out_file,
165 *args1,
167 GList *list;
168 struct stat st;
169 time_t mtime;
170 static const gchar *gnetlist = NULL;
173 * Allow the user to specify a full path or a different name for
174 * the gnetlist command. Especially useful if multiple copies
175 * are installed at once.
177 if (gnetlist == NULL)
178 gnetlist = g_getenv ("GNETLIST");
179 if (gnetlist == NULL)
180 gnetlist = "gnetlist";
182 if (verbose)
184 command = g_strconcat(
185 gnetlist, " -g pcbpins -o ", pins_file, " ", args, NULL);
186 printf("Running command:\n\t%s\n", command);
187 printf(SEP_STRING);
189 else
190 command = g_strconcat(
191 gnetlist, " -q -g pcbpins -o ", pins_file, " ", args, NULL);
192 g_spawn_command_line_sync(command, NULL, NULL, NULL, NULL);
193 g_free(command);
195 if (verbose)
197 command = g_strconcat(
198 gnetlist, " -g PCB -o ", net_file, " ", args, NULL);
199 printf("Running command:\n\t%s\n", command);
200 printf(SEP_STRING);
202 else
203 command = g_strconcat(
204 gnetlist, " -q -g PCB -o ", net_file, " ", args, NULL);
205 g_spawn_command_line_sync(command, NULL, NULL, NULL, NULL);
206 g_free(command);
208 create_m4_override_file();
209 if (m4_override_file)
210 args1 = g_strconcat("-m ", m4_override_file, " ", args, NULL);
211 else
212 args1 = g_strdup(args);
214 mtime = (stat(pcb_file, &st) == 0) ? st.st_mtime : 0;
216 if (verbose)
218 printf(SEP_STRING);
219 command = g_strconcat(gnetlist, " -g gsch2pcb -o ", pcb_file,
220 " ", args1, NULL);
221 printf("Running command:\n\t%s\n", command);
222 printf(SEP_STRING);
224 else
225 command = g_strconcat(gnetlist, " -q -g gsch2pcb -o ", pcb_file,
226 " ", args1, NULL);
228 g_spawn_command_line_sync(command, NULL, NULL, NULL, NULL);
230 if (verbose)
231 printf(SEP_STRING);
233 if ( stat(pcb_file, &st) != 0
234 || mtime == st.st_mtime
237 fprintf(stderr, "gsch2pcb: gnetlist command (%s) failed.\n", command);
238 if (m4_override_file)
239 fprintf(stderr,
240 " At least gnetlist 20030901 is required for m4-xxx options.\n");
241 exit(1);
243 g_free(command);
244 g_free(args1);
245 if (m4_override_file)
246 unlink(m4_override_file);
248 for (list = extra_gnetlist_list; list; list = g_list_next(list))
250 s = (gchar *) list->data;
251 if (!strstr(s, " -o "))
252 out_file = g_strconcat(" -o ", basename, ".", s, NULL);
253 else
254 out_file = g_strdup(" ");
256 if (verbose)
258 printf(SEP_STRING);
259 command = g_strconcat(gnetlist, " -g ", s, out_file,
260 " ", args, NULL);
261 printf("Running command:\n\t%s\n", command);
262 printf(SEP_STRING);
264 else
265 command = g_strconcat(gnetlist, " -q -g ", s, out_file,
266 " ", args, NULL);
268 g_spawn_command_line_sync(command, NULL, NULL, NULL, NULL);
269 g_free(command);
270 g_free(out_file);
271 if (verbose)
272 printf(SEP_STRING);
276 static gchar *
277 token(gchar *string, gchar **next, gboolean *quoted_ret)
279 static gchar *str;
280 gchar *s, *ret;
281 gboolean quoted = FALSE;
283 if (string)
284 str = string;
285 if (!str || !*str)
287 if (next)
288 *next = str;
289 return g_strdup("");
291 while (*str == ' ' || *str == '\t' || *str == ',' || *str == '\n')
292 ++str;
293 if (*str == '"')
295 quoted = TRUE;
296 if (quoted_ret)
297 *quoted_ret = TRUE;
298 ++str;
299 for (s = str; *s && *s != '"' && *s != '\n'; ++s)
302 else
304 if (quoted_ret)
305 *quoted_ret = FALSE;
306 for (s = str;
307 *s && (*s != ' ' && *s != '\t' && *s != ',' && *s != '\n');
308 ++s)
311 ret = g_strndup(str, s - str);
312 str = (quoted && *s) ? s + 1 : s;
313 if (next)
314 *next = str;
315 return ret;
318 static gchar *
319 fix_spaces(gchar *str)
321 gchar *s;
323 if (!str)
324 return NULL;
325 for (s = str; *s; ++s)
326 if (*s == ' ' || *s == '\t')
327 *s = '_';
328 return str;
331 /* As of 1/9/2004 CVS hi_res Element[] line format:
332 | Element[element_flags, description, pcb-name, value, mark_x, mark_y,
333 | text_x, text_y, text_direction, text_scale, text_flags]
334 | New PCB 1.7 / 1.99 Element() line format:
335 | Element(element_flags, description, pcb-name, value, mark_x, mark_y,
336 | text_x, text_y, text_direction, text_scale, text_flags)
337 | Old PCB 1.6 Element() line format:
338 | Element(element_flags, description, pcb-name, value,
339 | text_x, text_y, text_direction, text_scale, text_flags)
341 | (mark_x, mark_y) is the element position (mark) and (text_x,text_y)
342 | is the description text position which is absolute in pre 1.7 and
343 | is now relative. The hi_res mark_x,mark_y and text_x,text_y resolutions
344 | are 100x the other formats.
346 PcbElement *
347 pcb_element_line_parse(gchar *line)
349 PcbElement *el = NULL;
350 gchar *s, *t, close_char;
351 gint state = 0, elcount = 0;
353 if (strncmp(line, "Element", 7))
354 return NULL;
356 el = g_new0(PcbElement, 1);
358 s = line + 7;
359 while (*s == ' ' || *s == '\t')
360 ++s;
362 if (*s == '[')
363 el->hi_res_format = TRUE;
364 else if (*s != '(')
366 g_free(el);
367 return NULL;
370 el->res_char = el->hi_res_format ? '[' : '(';
371 close_char = el->hi_res_format ? ']' : ')';
373 el->flags = token(s + 1, NULL, &el->quoted_flags);
374 el->description = token(NULL, NULL, NULL);
375 el->refdes = token(NULL, NULL, NULL);
376 el->value = token(NULL, NULL, NULL);
378 s = token(NULL, NULL, NULL);
379 el->x = atoi(s);
380 g_free(s);
382 s = token(NULL, &t, NULL);
383 el->y = atoi(s);
384 g_free(s);
386 el->tail = g_strdup(t ? t : "");
387 if ((s = strrchr(el->tail, (gint) '\n')) != NULL)
388 *s = '\0';
390 /* Count the tokens in tail to decide if it's new or old format.
391 | Old format will have 3 tokens, new format will have 5 tokens.
393 for (s = el->tail; *s && *s != close_char; ++s)
395 if (*s != ' ')
397 if (state == 0)
398 ++elcount;
399 state = 1;
401 else
402 state = 0;
404 if (elcount > 4)
405 el->new_format = TRUE;
407 fix_spaces(el->description);
408 fix_spaces(el->refdes);
409 fix_spaces(el->value);
411 /* Don't allow elements with no refdes to ever be deleted
412 | because they may be desired pc board elements not in schematics.
413 | So initialize still_exists to TRUE if empty or non-alphanumeric refdes.
415 if (!*el->refdes || !isalnum((gint) (*el->refdes)))
416 el->still_exists = TRUE;
418 return el;
421 static void
422 pcb_element_free(PcbElement *el)
424 if (!el)
425 return;
426 g_free(el->flags);
427 g_free(el->description);
428 g_free(el->changed_description);
429 g_free(el->changed_value);
430 g_free(el->refdes);
431 g_free(el->value);
432 g_free(el->tail);
433 g_free(el->pkg_name_fix);
434 g_free(el);
437 static void
438 get_pcb_element_list(gchar *pcb_file)
440 FILE *f;
441 PcbElement *el;
442 gchar *s, buf[1024];
444 if ((f = fopen(pcb_file, "r")) == NULL)
445 return;
446 while ((fgets(buf, sizeof(buf), f)) != NULL)
448 for (s = buf; *s == ' ' || *s == '\t'; ++s)
450 if (!strncmp(s, "PKG_", 4))
452 need_PKG_purge = TRUE;
453 continue;
455 if ((el = pcb_element_line_parse(s)) == NULL)
456 continue;
457 pcb_element_list = g_list_append(pcb_element_list, el);
459 fclose(f);
462 static PcbElement *
463 pcb_element_exists(PcbElement *el_test, gboolean record)
465 GList *list;
466 PcbElement *el;
468 for (list = pcb_element_list; list; list = g_list_next(list))
470 el = (PcbElement *) list->data;
472 if (strcmp(el_test->refdes, el->refdes))
473 continue;
474 if (strcmp(el_test->description, el->description)) /* footprint */
476 if (record)
477 el->changed_description = g_strdup(el_test->description);
479 else
481 if (record)
483 if (strcmp(el_test->value, el->value))
484 el->changed_value = g_strdup(el_test->value);
485 el->still_exists = TRUE;
487 return el;
490 return NULL;
493 /* A problem is that new PCB 1.7 file elements have the (mark_x,mark_y)
494 | value set to wherever the element was created and no equivalent of a
495 | gschem translate symbol was done.
496 | So, file elements inserted can be scattered over a big area and this is
497 | bad when loading a file.new.pcb into an existing PC board. So, do a
498 | simple translate if (mark_x,mark_y) is (arbitrarily) over 1000. I'll
499 | assume that for values < 1000 the element creator was concerned with a
500 | sane initial element placement. Unless someone has a better idea?
501 | Don't bother with pre PCB 1.7 formats as that would require parsing
502 | the mark(). Current m4 elements use the old format but they seem to
503 | have a reasonable initial mark().
505 static void
506 simple_translate(PcbElement *el)
508 gint factor;
510 if (el->new_format)
512 factor = el->hi_res_format ? 100 : 1;
513 if (el->x > 1000 * factor)
514 el->x = 500 * factor;
515 if (el->y > 1000 * factor)
516 el->y = 500 * factor;
520 static gboolean
521 insert_element(FILE *f_out, gchar *element_file,
522 gchar *footprint, gchar *refdes, gchar *value)
524 FILE *f_in;
525 PcbElement *el;
526 gchar *fmt, *s, buf[1024];
527 gboolean retval = FALSE;
529 if ((f_in = fopen(element_file, "r")) == NULL)
531 s = g_strdup_printf("insert_element() can't open %s", element_file);
532 perror(s);
533 g_free(s);
534 return FALSE;
536 /* Copy the file element lines. Substitute new parameters into the
537 | Element() or Element[] line and strip comments.
539 while ((fgets(buf, sizeof(buf), f_in)) != NULL)
541 for (s = buf; *s == ' ' || *s == '\t'; ++s)
543 if ((el = pcb_element_line_parse(s)) != NULL)
545 simple_translate(el);
546 fmt = el->quoted_flags ?
547 "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %d %d%s\n" :
548 "Element%c%s \"%s\" \"%s\" \"%s\" %d %d%s\n";
550 fprintf(f_out, fmt,
551 el->res_char, el->flags, footprint, refdes, value,
552 el->x, el->y, el->tail);
553 retval = TRUE;;
555 else if (*s != '#')
556 fputs(buf, f_out);
557 pcb_element_free(el);
559 fclose(f_in);
560 return retval;
564 gchar *
565 find_element(gchar *dir_path, gchar *element)
567 GDir *dir;
568 gchar *path, *name, *s, *found = NULL;
570 if ((dir = g_dir_open(dir_path, 0, NULL)) == NULL)
572 s = g_strdup_printf("find_element can't open dir \"%s\"", dir_path);
573 perror(s);
574 g_free(s);
575 return NULL;
577 if (verbose > 1)
578 printf("\t Searching: \"%s\" for \"%s\"\n", dir_path, element);
579 while ((name = (gchar *) g_dir_read_name(dir)) != NULL)
581 path = g_strconcat(dir_path, "/", name, NULL);
582 found = NULL;
584 /* if we got a directory name, then recurse down into it */
585 if (g_file_test(path, G_FILE_TEST_IS_DIR))
586 found = find_element(path, element);
588 /* otherwise assume it is a file and see if it is the one we want */
589 else
591 if (verbose > 1)
592 printf("\t : %s\t", name);
593 if (!strcmp(name, element))
594 found = g_strdup(path);
595 else
597 gchar *tmps;
598 tmps = g_strconcat (element, ".fp", NULL);
599 if (!strcmp(name, tmps))
600 found = g_strdup(path);
601 g_free (tmps);
603 if (verbose > 1)
604 printf("%s\n", found ? "Yes" : "No");
606 g_free(path);
607 if (found)
608 break;
610 g_dir_close(dir);
611 return found;
614 gchar *
615 search_element_directories( PcbElement *el)
617 GList *list;
618 gchar *s, *elname = NULL, *dir_path, *path = NULL;
619 gint n1, n2;
621 /* See comment before pkg_to_element() */
622 if (el->pkg_name_fix)
624 if (strchr(el->description, '-'))
626 n1 = strlen(el->description);
627 n2 = strlen(el->pkg_name_fix);
628 s = el->description + n1 - n2 - 1;
630 // printf("n1=%d n2=%d desc:%s fix:%s s:%s\n",
631 // n1, n2, el->description, el->pkg_name_fix, s);
633 if ( n1 > 0 && n2 < n1
634 && *s == '-'
635 && *(s + 1) == *el->pkg_name_fix
638 s = g_strndup(el->description, n1 - n2 - 1);
639 elname = g_strconcat(s, " ", el->pkg_name_fix, NULL);
640 g_free(s);
643 if (!elname)
645 printf("Warning: argument passing may have been confused by\n");
646 printf(" a comma in a component value:\n");
647 printf(" Check %s %s %s\n",
648 el->refdes, el->description, el->value);
649 printf(" Maybe just use a space instead of a comma?\n");
652 if (!elname)
653 elname = g_strdup(el->description);
655 if (!strcmp(elname, "unknown"))
657 g_free(elname);
658 return NULL;
660 if (verbose > 1)
661 printf("\tSearching directories looking for file element: %s\n",
662 elname);
663 for (list = element_directory_list; list; list = g_list_next(list))
665 dir_path = (gchar *) list->data;
666 if (verbose > 1)
667 printf("\tLooking in directory: \"%s\"\n", dir_path);
668 path = find_element(dir_path, elname);
669 if (path)
671 if (verbose)
672 printf("\tFound: %s\n", path);
673 break;
676 g_free(elname);
677 return path;
680 /* The gnetlist backend gnet-gsch2pcb.scm generates PKG_ lines:
682 | PKG_footprint(footprint{-fp0-fp1},refdes,value{,fp0,fp1})
684 | where fp1 and fp2 (if they exist) are the extra footprint components when
685 | specifying footprints like "DIL 14 300". This is needed for m4 macros.
686 | A complication is if the footprint references a file element with spaces
687 | embedded in the name. The gnetlist backend will interpret these as
688 | fp0, fp1, ... args and the footprint will in this case incorrectly have
689 | '-' inserted where the spaces should be. So, if there are additional
690 | args, reconstruct the portion of the name given by the args with spaces
691 | for later use. Eg. if the footprint is "100 Pin jack", we will have
692 | PKG_100-Pin-jack(100-Pin-jack,refdes,value,Pin,jack)
693 | So put "Pin jack" into pkg_name_fix so if this element is searched
694 | as a file element we can munge the description to what it should be, eg:
695 | 100-Pin-jack -> 100 Pin jack
697 static PcbElement *
698 pkg_to_element(FILE *f, gchar *pkg_line)
700 PcbElement *el;
701 gchar **args, *s;
702 gint n, n_extra_args, n_dashes;
704 if ( strncmp(pkg_line, "PKG_", 4)
705 || (s = strchr(pkg_line, (gint) '(')) == NULL
707 return NULL;
709 args = g_strsplit(s + 1, ",", 12);
710 if (!args[0] || !args[1] || !args[2])
712 fprintf(stderr, "Bad package line: %s\n", pkg_line);
713 return NULL;
715 fix_spaces(args[0]);
716 fix_spaces(args[1]);
717 fix_spaces(args[2]);
719 el = g_new0(PcbElement, 1);
720 el->description = g_strdup(args[0]);
721 el->refdes = g_strdup(args[1]);
722 el->value = g_strdup(args[2]);
723 if ((s = strchr(el->value, (gint) ')')) != NULL)
724 *s = '\0';
726 /* If the component value has a comma, eg "1k, 1%", the gnetlist generated
727 | PKG line will be PKG_XXX(`R0w8',`R100',`1k, 1%'), but after processed
728 | by m4, the input to gsch2pcb will be PKG_XXX(R0w8,R100,1k, 1%). So the
729 | quoting info has been lost when processing for file elements. So here
730 | try to detect and fix this. But I can't handle the situation where
731 | the description has a '-' and the value has a comma because
732 | gnet-gsch2pcb.scm munges the description with '-' when there are
733 | extra args.
735 for (n_extra_args = 0; args[3 + n_extra_args] != NULL; ++n_extra_args)
737 s = el->description;
738 for (n_dashes = 0; (s = strchr(s + 1, '-')) != NULL; ++n_dashes)
741 n = 3;
742 if (n_extra_args == n_dashes + 1)
743 { /* Assume there was a comma in the value, eg "1K, 1%" */
744 s = el->value;
745 el->value = g_strconcat(s, ",", fix_spaces(args[n]), NULL);
746 g_free(s);
747 if ((s = strchr(el->value, (gint) ')')) != NULL)
748 *s = '\0';
749 n = 4;
751 if (args[n])
753 el->pkg_name_fix = g_strdup(args[n]);
754 for (n += 1; args[n] != NULL; ++n)
756 s = el->pkg_name_fix;
757 el->pkg_name_fix = g_strconcat(s, " ", args[n], NULL);
758 g_free(s);
760 if ((s = strchr(el->pkg_name_fix, (gint) ')')) != NULL)
761 *s = '\0';
763 g_strfreev(args);
765 if (empty_footprint_name && !strcmp(el->description, empty_footprint_name))
767 if (verbose)
768 printf(
769 "%s: has the empty footprint attribute \"%s\" so won't be in the layout.\n",
770 el->refdes, el->description);
771 n_empty += 1;
772 el->omit_PKG = TRUE;
774 else if (!strcmp(el->description, "none"))
776 fprintf(stderr,
777 "WARNING: %s has a footprint attribute \"%s\" so won't be in the layout.\n",
778 el->refdes, el->description);
779 n_none += 1;
780 el->omit_PKG = TRUE;
782 else if (!strcmp(el->description, "unknown"))
784 fprintf(stderr,
785 "WARNING: %s has no footprint attribute so won't be in the layout.\n",
786 el->refdes);
787 n_unknown += 1;
788 el->omit_PKG = TRUE;
790 return el;
793 /* Process the newly created pcb file which is the output from
794 | gnetlist -g gsch2pcb ...
795 | It will have elements found via the m4 interface and PKG_ lines for
796 | elements not found.
797 | Insert pcb file elements for PKG_ lines if file elements can be found.
798 | If there was an existing pcb file, strip out any elements if they are
799 | already present so that the new pcb file will only have new elements.
801 static gint
802 add_elements(gchar *pcb_file)
804 FILE *f_in, *f_out;
805 PcbElement *el = NULL;
806 gchar *command, *p, *tmp_file, *s, buf[1024];
807 gint total, paren_level = 0;
808 gboolean is_m4, skipping = FALSE;
810 if ((f_in = fopen(pcb_file, "r")) == NULL)
811 return 0;
812 tmp_file = g_strconcat(pcb_file, ".tmp", NULL);
813 if ((f_out = fopen(tmp_file, "wb")) == NULL)
815 fclose(f_in);
816 g_free(tmp_file);
817 return 0;
819 while ((fgets(buf, sizeof(buf), f_in)) != NULL)
821 for (s = buf; *s == ' ' || *s == '\t'; ++s)
823 if (skipping)
825 if (*s == '(')
826 ++paren_level;
827 else if (*s == ')' && --paren_level <= 0)
828 skipping = FALSE;
829 continue;
831 is_m4 = FALSE;
832 if ((el = pcb_element_line_parse(s)) != NULL)
833 is_m4 = TRUE;
834 else
835 el = pkg_to_element(f_out, s);
836 if (el && pcb_element_exists(el, TRUE))
838 skipping = is_m4;
839 pcb_element_free(el);
840 continue;
842 if (!el || el->omit_PKG)
844 if (el)
848 else
849 fputs(buf, f_out);
850 continue;
852 if (!is_m4 || (is_m4 && force_element_files))
854 if (verbose && !is_m4)
855 printf(
856 "%s: need new file element for footprint %s (value=%s)\n",
857 el->refdes, el->description, el->value);
858 if (verbose && is_m4 && force_element_files)
859 printf(
860 "%s: have m4 element %s, but trying to replace with a file element.\n",
861 el->refdes, el->description);
862 p = search_element_directories(el);
863 if (!p && verbose && is_m4 && force_element_files)
864 printf("\tNo file element found.\n");
866 if (p && insert_element(f_out, p,
867 el->description, el->refdes, el->value))
869 skipping = is_m4;
870 is_m4 = FALSE;
871 ++n_added_ef;
872 if (verbose)
873 printf(
874 "%s: added new file element for footprint %s (value=%s)\n",
875 el->refdes, el->description, el->value);
877 else if (!is_m4)
879 fprintf(stderr,
880 "%s: can't find PCB element for footprint %s (value=%s)\n",
881 el->refdes, el->description, el->value);
882 if (remove_unfound_elements && !fix_elements)
884 fprintf(stderr,
885 "So device %s will not be in the layout.\n",
886 el->refdes);
887 ++n_PKG_removed_new;
889 else
891 ++n_not_found;
892 fputs(buf, f_out); /* Copy PKG_ line */
895 g_free(p);
897 if (is_m4)
899 fputs(buf, f_out);
900 ++n_added_m4;
901 if (verbose)
902 printf(
903 "%s: added new m4 element for footprint %s (value=%s)\n",
904 el->refdes, el->description, el->value);
906 pcb_element_free(el);
907 if (verbose)
908 printf("----\n");
910 fclose(f_in);
911 fclose(f_out);
913 total = n_added_ef + n_added_m4 + n_not_found;
914 if (total == 0)
915 command = g_strconcat("rm ", tmp_file, NULL);
916 else
917 command = g_strconcat("mv ", tmp_file, " ", pcb_file, NULL);
918 g_spawn_command_line_sync(command, NULL, NULL, NULL, NULL);
919 g_free(command);
920 g_free(tmp_file);
921 return total;
924 static void
925 update_element_descriptions(gchar *pcb_file, gchar *bak)
927 FILE *f_in, *f_out;
928 GList *list;
929 PcbElement *el, *el_exists;
930 gchar *fmt, *command, *tmp, *s, buf[1024];
932 for (list = pcb_element_list; list; list = g_list_next(list))
934 el = (PcbElement *) list->data;
935 if (el->changed_description)
936 ++n_fixed;
938 if (!pcb_element_list || n_fixed == 0)
940 fprintf(stderr, "Could not find any elements to fix.\n");
941 return;
943 if ((f_in = fopen(pcb_file, "r")) == NULL)
944 return;
945 tmp = g_strconcat(pcb_file, ".tmp", NULL);
946 if ((f_out = fopen(tmp, "wb")) == NULL)
948 fclose(f_in);
949 return;
951 while ((fgets(buf, sizeof(buf), f_in)) != NULL)
953 for (s = buf; *s == ' ' || *s == '\t'; ++s)
955 if ( (el = pcb_element_line_parse(s)) != NULL
956 && (el_exists = pcb_element_exists(el, FALSE)) != NULL
957 && el_exists->changed_description
960 fmt = el->quoted_flags ?
961 "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %d %d%s\n" :
962 "Element%c%s \"%s\" \"%s\" \"%s\" %d %d%s\n";
963 fprintf(f_out, fmt,
964 el->res_char,
965 el->flags, el_exists->changed_description,
966 el->refdes, el->value, el->x, el->y, el->tail);
967 printf("%s: updating element Description: %s -> %s\n",
968 el->refdes, el->description,
969 el_exists->changed_description);
970 el_exists->still_exists = TRUE;
972 else
973 fputs(buf, f_out);
974 pcb_element_free(el);
976 fclose(f_in);
977 fclose(f_out);
979 if (!bak_done)
981 command = g_strconcat("mv ", pcb_file, " ", bak, NULL);
982 g_spawn_command_line_sync(command, NULL, NULL, NULL, NULL);
983 g_free(command);
984 bak_done = TRUE;
987 command = g_strconcat("mv ", tmp, " ", pcb_file, NULL);
988 g_spawn_command_line_sync(command, NULL, NULL, NULL, NULL);
989 g_free(command);
990 g_free(tmp);
993 static void
994 prune_elements(gchar *pcb_file, gchar *bak)
996 FILE *f_in, *f_out;
997 GList *list;
998 PcbElement *el, *el_exists;
999 gchar *fmt, *command, *tmp, *s, buf[1024];
1000 gint paren_level = 0;
1001 gboolean skipping = FALSE;
1003 for (list = pcb_element_list; list; list = g_list_next(list))
1005 el = (PcbElement *) list->data;
1006 if (!el->still_exists)
1008 if (preserve)
1010 ++n_preserved;
1011 fprintf(stderr,
1012 "Preserving PCB element not in the schematic: %s (element %s)\n",
1013 el->refdes, el->description);
1015 else
1016 ++n_deleted;
1018 else if (el->changed_value)
1019 ++n_changed_value;
1021 if ( !pcb_element_list
1022 || (n_deleted == 0 && !need_PKG_purge && n_changed_value == 0)
1024 return;
1025 if ((f_in = fopen(pcb_file, "r")) == NULL)
1026 return;
1027 tmp = g_strconcat(pcb_file, ".tmp", NULL);
1028 if ((f_out = fopen(tmp, "wb")) == NULL)
1030 fclose(f_in);
1031 return;
1033 while ((fgets(buf, sizeof(buf), f_in)) != NULL)
1035 for (s = buf; *s == ' ' || *s == '\t'; ++s)
1037 if (skipping)
1039 if (*s == '(')
1040 ++paren_level;
1041 else if (*s == ')' && --paren_level <= 0)
1042 skipping = FALSE;
1043 continue;
1045 el_exists = NULL;
1046 if ( (el = pcb_element_line_parse(s)) != NULL
1047 && (el_exists = pcb_element_exists(el, FALSE)) != NULL
1048 && !el_exists->still_exists
1049 && !preserve
1052 skipping = TRUE;
1053 if (verbose)
1054 printf(
1055 "%s: deleted element %s (value=%s)\n",
1056 el->refdes, el->description, el->value);
1057 pcb_element_free(el);
1058 continue;
1060 if (el_exists && el_exists->changed_value)
1062 fmt = el->quoted_flags ?
1063 "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %d %d%s\n" :
1064 "Element%c%s \"%s\" \"%s\" \"%s\" %d %d%s\n";
1065 fprintf(f_out, fmt,
1066 el->res_char, el->flags, el->description, el->refdes,
1067 el_exists->changed_value, el->x, el->y, el->tail);
1068 if (verbose)
1069 printf(
1070 "%s: changed element %s value: %s -> %s\n",
1071 el->refdes, el->description,
1072 el->value, el_exists->changed_value);
1074 else if (!strncmp(s, "PKG_", 4))
1075 ++n_PKG_removed_old;
1076 else
1077 fputs(buf, f_out);
1078 pcb_element_free(el);
1080 fclose(f_in);
1081 fclose(f_out);
1083 if (!bak_done)
1085 command = g_strconcat("mv ", pcb_file, " ", bak, NULL);
1086 g_spawn_command_line_sync(command, NULL, NULL, NULL, NULL);
1087 g_free(command);
1088 bak_done = TRUE;
1091 command = g_strconcat("mv ", tmp, " ", pcb_file, NULL);
1092 g_spawn_command_line_sync(command, NULL, NULL, NULL, NULL);
1093 g_free(command);
1094 g_free(tmp);
1097 static void
1098 add_m4_file(gchar *arg)
1100 gchar *s;
1102 if (!m4_files)
1103 m4_files = g_strdup(arg);
1104 else
1106 s = m4_files;
1107 m4_files = g_strconcat(m4_files, " ", arg, NULL);
1108 g_free(s);
1112 static gchar *
1113 expand_dir(gchar *dir)
1115 gchar *s;
1117 if (*dir == '~')
1118 s = g_build_filename((gchar *) g_get_home_dir(), dir + 1, NULL);
1119 else
1120 s = g_strdup(dir);
1121 return s;
1124 static void
1125 add_default_m4_files(void)
1127 gchar *path;
1129 path = g_build_filename((gchar *) g_get_home_dir(),
1130 ".pcb", DEFAULT_PCB_INC, NULL);
1131 if (g_file_test(path, G_FILE_TEST_IS_REGULAR))
1132 add_m4_file(path);
1133 g_free(path);
1135 if (g_file_test(DEFAULT_PCB_INC, G_FILE_TEST_IS_REGULAR))
1136 add_m4_file(DEFAULT_PCB_INC);
1140 static void
1141 add_schematic(gchar *sch)
1143 gchar *s;
1145 s = schematics;
1146 if (s)
1147 schematics = g_strconcat(s, " ", sch, NULL);
1148 else
1149 schematics = g_strdup(sch);
1150 g_free(s);
1151 if (!basename && (s = strstr(sch, ".sch")) != NULL)
1152 basename = g_strndup(sch, s - sch);
1155 static gint
1156 parse_config(gchar *config, gchar *arg)
1158 gchar *s;
1160 /* remove trailing white space otherwise strange things can happen */
1161 if ( (arg != NULL) && (strlen(arg) >= 1) )
1163 s = arg + strlen(arg) - 1;
1164 while( (*s == ' ' || *s == '\t' ) && (s != arg) )
1165 s--;
1166 s++;
1167 *s = '\0';
1169 if (verbose)
1170 printf(" %s \"%s\"\n", config, arg ? arg : "");
1172 if (!strcmp(config, "remove-unfound") || !strcmp(config, "r"))
1174 /* This is default behavior set in header section */
1175 remove_unfound_elements = TRUE;
1176 return 0;
1178 if (!strcmp(config, "keep-unfound") || !strcmp(config, "k"))
1180 remove_unfound_elements = FALSE;
1181 return 0;
1183 if (!strcmp(config, "quiet") || !strcmp(config, "q"))
1185 quiet_mode = TRUE;
1186 return 0;
1188 if (!strcmp(config, "preserve") || !strcmp(config, "p"))
1190 preserve = TRUE;
1191 return 0;
1193 if (!strcmp(config, "use-files") || !strcmp(config, "f"))
1195 force_element_files = TRUE;
1196 return 0;
1198 if (!strcmp(config, "skip-m4") || !strcmp(config, "s"))
1200 use_m4 = FALSE;
1201 return 0;
1203 if (!strcmp(config, "elements-dir") || !strcmp(config, "d"))
1205 if (verbose > 1)
1206 printf("\tAdding directory to file element directory list: %s\n",
1207 expand_dir(arg));
1208 element_directory_list =
1209 g_list_prepend(element_directory_list, expand_dir(arg));
1211 else if (!strcmp(config, "output-name") || !strcmp(config, "o"))
1212 basename = g_strdup(arg);
1213 else if (!strcmp(config, "schematics"))
1214 add_schematic(arg);
1215 else if (!strcmp(config, "m4-command"))
1216 m4_command = g_strdup(arg);
1217 else if (!strcmp(config, "m4-pcbdir"))
1218 m4_pcbdir = g_strdup(arg);
1219 else if (!strcmp(config, "m4-file"))
1220 add_m4_file(arg);
1221 else if (!strcmp(config, "gnetlist"))
1222 extra_gnetlist_list =
1223 g_list_append(extra_gnetlist_list, g_strdup(arg));
1224 else if (!strcmp(config, "empty-footprint"))
1225 empty_footprint_name = g_strdup(arg);
1226 else
1227 return -1;
1229 return 1;
1232 static void
1233 load_project(gchar *path)
1235 FILE *f;
1236 gchar *s, buf[1024], config[32], arg[768];
1238 f = fopen(path, "r");
1239 if (!f)
1240 return;
1241 if (verbose)
1242 printf("Reading project file: %s\n", path);
1243 while (fgets(buf, sizeof(buf), f))
1245 for (s = buf; *s == ' ' || *s == '\t' || *s == '\n'; ++s)
1247 if (!*s || *s == '#' || *s == '/' || *s == ';')
1248 continue;
1249 arg[0] = '\0';
1250 sscanf(s, "%31s %767[^\n]", config, arg);
1251 parse_config(config, arg);
1253 fclose(f);
1256 static void
1257 load_extra_project_files(void)
1259 gchar *path;
1260 static gboolean done = FALSE;
1262 if (done)
1263 return;
1265 load_project("/etc/gsch2pcb");
1266 load_project("/usr/local/etc/gsch2pcb");
1268 path = g_build_filename((gchar *) g_get_home_dir(), ".gsch2pcb", NULL);
1269 load_project(path);
1270 g_free(path);
1272 done = TRUE;
1275 static gchar *usage_string0 =
1276 "usage: gsch2pcb [options] {project | foo.sch [foo1.sch ...]}\n"
1277 "\n"
1278 "Generate a PCB layout file from a set of gschem schematics.\n"
1279 " gnetlist -g PCB is run to generate foo.net from the schematics.\n"
1280 "\n"
1281 " gnetlist -g gsch2pcb is run to get PCB m4 derived elements which\n"
1282 " match schematic footprints. For schematic footprints which don't match\n"
1283 " any PCB m4 layout elements, search a set of file element directories in\n"
1284 " an attempt to find matching PCB file elements.\n"
1285 " Output to foo.pcb if it doesn't exist. If there is a current foo.pcb,\n"
1286 " output only new elements to foo.new.pcb.\n"
1287 " If any elements with a non-empty element name in the current foo.pcb\n"
1288 " have no matching schematic component, then remove those elements from\n"
1289 " foo.pcb and rename foo.pcb to a foo.pcb.bak sequence.\n"
1290 "\n"
1291 " gnetlist -g pcbpins is run to get a PCB actions file which will rename all\n"
1292 " of the pins in a .pcb file to match pin names from the schematic.\n"
1293 "\n"
1294 " \"project\" is a file (not ending in .sch) containing a list of\n"
1295 " schematics to process and some options. A schematics line is like:\n"
1296 " schematics foo1.sch foo2.sch ...\n"
1297 " Options in a project file are like command line args without the \"-\":\n"
1298 " output-name myproject\n"
1299 "\n"
1300 "options (may be included in a project file):\n"
1301 " -d, --elements-dir D Search D for PCB file elements. These defaults\n"
1302 " are searched if they exist: ./packages,\n"
1303 " /usr/local/share/pcb/newlib, /usr/share/pcb/newlib,\n"
1304 " (old pcb) /usr/local/lib/pcb_lib, /usr/lib/pcb_lib,\n"
1305 " (old pcb) /usr/local/pcb_lib\n"
1306 " -o, --output-name N Use output file names N.net, N.pcb, and N.new.pcb\n"
1307 " instead of foo.net, ... where foo is the basename\n"
1308 " of the first command line .sch file.\n"
1309 " -f, --use-files Force using file elements over m4 PCB elements\n"
1310 " for new footprints even though m4 elements are\n"
1311 " searched for first and may have been found.\n"
1312 " -r, --remove-unfound Don't include references to unfound elements in\n"
1313 " the generated .pcb files. Use if you want PCB to\n"
1314 " be able to load the (incomplete) .pcb file.\n"
1315 " This is the default behavior.\n"
1316 " -k, --keep-unfound Keep include references to unfound elements in\n"
1317 " the generated .pcb files. Use if you want to hand\n"
1318 " edit or otherwise preprocess the generated .pcb file\n"
1319 " before running pcb.\n"
1320 " -p, --preserve Preserve elements in PCB files which are not found\n"
1321 " in the schematics. Note that elements with an empty\n"
1322 " element name (schematic refdes) are never deleted,\n"
1323 " so you really shouldn't need this option.\n"
1324 " -q, --quiet Don't tell the user what to do next after running gsch2pcb.\n"
1325 "\n"
1326 " -s, --skip-m4 Skip m4 when looking for footprints. The default is to use\n"
1327 " m4 (which is what previous versions did).\n"
1328 " --m4-file F.inc Use m4 file F.inc in addition to the default m4\n"
1329 " files ./pcb.inc and ~/.pcb/pcb.inc.\n"
1330 " --m4-pcbdir D Use D as the PCB m4 files install directory\n"
1331 " instead of the default:\n";
1333 static gchar *usage_string1 =
1334 " --gnetlist backend A convenience run of extra gnetlist -g commands.\n"
1335 " Example: gnetlist partslist3\n"
1336 " Creates: myproject.partslist3\n"
1337 " --empty-footprint name See the project.sample file.\n"
1338 "\n"
1339 "options (not recognized in a project file):\n"
1340 " --fix-elements If a schematic component footprint is not equal\n"
1341 " to its PCB element Description, update the\n"
1342 " Description instead of replacing the element.\n"
1343 " Do this the first time gsch2pcb is used with\n"
1344 " PCB files originally created with gschem2pcb.\n"
1345 " -v, --verbose Use -v -v for additional file element debugging.\n"
1346 " -V, --version\n\n"
1347 "environment variables:\n"
1348 " GNETLIST If set, this specifies the name of the gnetlist program\n"
1349 " to execute.\n"
1350 "\n"
1351 "Additional Resources:\n"
1352 "\n"
1353 " gEDA homepage: http://www.geda.seul.org\n"
1354 " PCB homepage: http://pcb.sf.net\n"
1355 " gEDA Wiki: http://geda.seul.org/dokuwiki/doku.php?id=geda\n"
1356 "\n"
1359 static void
1360 usage()
1362 printf(usage_string0);
1363 printf(" %s\n", default_m4_pcbdir);
1364 printf(usage_string1);
1365 exit(0);
1368 static void
1369 get_args(gint argc, gchar **argv)
1371 gchar *opt, *arg, *s;
1372 gint i, r;
1374 for (i = 1; i < argc; ++i)
1376 opt = argv[i];
1377 arg = argv[i + 1];
1378 if (*opt == '-')
1380 ++opt;
1381 if (*opt == '-')
1382 ++opt;
1383 if (!strcmp(opt, "version") || !strcmp(opt, "V"))
1385 printf("gsch2pcb %s\n", GSC2PCB_VERSION);
1386 exit(0);
1388 else if (!strcmp(opt, "verbose") || !strcmp(opt, "v"))
1390 verbose += 1;
1391 continue;
1393 else if (!strcmp(opt, "fix-elements"))
1395 fix_elements = TRUE;
1396 continue;
1398 else if (!strcmp(opt, "help") || !strcmp(opt, "h"))
1399 usage();
1400 else if ( i < argc
1401 && ((r = parse_config(opt, (i < argc - 1) ? arg : NULL))
1402 >= 0)
1405 i += r;
1406 continue;
1408 printf("gsch2pcb: bad or incomplete arg: %s\n", argv[i]);
1409 usage();
1411 else
1413 if ((s = strstr(argv[i], ".sch")) == NULL)
1415 load_extra_project_files();
1416 load_project(argv[i]);
1418 else
1419 add_schematic(argv[i]);
1424 gint
1425 main(gint argc, gchar **argv)
1427 gchar *pcb_file_name,
1428 *pcb_new_file_name,
1429 *bak_file_name,
1430 *pins_file_name,
1431 *net_file_name,
1432 *tmp;
1433 gint i;
1434 gboolean initial_pcb = TRUE;
1435 gboolean created_pcb_file = TRUE;
1436 char *path, *p;
1437 const char *pcbdata_path;
1439 if (argc < 2)
1440 usage();
1442 pcbdata_path = g_getenv ("PCBDATA"); /* do not free return value */
1443 if (pcbdata_path != NULL)
1445 /* If PCBDATA is set, use the value */
1446 m4_pcbdir = g_strconcat( pcbdata_path, "/pcb/m4", NULL );
1447 } else {
1448 /* Use the default value passed in from the configure script
1449 * instead of trying to hard code a value which is very
1450 * likely wrong
1452 m4_pcbdir = g_strconcat( PCBDATADIR, "/pcb/m4", NULL );
1455 default_m4_pcbdir = g_strdup(m4_pcbdir);
1457 get_args(argc, argv);
1459 load_extra_project_files();
1460 add_default_m4_files();
1462 if (!schematics)
1463 usage();
1466 /* Defaults for the search path if not configured in the project file */
1467 if (g_file_test("packages", G_FILE_TEST_IS_DIR))
1468 element_directory_list = g_list_append(element_directory_list,
1469 "packages");
1471 #define PCB_PATH_DELIMETER ":"
1472 if (verbose)
1473 printf ("Processing PCBLIBPATH=\"%s\"\n", PCBLIBPATH);
1475 path = g_strdup (PCBLIBPATH);
1476 for (p = strtok (path, PCB_PATH_DELIMETER); p && *p;
1477 p = strtok (NULL, PCB_PATH_DELIMETER))
1479 if (g_file_test(p, G_FILE_TEST_IS_DIR))
1481 if (verbose)
1482 printf ("Adding %s to the newlib search path\n", p);
1483 element_directory_list = g_list_append(element_directory_list,
1484 g_strdup(p));
1487 g_free (path);
1489 pins_file_name = g_strconcat(basename, ".cmd", NULL);
1490 net_file_name = g_strconcat(basename, ".net", NULL);
1491 pcb_file_name = g_strconcat(basename, ".pcb", NULL);
1492 bak_file_name = g_strconcat(basename, ".pcb.bak", NULL);
1493 tmp = g_strdup(bak_file_name);
1495 for (i = 0; g_file_test(bak_file_name, G_FILE_TEST_EXISTS); ++i)
1497 g_free(bak_file_name);
1498 bak_file_name = g_strdup_printf("%s%d", tmp, i);
1500 g_free(tmp);
1502 if (g_file_test(pcb_file_name, G_FILE_TEST_EXISTS))
1504 initial_pcb = FALSE;
1505 pcb_new_file_name = g_strconcat(basename, ".new.pcb", NULL);
1506 get_pcb_element_list(pcb_file_name);
1508 else
1509 pcb_new_file_name = g_strdup(pcb_file_name);
1511 run_gnetlist(pins_file_name, net_file_name, pcb_new_file_name, basename, schematics);
1513 if (add_elements(pcb_new_file_name) == 0)
1515 tmp = g_strconcat("rm ", pcb_new_file_name, NULL);
1516 g_spawn_command_line_sync(tmp, NULL, NULL, NULL, NULL);
1517 g_free(tmp);
1518 if (initial_pcb)
1520 printf("No elements found, so nothing to do.\n");
1521 exit(0);
1525 if (fix_elements)
1526 update_element_descriptions(pcb_file_name, bak_file_name);
1527 prune_elements(pcb_file_name, bak_file_name);
1529 /* Report work done during processing */
1530 if (verbose)
1531 printf("\n");
1532 printf("\n----------------------------------\n");
1533 printf("Done processing. Work performed:\n");
1534 if (n_deleted > 0 || n_fixed > 0 || need_PKG_purge || n_changed_value > 0)
1535 printf("%s is backed up as %s.\n",
1536 pcb_file_name, bak_file_name);
1537 if (pcb_element_list && n_deleted > 0)
1538 printf("%d elements deleted from %s.\n", n_deleted, pcb_file_name);
1540 if (n_added_ef + n_added_m4 > 0)
1541 printf("%d file elements and %d m4 elements added to %s.\n",
1542 n_added_ef, n_added_m4, pcb_new_file_name);
1543 else if (n_not_found == 0)
1545 printf("No elements to add so not creating %s\n", pcb_new_file_name);
1546 created_pcb_file = FALSE;
1549 if (n_not_found > 0)
1551 printf("%d not found elements added to %s.\n",
1552 n_not_found, pcb_new_file_name);
1554 if (n_unknown > 0)
1555 printf("%d components had no footprint attribute and are omitted.\n",
1556 n_unknown);
1557 if (n_none > 0)
1558 printf("%d components with footprint \"none\" omitted from %s.\n",
1559 n_none, pcb_new_file_name);
1560 if (n_empty > 0)
1561 printf("%d components with empty footprint \"%s\" omitted from %s.\n",
1562 n_empty, empty_footprint_name, pcb_new_file_name);
1563 if (n_changed_value > 0)
1564 printf("%d elements had a value change in %s.\n",
1565 n_changed_value, pcb_file_name);
1566 if (n_fixed > 0)
1567 printf("%d elements fixed in %s.\n", n_fixed, pcb_file_name);
1568 if (n_PKG_removed_old > 0)
1570 printf("%d elements could not be found.", n_PKG_removed_old);
1571 if (created_pcb_file)
1572 printf(" So %s is incomplete.\n", pcb_file_name);
1573 else
1574 printf("\n");
1576 if (n_PKG_removed_new > 0)
1578 printf("%d elements could not be found.", n_PKG_removed_new);
1579 if (created_pcb_file)
1580 printf(" So %s is incomplete.\n", pcb_new_file_name);
1581 else
1582 printf("\n");
1584 if (n_preserved > 0)
1585 printf("%d elements not in the schematic preserved in %s.\n",
1586 n_preserved, pcb_file_name);
1588 /* Tell user what to do next */
1589 if (verbose)
1590 printf("\n");
1592 if (n_added_ef + n_added_m4 > 0)
1594 if (initial_pcb)
1596 printf("\nNext step:\n");
1597 printf("1. Run pcb on your file %s.\n", pcb_file_name);
1598 printf(" You will find all your footprints in a bundle ready for you to place\n");
1599 printf(" or disperse with \"Select -> Disperse all elements\" in PCB.\n\n");
1600 printf("2. From within PCB, select \"File -> Load netlist file\" and select \n");
1601 printf(" %s to load the netlist.\n\n", net_file_name);
1602 printf("3. From within PCB, enter\n\n");
1603 printf(" :ExecuteFile(%s)\n\n", pins_file_name);
1604 printf(" to propagate the pin names of all footprints to the layout.\n\n");
1606 else if (quiet_mode == FALSE)
1608 printf("\nNext steps:\n");
1609 printf("1. Run pcb on your file %s.\n", pcb_file_name);
1610 printf("2. From within PCB, select \"File -> Load layout data to paste buffer\"\n");
1611 printf(" and select %s to load the new footprints into your existing layout.\n",
1612 pcb_new_file_name);
1613 printf("3. From within PCB, select \"File -> Load netlist file\" and select \n");
1614 printf(" %s to load the updated netlist.\n\n", net_file_name);
1615 printf("4. From within PCB, enter\n\n");
1616 printf(" :ExecuteFile(%s)\n\n", pins_file_name);
1617 printf(" to update the pin names of all footprints.\n\n");
1621 g_free(net_file_name);
1622 g_free(pins_file_name);
1623 g_free(pcb_file_name);
1624 g_free(bak_file_name);
1626 return 0;