gaf: Fix memory leak
[geda-gaf.git] / utils / src / gsch2pcb.c
blob4bded6b95b97f7aa9c536ca076bb2da5187c7603
1 /* gsch2pcb
3 * Bill Wilson billw@wt.net
5 * This program is free software which I release under the GNU General Public
6 * License. You may redistribute and/or modify this program under the terms
7 * of that license as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details. Version 2 is in the
14 * COPYRIGHT file in the top level directory of this distribution.
16 * To get a copy of the GNU General Puplic License, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
25 #include <glib.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <unistd.h>
32 #include <sys/stat.h>
34 #define GSC2PCB_VERSION "1.6"
36 #define DEFAULT_PCB_INC "pcb.inc"
38 #define SEP_STRING "--------\n"
40 typedef struct
42 gchar *refdes, *value, *description, *changed_description, *changed_value;
43 gchar *flags, *tail;
44 gchar *x, *y;
45 gchar *pkg_name_fix;
46 gchar res_char;
48 gboolean still_exists, new_format, hi_res_format, quoted_flags, omit_PKG;
50 PcbElement;
53 typedef struct
55 gchar *part_number, *element_name;
57 ElementMap;
59 static GList *pcb_element_list,
60 *element_directory_list, *extra_gnetlist_list, *extra_gnetlist_arg_list;
62 static gchar *sch_basename;
64 static GList *schematics;
66 static gchar *m4_pcbdir, *default_m4_pcbdir, *m4_files, *m4_override_file;
68 static gboolean use_m4 = TRUE;
70 static gchar *empty_footprint_name;
72 static gint verbose,
73 n_deleted,
74 n_added_m4,
75 n_added_ef,
76 n_fixed,
77 n_PKG_removed_new,
78 n_PKG_removed_old,
79 n_preserved, n_changed_value, n_not_found, n_unknown, n_none, n_empty;
81 static gboolean remove_unfound_elements = TRUE,
82 quiet_mode = FALSE,
83 force_element_files, preserve, fix_elements, bak_done, need_PKG_purge;
86 static void
87 create_m4_override_file ()
89 FILE *f;
91 m4_override_file = "gnet-gsch2pcb-tmp.scm";
92 f = fopen (m4_override_file, "wb");
93 if (!f) {
94 m4_override_file = NULL;
95 return;
97 if (m4_pcbdir)
98 fprintf (f, "(define gsch2pcb:pcb-m4-dir \"%s\")\n", m4_pcbdir);
99 if (m4_files)
100 fprintf (f, "(define gsch2pcb:m4-files \"%s\")\n", m4_files);
101 fprintf (f, "(define gsch2pcb:use-m4 %s)\n", use_m4 == TRUE ? "#t" : "#f");
103 fclose (f);
104 if (verbose) {
105 printf ("Default m4-pcbdir: %s\n", default_m4_pcbdir);
106 printf ("--------\ngnet-gsch2pcb-tmp.scm override file:\n");
107 if (m4_pcbdir)
108 printf (" (define gsch2pcb:pcb-m4-dir \"%s\")\n", m4_pcbdir);
109 if (m4_files)
110 printf (" (define gsch2pcb:m4-files \"%s\")\n", m4_files);
111 printf (" (define gsch2pcb:use-m4 %s)\n", use_m4 == TRUE ? "#t" : "#f");
116 * Build and run a command. No redirection or error handling is
117 * done. Format string is split on whitespace. Specifiers %l and %s
118 * are replaced with contents of positional args. To be recognized,
119 * specifiers must be separated from other arguments in the format by
120 * whitespace.
121 * - %l expects a GList, contents used as separate arguments
122 * - %s expects a gchar*, contents used as a single argument
123 * @param[in] format used to specify command to be executed
124 * @param[in] ... positional parameters
126 static gboolean
127 build_and_run_command (const gchar *format, ...)
129 va_list vargs;
130 gchar ** split;
131 GList *tmp = NULL;
132 gint num_split;
133 gint i;
134 gint status;
135 gboolean result = FALSE;
136 gboolean spawn_result;
137 gchar *standard_output = NULL;
138 gchar *standard_error = NULL;
139 GError * error = NULL;
141 va_start (vargs, format);
142 split = g_strsplit_set (format, " \t\n\v", 0);
143 num_split = g_strv_length (split);
144 for (i = 0; i < num_split; ++i) {
145 gchar *chunk = split[i];
146 if (strcmp (chunk, "%l") == 0) {
147 /* append contents of list into command args - shared data */
148 tmp = g_list_concat (tmp, g_list_copy (va_arg (vargs, GList*)));
149 } else if (strcmp (chunk, "%s") == 0) {
150 /* insert contents of string into output */
151 tmp = g_list_append (tmp, va_arg (vargs, gchar*));
152 } else {
153 /* bare string, use as is */
154 tmp = g_list_append (tmp, chunk);
157 va_end (vargs);
159 if (tmp) {
160 /* we have something in the list, build & call command */
161 GList *p;
162 gint i = 0;
163 gchar ** args = g_new0 (gchar*, g_list_length (tmp) + 1/* NULL terminate the list */);
165 if (verbose)
166 printf ("Running command:\n\t");
168 for (p = tmp; p; p = g_list_next (p)) {
169 args[i++] = (gchar*) p->data;
170 if (verbose)
171 printf ("%s ", (char*)p->data);
174 if (verbose)
175 printf ("\n%s", SEP_STRING);
177 if (g_spawn_sync (".", /* Working directory */
178 args, /* argv */
179 NULL, /* envp */
180 G_SPAWN_SEARCH_PATH, /* flags */
181 NULL, /* child_setup */
182 NULL, /* user data */
183 &standard_output, /* standard output */
184 &standard_error, /* standard error */
185 &status, /* exit status return */
186 &error)) { /* GError return */
187 if (verbose)
188 fputs(standard_output, stdout);
189 if (status == 0)
190 result = TRUE;
191 else {
192 if (standard_error)
193 fputs(standard_error, stderr);
196 else {
197 fprintf(stderr, "Failed to execute external program: %s\n", error->message);
198 g_error_free(error);
201 if (verbose)
202 printf ("\n%s", SEP_STRING);
204 g_free(standard_error);
205 g_free (standard_output);
207 g_free (args);
208 /* free the list, but leave data untouched */
209 g_list_free (tmp);
212 g_strfreev (split);
214 return result;
217 /* Run gnetlist to generate a netlist and a PCB board file. gnetlist
218 * has exit status of 0 even if it's given an invalid arg, so do some
219 * stat() hoops to decide if gnetlist successfully generated the PCB
220 * board file (only gnetlist >= 20030901 recognizes -m).
222 static gboolean
223 run_gnetlist (gchar * pins_file, gchar * net_file, gchar * pcb_file,
224 gchar * basename, GList * largs)
226 struct stat st;
227 time_t mtime;
228 static const gchar *gnetlist = NULL;
229 GList *list = NULL;
230 GList *verboseList = NULL;
231 GList *args1 = NULL;
233 /* Allow the user to specify a full path or a different name for
234 * the gnetlist command. Especially useful if multiple copies
235 * are installed at once.
237 if (gnetlist == NULL)
238 gnetlist = g_getenv ("GNETLIST");
239 if (gnetlist == NULL)
240 gnetlist = "gnetlist";
242 if (!verbose)
243 verboseList = g_list_append (verboseList, "-q");
245 if (!build_and_run_command ("%s %l -g pcbpins -o %s %l %l",
246 gnetlist,
247 verboseList,
248 pins_file,
249 extra_gnetlist_arg_list,
250 largs))
251 return FALSE;
253 if (!build_and_run_command ("%s %l -g PCB -o %s %l %l",
254 gnetlist,
255 verboseList,
256 net_file,
257 extra_gnetlist_arg_list,
258 largs))
259 return FALSE;
260 create_m4_override_file ();
262 if (m4_override_file) {
263 args1 = g_list_append (args1, "-m");
264 args1 = g_list_append (args1, m4_override_file);
267 mtime = (stat (pcb_file, &st) == 0) ? st.st_mtime : 0;
269 if (!build_and_run_command ("%s %l -g gsch2pcb -o %s %l %l %l",
270 gnetlist,
271 verboseList,
272 pcb_file,
273 args1,
274 extra_gnetlist_arg_list,
275 largs)) {
276 if (stat (pcb_file, &st) != 0 || mtime == st.st_mtime) {
277 fprintf (stderr,
278 "gsch2pcb: gnetlist command failed, `%s' not updated\n",
279 pcb_file
281 if (m4_override_file)
282 fprintf (stderr,
283 " At least gnetlist 20030901 is required for m4-xxx options.\n");
284 return FALSE;
286 return FALSE;
289 if (m4_override_file)
290 unlink (m4_override_file);
292 for (list = extra_gnetlist_list; list; list = g_list_next (list)) {
293 const gchar *s = (gchar *) list->data;
294 const gchar *s2 = strstr (s, " -o ");
295 gchar *out_file;
296 gchar *backend;
297 if (!s2) {
298 out_file = g_strconcat (basename, ".", s, NULL);
299 backend = g_strdup (s);
300 } else {
301 out_file = g_strdup (s2 + 4);
302 backend = g_strndup (s, s2 - s);
305 if (!build_and_run_command ("%s %l -g %s -o %s %l %l",
306 gnetlist,
307 verboseList,
308 backend,
309 out_file,
310 extra_gnetlist_arg_list,
311 largs))
312 return FALSE;
313 g_free (out_file);
314 g_free (backend);
317 g_list_free (args1);
318 g_list_free (verboseList);
320 return TRUE;
323 static gchar *
324 token (gchar * string, gchar ** next, gboolean * quoted_ret)
326 static gchar *str;
327 gchar *s, *ret;
328 gboolean quoted = FALSE;
330 if (string)
331 str = string;
332 if (!str || !*str) {
333 if (next)
334 *next = str;
335 return g_strdup ("");
337 while (*str == ' ' || *str == '\t' || *str == ',' || *str == '\n')
338 ++str;
339 if (*str == '"') {
340 quoted = TRUE;
341 if (quoted_ret)
342 *quoted_ret = TRUE;
343 ++str;
344 for (s = str; *s && *s != '"' && *s != '\n'; ++s);
345 } else {
346 if (quoted_ret)
347 *quoted_ret = FALSE;
348 for (s = str;
349 *s && (*s != ' ' && *s != '\t' && *s != ',' && *s != '\n'); ++s);
351 ret = g_strndup (str, s - str);
352 str = (quoted && *s) ? s + 1 : s;
353 if (next)
354 *next = str;
355 return ret;
358 static gchar *
359 fix_spaces (gchar * str)
361 gchar *s;
363 if (!str)
364 return NULL;
365 for (s = str; *s; ++s)
366 if (*s == ' ' || *s == '\t')
367 *s = '_';
368 return str;
371 /* As of 1/9/2004 CVS hi_res Element[] line format:
372 * Element[element_flags, description, pcb-name, value, mark_x, mark_y,
373 * text_x, text_y, text_direction, text_scale, text_flags]
374 * New PCB 1.7 / 1.99 Element() line format:
375 * Element(element_flags, description, pcb-name, value, mark_x, mark_y,
376 * text_x, text_y, text_direction, text_scale, text_flags)
377 * Old PCB 1.6 Element() line format:
378 * Element(element_flags, description, pcb-name, value,
379 * text_x, text_y, text_direction, text_scale, text_flags)
381 * (mark_x, mark_y) is the element position (mark) and (text_x,text_y)
382 * is the description text position which is absolute in pre 1.7 and
383 * is now relative. The hi_res mark_x,mark_y and text_x,text_y resolutions
384 * are 100x the other formats.
386 PcbElement *
387 pcb_element_line_parse (gchar * line)
389 PcbElement *el = NULL;
390 gchar *s, *t, close_char;
391 gint state = 0, elcount = 0;
393 if (strncmp (line, "Element", 7))
394 return NULL;
396 el = g_new0 (PcbElement, 1);
398 s = line + 7;
399 while (*s == ' ' || *s == '\t')
400 ++s;
402 if (*s == '[')
403 el->hi_res_format = TRUE;
404 else if (*s != '(') {
405 g_free (el);
406 return NULL;
409 el->res_char = el->hi_res_format ? '[' : '(';
410 close_char = el->hi_res_format ? ']' : ')';
412 el->flags = token (s + 1, NULL, &el->quoted_flags);
413 el->description = token (NULL, NULL, NULL);
414 el->refdes = token (NULL, NULL, NULL);
415 el->value = token (NULL, NULL, NULL);
417 el->x = token (NULL, NULL, NULL);
418 el->y = token (NULL, &t, NULL);
420 el->tail = g_strdup (t ? t : "");
421 if ((s = strrchr (el->tail, (gint) '\n')) != NULL)
422 *s = '\0';
424 /* Count the tokens in tail to decide if it's new or old format.
425 * Old format will have 3 tokens, new format will have 5 tokens.
427 for (s = el->tail; *s && *s != close_char; ++s) {
428 if (*s != ' ') {
429 if (state == 0)
430 ++elcount;
431 state = 1;
432 } else
433 state = 0;
435 if (elcount > 4)
436 el->new_format = TRUE;
438 fix_spaces (el->description);
439 fix_spaces (el->refdes);
440 fix_spaces (el->value);
442 /* Don't allow elements with no refdes to ever be deleted because
443 * they may be desired pc board elements not in schematics. So
444 * initialize still_exists to TRUE if empty or non-alphanumeric
445 * refdes.
447 if (!*el->refdes || !isalnum ((gint) (*el->refdes)))
448 el->still_exists = TRUE;
450 return el;
453 static void
454 pcb_element_free (PcbElement * el)
456 if (!el)
457 return;
458 g_free (el->flags);
459 g_free (el->description);
460 g_free (el->changed_description);
461 g_free (el->changed_value);
462 g_free (el->refdes);
463 g_free (el->value);
464 g_free (el->x);
465 g_free (el->y);
466 g_free (el->tail);
467 g_free (el->pkg_name_fix);
468 g_free (el);
471 static void
472 get_pcb_element_list (gchar * pcb_file)
474 FILE *f;
475 PcbElement *el;
476 gchar *s, buf[1024];
478 if ((f = fopen (pcb_file, "r")) == NULL)
479 return;
480 while ((fgets (buf, sizeof (buf), f)) != NULL) {
481 for (s = buf; *s == ' ' || *s == '\t'; ++s);
482 if (!strncmp (s, "PKG_", 4)) {
483 need_PKG_purge = TRUE;
484 continue;
486 if ((el = pcb_element_line_parse (s)) == NULL)
487 continue;
488 pcb_element_list = g_list_append (pcb_element_list, el);
490 fclose (f);
493 static PcbElement *
494 pcb_element_exists (PcbElement * el_test, gboolean record)
496 GList *list;
497 PcbElement *el;
499 for (list = pcb_element_list; list; list = g_list_next (list)) {
500 el = (PcbElement *) list->data;
502 if (strcmp (el_test->refdes, el->refdes))
503 continue;
504 if (strcmp (el_test->description, el->description)) { /* footprint */
505 if (record)
506 el->changed_description = g_strdup (el_test->description);
507 } else {
508 if (record) {
509 if (strcmp (el_test->value, el->value))
510 el->changed_value = g_strdup (el_test->value);
511 el->still_exists = TRUE;
513 return el;
516 return NULL;
519 /* A problem is that new PCB 1.7 file elements have the
520 * (mark_x,mark_y) value set to wherever the element was created and
521 * no equivalent of a gschem translate symbol was done.
523 * So, file elements inserted can be scattered over a big area and
524 * this is bad when loading a file.new.pcb into an existing PC
525 * board. So, do a simple translate if (mark_x,mark_y) is
526 * (arbitrarily) over 1000. I'll assume that for values < 1000 the
527 * element creator was concerned with a sane initial element
528 * placement. Unless someone has a better idea? Don't bother with
529 * pre PCB 1.7 formats as that would require parsing the mark().
530 * Current m4 elements use the old format but they seem to have a
531 * reasonable initial mark().
533 static void
534 simple_translate (PcbElement * el)
537 el->x=strdup("0");
538 el->y=strdup("0");
541 static gboolean
542 insert_element (FILE * f_out, gchar * element_file,
543 gchar * footprint, gchar * refdes, gchar * value)
545 FILE *f_in;
546 PcbElement *el;
547 gchar *fmt, *s, buf[1024];
548 gboolean retval = FALSE;
550 if ((f_in = fopen (element_file, "r")) == NULL) {
551 s = g_strdup_printf ("insert_element() can't open %s", element_file);
552 perror (s);
553 g_free (s);
554 return FALSE;
556 /* Scan the file to detect whether it's actually a PCB
557 * layout. Assumes that a PCB layout will have a "PCB" line. */
558 while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
559 for (s = buf; *s == ' ' || *s == '\t'; ++s);
560 s[3] = 0; /* Truncate line */
561 if (strncmp ("PCB", s, sizeof (buf)) == 0) {
562 printf ("Warning: %s appears to be a PCB layout file. Skipping.\n",
563 element_file);
564 fclose (f_in);
565 return FALSE;
568 rewind (f_in);
570 /* Copy the file element lines. Substitute new parameters into the
571 * Element() or Element[] line and strip comments.
573 while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
574 for (s = buf; *s == ' ' || *s == '\t'; ++s);
575 if ((el = pcb_element_line_parse (s)) != NULL) {
576 simple_translate (el);
577 fmt = el->quoted_flags ?
578 "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" :
579 "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n";
581 fprintf (f_out, fmt,
582 el->res_char, el->flags, footprint, refdes, value,
583 el->x, el->y, el->tail);
584 retval = TRUE;
585 } else if (*s != '#')
586 fputs (buf, f_out);
587 pcb_element_free (el);
589 fclose (f_in);
590 return retval;
594 gchar *
595 find_element (gchar * dir_path, gchar * element)
597 GDir *dir;
598 gchar *path, *name, *s, *found = NULL;
600 if ((dir = g_dir_open (dir_path, 0, NULL)) == NULL) {
601 s = g_strdup_printf ("find_element can't open dir \"%s\"", dir_path);
602 perror (s);
603 g_free (s);
604 return NULL;
606 if (verbose > 1)
607 printf ("\t Searching: \"%s\" for \"%s\"\n", dir_path, element);
608 while ((name = (gchar *) g_dir_read_name (dir)) != NULL) {
609 path = g_strconcat (dir_path, "/", name, NULL);
610 found = NULL;
612 /* if we got a directory name, then recurse down into it */
613 if (g_file_test (path, G_FILE_TEST_IS_DIR))
614 found = find_element (path, element);
616 /* otherwise assume it is a file and see if it is the one we want */
617 else {
618 if (verbose > 1)
619 printf ("\t : %s\t", name);
620 if (!strcmp (name, element))
621 found = g_strdup (path);
622 else {
623 gchar *tmps;
624 tmps = g_strconcat (element, ".fp", NULL);
625 if (!strcmp (name, tmps))
626 found = g_strdup (path);
627 g_free (tmps);
629 if (verbose > 1)
630 printf ("%s\n", found ? "Yes" : "No");
632 g_free (path);
633 if (found)
634 break;
636 g_dir_close (dir);
637 return found;
640 gchar *
641 search_element_directories (PcbElement * el)
643 GList *list;
644 gchar *s, *elname = NULL, *dir_path, *path = NULL;
645 gint n1, n2;
647 /* See comment before pkg_to_element() */
648 if (el->pkg_name_fix) {
649 if (strchr (el->description, '-')) {
650 n1 = strlen (el->description);
651 n2 = strlen (el->pkg_name_fix);
652 s = el->description + n1 - n2 - 1;
654 // printf("n1=%d n2=%d desc:%s fix:%s s:%s\n",
655 // n1, n2, el->description, el->pkg_name_fix, s);
657 if (n1 > 0 && n2 < n1 && *s == '-' && *(s + 1) == *el->pkg_name_fix) {
658 s = g_strndup (el->description, n1 - n2 - 1);
659 elname = g_strconcat (s, " ", el->pkg_name_fix, NULL);
660 g_free (s);
663 if (!elname) {
664 printf ("Warning: argument passing may have been confused by\n");
665 printf (" a comma in a component value:\n");
666 printf (" Check %s %s %s\n",
667 el->refdes, el->description, el->value);
668 printf (" Maybe just use a space instead of a comma?\n");
671 if (!elname)
672 elname = g_strdup (el->description);
674 if (!strcmp (elname, "unknown")) {
675 g_free (elname);
676 return NULL;
678 if (verbose > 1)
679 printf ("\tSearching directories looking for file element: %s\n", elname);
680 for (list = element_directory_list; list; list = g_list_next (list)) {
681 dir_path = (gchar *) list->data;
682 if (verbose > 1)
683 printf ("\tLooking in directory: \"%s\"\n", dir_path);
684 path = find_element (dir_path, elname);
685 if (path) {
686 if (verbose)
687 printf ("\tFound: %s\n", path);
688 break;
691 g_free (elname);
692 return path;
695 /* The gnetlist backend gnet-gsch2pcb.scm generates PKG_ lines:
697 * PKG_footprint(footprint{-fp0-fp1},refdes,value{,fp0,fp1})
699 * where fp1 and fp2 (if they exist) are the extra footprint
700 * components when specifying footprints like "DIL 14 300". This is
701 * needed for m4 macros.
703 * A complication is if the footprint references a file element with
704 * spaces embedded in the name. The gnetlist backend will interpret
705 * these as fp0, fp1, ... args and the footprint will in this case
706 * incorrectly have '-' inserted where the spaces should be. So, if
707 * there are additional args, reconstruct the portion of the name
708 * given by the args with spaces for later use. Eg. if the footprint
709 * is "100 Pin jack", we will have
711 * PKG_100-Pin-jack(100-Pin-jack,refdes,value,Pin,jack)
713 * So put "Pin jack" into pkg_name_fix so if this element is searched
714 * as a file element we can munge the description to what it should
715 * be, eg:
717 * 100-Pin-jack -> 100 Pin jack
719 static PcbElement *
720 pkg_to_element (FILE * f, gchar * pkg_line)
722 PcbElement *el;
723 gchar **args, *s;
724 gint n, n_extra_args, n_dashes;
726 if (strncmp (pkg_line, "PKG_", 4)
727 || (s = strchr (pkg_line, (gint) '(')) == NULL)
728 return NULL;
730 args = g_strsplit (s + 1, ",", 12);
731 if (!args[0] || !args[1] || !args[2]) {
732 fprintf (stderr, "Bad package line: %s\n", pkg_line);
733 return NULL;
735 fix_spaces (args[0]);
736 fix_spaces (args[1]);
737 fix_spaces (args[2]);
739 el = g_new0 (PcbElement, 1);
740 el->description = g_strdup (args[0]);
741 el->refdes = g_strdup (args[1]);
742 el->value = g_strdup (args[2]);
743 if ((s = strchr (el->value, (gint) ')')) != NULL)
744 *s = '\0';
746 /* If the component value has a comma, eg "1k, 1%", the gnetlist generated
747 * PKG line will be
749 * PKG_XXX(`R0w8',`R100',`1k, 1%'),
751 * but after processed by m4, the input to gsch2pcb will be
753 * PKG_XXX(R0w8,R100,1k, 1%).
755 * So the quoting info has been lost when processing for file
756 * elements. So here try to detect and fix this. But I can't
757 * handle the situation where the description has a '-' and the
758 * value has a comma because gnet-gsch2pcb.scm munges the
759 * description with '-' when there are extra args.
761 for (n_extra_args = 0; args[3 + n_extra_args] != NULL; ++n_extra_args);
762 s = el->description;
763 for (n_dashes = 0; (s = strchr (s + 1, '-')) != NULL; ++n_dashes);
765 n = 3;
766 if (n_extra_args == n_dashes + 1) { /* Assume there was a comma in the value, eg "1K, 1%" */
767 s = el->value;
768 el->value = g_strconcat (s, ",", fix_spaces (args[n]), NULL);
769 g_free (s);
770 if ((s = strchr (el->value, (gint) ')')) != NULL)
771 *s = '\0';
772 n = 4;
774 if (args[n]) {
775 el->pkg_name_fix = g_strdup (args[n]);
776 for (n += 1; args[n] != NULL; ++n) {
777 s = el->pkg_name_fix;
778 el->pkg_name_fix = g_strconcat (s, " ", args[n], NULL);
779 g_free (s);
781 if ((s = strchr (el->pkg_name_fix, (gint) ')')) != NULL)
782 *s = '\0';
784 g_strfreev (args);
786 if (empty_footprint_name && !strcmp (el->description, empty_footprint_name)) {
787 if (verbose)
788 printf
789 ("%s: has the empty footprint attribute \"%s\" so won't be in the layout.\n",
790 el->refdes, el->description);
791 n_empty += 1;
792 el->omit_PKG = TRUE;
793 } else if (!strcmp (el->description, "none")) {
794 fprintf (stderr,
795 "WARNING: %s has a footprint attribute \"%s\" so won't be in the layout.\n",
796 el->refdes, el->description);
797 n_none += 1;
798 el->omit_PKG = TRUE;
799 } else if (!strcmp (el->description, "unknown")) {
800 fprintf (stderr,
801 "WARNING: %s has no footprint attribute so won't be in the layout.\n",
802 el->refdes);
803 n_unknown += 1;
804 el->omit_PKG = TRUE;
806 return el;
809 /* Process the newly created pcb file which is the output from
810 * gnetlist -g gsch2pcb ...
812 * It will have elements found via the m4 interface and PKG_ lines for
813 * elements not found. Insert pcb file elements for PKG_ lines if
814 * file elements can be found. If there was an existing pcb file,
815 * strip out any elements if they are already present so that the new
816 * pcb file will only have new elements.
818 static gint
819 add_elements (gchar * pcb_file)
821 FILE *f_in, *f_out;
822 PcbElement *el = NULL;
823 gchar *command, *p, *tmp_file, *s, buf[1024];
824 gint total, paren_level = 0;
825 gboolean is_m4, skipping = FALSE;
827 if ((f_in = fopen (pcb_file, "r")) == NULL)
828 return 0;
829 tmp_file = g_strconcat (pcb_file, ".tmp", NULL);
830 if ((f_out = fopen (tmp_file, "wb")) == NULL) {
831 fclose (f_in);
832 g_free (tmp_file);
833 return 0;
835 while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
836 for (s = buf; *s == ' ' || *s == '\t'; ++s);
837 if (skipping) {
838 if (*s == '(')
839 ++paren_level;
840 else if (*s == ')' && --paren_level <= 0)
841 skipping = FALSE;
842 continue;
844 is_m4 = FALSE;
845 if ((el = pcb_element_line_parse (s)) != NULL)
846 is_m4 = TRUE;
847 else
848 el = pkg_to_element (f_out, s);
849 if (el && pcb_element_exists (el, TRUE)) {
850 skipping = is_m4;
851 pcb_element_free (el);
852 continue;
854 if (!el || el->omit_PKG) {
855 if (el) {
857 } else
858 fputs (buf, f_out);
859 continue;
861 if (!is_m4 || (is_m4 && force_element_files)) {
862 if (verbose && !is_m4)
863 printf ("%s: need new file element for footprint %s (value=%s)\n",
864 el->refdes, el->description, el->value);
865 if (verbose && is_m4 && force_element_files)
866 printf
867 ("%s: have m4 element %s, but trying to replace with a file element.\n",
868 el->refdes, el->description);
869 p = search_element_directories (el);
870 if (!p && verbose && is_m4 && force_element_files)
871 printf ("\tNo file element found.\n");
873 if (p && insert_element (f_out, p,
874 el->description, el->refdes, el->value)) {
875 skipping = is_m4;
876 is_m4 = FALSE;
877 ++n_added_ef;
878 if (verbose)
879 printf ("%s: added new file element for footprint %s (value=%s)\n",
880 el->refdes, el->description, el->value);
881 } else if (!is_m4) {
882 fprintf (stderr,
883 "%s: can't find PCB element for footprint %s (value=%s)\n",
884 el->refdes, el->description, el->value);
885 if (remove_unfound_elements && !fix_elements) {
886 fprintf (stderr,
887 "So device %s will not be in the layout.\n", el->refdes);
888 ++n_PKG_removed_new;
889 } else {
890 ++n_not_found;
891 fputs (buf, f_out); /* Copy PKG_ line */
894 g_free (p);
896 if (is_m4) {
897 fputs (buf, f_out);
898 ++n_added_m4;
899 if (verbose)
900 printf ("%s: added new m4 element for footprint %s (value=%s)\n",
901 el->refdes, el->description, el->value);
903 pcb_element_free (el);
904 if (verbose)
905 printf ("----\n");
907 fclose (f_in);
908 fclose (f_out);
910 total = n_added_ef + n_added_m4 + n_not_found;
911 if (total == 0)
912 build_and_run_command ("rm %s", tmp_file);
913 else
914 build_and_run_command ("mv %s %s", tmp_file, pcb_file);
915 g_free (tmp_file);
916 return total;
919 static void
920 update_element_descriptions (gchar * pcb_file, gchar * bak)
922 FILE *f_in, *f_out;
923 GList *list;
924 PcbElement *el, *el_exists;
925 gchar *fmt, *command, *tmp, *s, buf[1024];
927 for (list = pcb_element_list; list; list = g_list_next (list)) {
928 el = (PcbElement *) list->data;
929 if (el->changed_description)
930 ++n_fixed;
932 if (!pcb_element_list || n_fixed == 0) {
933 fprintf (stderr, "Could not find any elements to fix.\n");
934 return;
936 if ((f_in = fopen (pcb_file, "r")) == NULL)
937 return;
938 tmp = g_strconcat (pcb_file, ".tmp", NULL);
939 if ((f_out = fopen (tmp, "wb")) == NULL) {
940 fclose (f_in);
941 return;
943 while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
944 for (s = buf; *s == ' ' || *s == '\t'; ++s);
945 if ((el = pcb_element_line_parse (s)) != NULL
946 && (el_exists = pcb_element_exists (el, FALSE)) != NULL
947 && el_exists->changed_description) {
948 fmt = el->quoted_flags ?
949 "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" :
950 "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n";
951 fprintf (f_out, fmt,
952 el->res_char,
953 el->flags, el_exists->changed_description,
954 el->refdes, el->value, el->x, el->y, el->tail);
955 printf ("%s: updating element Description: %s -> %s\n",
956 el->refdes, el->description, el_exists->changed_description);
957 el_exists->still_exists = TRUE;
958 } else
959 fputs (buf, f_out);
960 pcb_element_free (el);
962 fclose (f_in);
963 fclose (f_out);
965 if (!bak_done) {
966 build_and_run_command ("mv %s %s", pcb_file, bak);
967 bak_done = TRUE;
970 build_and_run_command ("mv %s %s", tmp, pcb_file);
971 g_free (tmp);
974 static void
975 prune_elements (gchar * pcb_file, gchar * bak)
977 FILE *f_in, *f_out;
978 GList *list;
979 PcbElement *el, *el_exists;
980 gchar *fmt, *command, *tmp, *s, buf[1024];
981 gint paren_level = 0;
982 gboolean skipping = FALSE;
984 for (list = pcb_element_list; list; list = g_list_next (list)) {
985 el = (PcbElement *) list->data;
986 if (!el->still_exists) {
987 if (preserve) {
988 ++n_preserved;
989 fprintf (stderr,
990 "Preserving PCB element not in the schematic: %s (element %s)\n",
991 el->refdes, el->description);
992 } else
993 ++n_deleted;
994 } else if (el->changed_value)
995 ++n_changed_value;
997 if (!pcb_element_list
998 || (n_deleted == 0 && !need_PKG_purge && n_changed_value == 0)
1000 return;
1001 if ((f_in = fopen (pcb_file, "r")) == NULL)
1002 return;
1003 tmp = g_strconcat (pcb_file, ".tmp", NULL);
1004 if ((f_out = fopen (tmp, "wb")) == NULL) {
1005 fclose (f_in);
1006 return;
1008 while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
1009 for (s = buf; *s == ' ' || *s == '\t'; ++s);
1010 if (skipping) {
1011 if (*s == '(')
1012 ++paren_level;
1013 else if (*s == ')' && --paren_level <= 0)
1014 skipping = FALSE;
1015 continue;
1017 el_exists = NULL;
1018 if ((el = pcb_element_line_parse (s)) != NULL
1019 && (el_exists = pcb_element_exists (el, FALSE)) != NULL
1020 && !el_exists->still_exists && !preserve) {
1021 skipping = TRUE;
1022 if (verbose)
1023 printf ("%s: deleted element %s (value=%s)\n",
1024 el->refdes, el->description, el->value);
1025 pcb_element_free (el);
1026 continue;
1028 if (el_exists && el_exists->changed_value) {
1029 fmt = el->quoted_flags ?
1030 "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" :
1031 "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n";
1032 fprintf (f_out, fmt,
1033 el->res_char, el->flags, el->description, el->refdes,
1034 el_exists->changed_value, el->x, el->y, el->tail);
1035 if (verbose)
1036 printf ("%s: changed element %s value: %s -> %s\n",
1037 el->refdes, el->description,
1038 el->value, el_exists->changed_value);
1039 } else if (!strncmp (s, "PKG_", 4))
1040 ++n_PKG_removed_old;
1041 else
1042 fputs (buf, f_out);
1043 pcb_element_free (el);
1045 fclose (f_in);
1046 fclose (f_out);
1048 if (!bak_done) {
1049 build_and_run_command ("mv %s %s", pcb_file, bak);
1050 bak_done = TRUE;
1053 build_and_run_command ("mv %s %s", tmp, pcb_file);
1054 g_free (tmp);
1057 static void
1058 add_m4_file (gchar * arg)
1060 gchar *s;
1062 if (!m4_files)
1063 m4_files = g_strdup (arg);
1064 else {
1065 s = m4_files;
1066 m4_files = g_strconcat (m4_files, " ", arg, NULL);
1067 g_free (s);
1071 static gchar *
1072 expand_dir (gchar * dir)
1074 gchar *s;
1075 if (dir == NULL)
1076 return NULL;
1077 if (*dir == '~')
1078 s = g_build_filename ((gchar *) g_get_home_dir (), dir + 1, NULL);
1079 else
1080 s = g_strdup (dir);
1081 return s;
1084 static void
1085 add_default_m4_files (void)
1087 gchar *path;
1089 path = g_build_filename ((gchar *) g_get_home_dir (),
1090 ".pcb", DEFAULT_PCB_INC, NULL);
1091 if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
1092 add_m4_file (path);
1093 g_free (path);
1095 if (g_file_test (DEFAULT_PCB_INC, G_FILE_TEST_IS_REGULAR))
1096 add_m4_file (DEFAULT_PCB_INC);
1100 static void
1101 add_schematic (gchar * sch)
1103 const gchar* s;
1104 schematics = g_list_append (schematics, g_strdup (sch));
1105 if (!sch_basename && (s = g_strrstr (sch, ".sch")) != NULL && strlen(s) == 4)
1106 sch_basename = g_strndup (sch, s - sch);
1109 static void
1110 add_multiple_schematics (gchar * sch)
1112 /* parse the string using shell semantics */
1113 gint count;
1114 gchar** args = NULL;
1115 GError* error = NULL;
1117 if (g_shell_parse_argv (sch, &count, &args, &error)) {
1118 int i;
1119 for (i = 0; i < count; ++i)
1121 add_schematic (args[i]);
1123 g_strfreev (args);
1124 } else {
1125 fprintf (stderr,
1126 "invalid `schematics' option: %s\n",
1127 error->message);
1128 g_error_free (error);
1132 static gint
1133 parse_config (gchar * config, gchar * arg)
1135 gchar *s;
1137 /* remove trailing white space otherwise strange things can happen */
1138 if ((arg != NULL) && (strlen (arg) >= 1)) {
1139 s = arg + strlen (arg) - 1;
1140 while ((*s == ' ' || *s == '\t') && (s != arg))
1141 s--;
1142 s++;
1143 *s = '\0';
1145 if (verbose)
1146 printf (" %s \"%s\"\n", config, arg ? arg : "");
1148 if (!strcmp (config, "remove-unfound") || !strcmp (config, "r")) {
1149 /* This is default behavior set in header section */
1150 remove_unfound_elements = TRUE;
1151 return 0;
1153 if (!strcmp (config, "keep-unfound") || !strcmp (config, "k")) {
1154 remove_unfound_elements = FALSE;
1155 return 0;
1157 if (!strcmp (config, "quiet") || !strcmp (config, "q")) {
1158 quiet_mode = TRUE;
1159 return 0;
1161 if (!strcmp (config, "preserve") || !strcmp (config, "p")) {
1162 preserve = TRUE;
1163 return 0;
1165 if (!strcmp (config, "use-files") || !strcmp (config, "f")) {
1166 force_element_files = TRUE;
1167 return 0;
1169 if (!strcmp (config, "skip-m4") || !strcmp (config, "s")) {
1170 use_m4 = FALSE;
1171 return 0;
1173 if (!strcmp (config, "elements-dir") || !strcmp (config, "d")) {
1174 gchar *elements_dir = expand_dir (arg);
1175 if (verbose > 1)
1176 printf ("\tAdding directory to file element directory list: %s\n",
1177 elements_dir);
1178 element_directory_list =
1179 g_list_prepend (element_directory_list, elements_dir);
1180 } else if (!strcmp (config, "output-name") || !strcmp (config, "o"))
1181 sch_basename = g_strdup (arg);
1182 else if (!strcmp (config, "schematics"))
1183 add_multiple_schematics (arg);
1184 else if (!strcmp (config, "m4-pcbdir")) {
1185 g_free (m4_pcbdir);
1186 m4_pcbdir = g_strdup (arg);
1187 } else if (!strcmp (config, "m4-file"))
1188 add_m4_file (arg);
1189 else if (!strcmp (config, "gnetlist"))
1190 extra_gnetlist_list = g_list_append (extra_gnetlist_list, g_strdup (arg));
1191 else if (!strcmp (config, "empty-footprint"))
1192 empty_footprint_name = g_strdup (arg);
1193 else
1194 return -1;
1196 return 1;
1199 static void
1200 load_project (gchar * path)
1202 FILE *f;
1203 gchar *s, buf[1024], config[32], arg[768];
1205 f = fopen (path, "r");
1206 if (!f)
1207 return;
1208 if (verbose)
1209 printf ("Reading project file: %s\n", path);
1210 while (fgets (buf, sizeof (buf), f)) {
1211 for (s = buf; *s == ' ' || *s == '\t' || *s == '\n'; ++s);
1212 if (!*s || *s == '#' || *s == '/' || *s == ';')
1213 continue;
1214 arg[0] = '\0';
1215 sscanf (s, "%31s %767[^\n]", config, arg);
1216 parse_config (config, arg);
1218 fclose (f);
1221 static void
1222 load_extra_project_files (void)
1224 gchar *path;
1225 static gboolean done = FALSE;
1227 if (done)
1228 return;
1230 load_project ("/etc/gsch2pcb");
1231 load_project ("/usr/local/etc/gsch2pcb");
1233 path = g_build_filename ((gchar *) g_get_home_dir (), ".gEDA",
1234 "gsch2pcb", NULL);
1235 load_project (path);
1236 g_free (path);
1238 done = TRUE;
1241 static gchar *usage_string0 =
1242 "usage: gsch2pcb [options] {project | foo.sch [foo1.sch ...]}\n"
1243 "\n"
1244 "Generate a PCB layout file from a set of gschem schematics.\n"
1245 " gnetlist -g PCB is run to generate foo.net from the schematics.\n"
1246 "\n"
1247 " gnetlist -g gsch2pcb is run to get PCB m4 derived elements which\n"
1248 " match schematic footprints. For schematic footprints which don't match\n"
1249 " any PCB m4 layout elements, search a set of file element directories in\n"
1250 " an attempt to find matching PCB file elements.\n"
1251 " Output to foo.pcb if it doesn't exist. If there is a current foo.pcb,\n"
1252 " output only new elements to foo.new.pcb.\n"
1253 " If any elements with a non-empty element name in the current foo.pcb\n"
1254 " have no matching schematic component, then remove those elements from\n"
1255 " foo.pcb and rename foo.pcb to a foo.pcb.bak sequence.\n"
1256 "\n"
1257 " gnetlist -g pcbpins is run to get a PCB actions file which will rename all\n"
1258 " of the pins in a .pcb file to match pin names from the schematic.\n"
1259 "\n"
1260 " \"project\" is a file (not ending in .sch) containing a list of\n"
1261 " schematics to process and some options. A schematics line is like:\n"
1262 " schematics foo1.sch foo2.sch ...\n"
1263 " Options in a project file are like command line args without the \"-\":\n"
1264 " output-name myproject\n"
1265 "\n"
1266 "options (may be included in a project file):\n"
1267 " -d, --elements-dir D Search D for PCB file elements. These defaults\n"
1268 " are searched if they exist: ./packages,\n"
1269 " /usr/local/share/pcb/newlib, /usr/share/pcb/newlib,\n"
1270 " (old pcb) /usr/local/lib/pcb_lib, /usr/lib/pcb_lib,\n"
1271 " (old pcb) /usr/local/pcb_lib\n"
1272 " -o, --output-name N Use output file names N.net, N.pcb, and N.new.pcb\n"
1273 " instead of foo.net, ... where foo is the basename\n"
1274 " of the first command line .sch file.\n"
1275 " -f, --use-files Force using file elements over m4 PCB elements\n"
1276 " for new footprints even though m4 elements are\n"
1277 " searched for first and may have been found.\n"
1278 " -r, --remove-unfound Don't include references to unfound elements in\n"
1279 " the generated .pcb files. Use if you want PCB to\n"
1280 " be able to load the (incomplete) .pcb file.\n"
1281 " This is the default behavior.\n"
1282 " -k, --keep-unfound Keep include references to unfound elements in\n"
1283 " the generated .pcb files. Use if you want to hand\n"
1284 " edit or otherwise preprocess the generated .pcb file\n"
1285 " before running pcb.\n"
1286 " -p, --preserve Preserve elements in PCB files which are not found\n"
1287 " in the schematics. Note that elements with an empty\n"
1288 " element name (schematic refdes) are never deleted,\n"
1289 " so you really shouldn't need this option.\n"
1290 " -q, --quiet Don't tell the user what to do next after running gsch2pcb.\n"
1291 "\n"
1292 " -s, --skip-m4 Skip m4 when looking for footprints. The default is to use\n"
1293 " m4 (which is what previous versions did).\n"
1294 " --m4-file F.inc Use m4 file F.inc in addition to the default m4\n"
1295 " files ./pcb.inc and ~/.pcb/pcb.inc.\n"
1296 " --m4-pcbdir D Use D as the PCB m4 files install directory\n"
1297 " instead of the default:\n";
1299 static gchar *usage_string1 =
1300 " --gnetlist backend A convenience run of extra gnetlist -g commands.\n"
1301 " Example: gnetlist partslist3\n"
1302 " Creates: myproject.partslist3\n"
1303 " --empty-footprint name See the project.sample file.\n"
1304 "\n"
1305 "options (not recognized in a project file):\n"
1306 " --gnetlist-arg arg Allows additional arguments to be passed to gnetlist.\n"
1307 " --fix-elements If a schematic component footprint is not equal\n"
1308 " to its PCB element Description, update the\n"
1309 " Description instead of replacing the element.\n"
1310 " Do this the first time gsch2pcb is used with\n"
1311 " PCB files originally created with gschem2pcb.\n"
1312 " -v, --verbose Use -v -v for additional file element debugging.\n"
1313 " -V, --version\n\n"
1314 "environment variables:\n"
1315 " GNETLIST If set, this specifies the name of the gnetlist program\n"
1316 " to execute.\n"
1317 "\n"
1318 "Additional Resources:\n"
1319 "\n"
1320 " gnetlist user guide: http://wiki.geda-project.org/geda:gnetlist_ug\n"
1321 " gEDA homepage: http://www.geda-project.org\n"
1322 " PCB homepage: http://pcb.geda-project.org\n" "\n";
1324 static void
1325 usage ()
1327 puts (usage_string0);
1328 printf (" %s\n\n", default_m4_pcbdir);
1329 puts (usage_string1);
1330 exit (0);
1333 static void
1334 get_args (gint argc, gchar ** argv)
1336 gchar *opt, *arg, *s;
1337 gint i, r;
1339 for (i = 1; i < argc; ++i) {
1340 opt = argv[i];
1341 arg = argv[i + 1];
1342 if (*opt == '-') {
1343 ++opt;
1344 if (*opt == '-')
1345 ++opt;
1346 if (!strcmp (opt, "version") || !strcmp (opt, "V")) {
1347 printf ("gsch2pcb %s\n", GSC2PCB_VERSION);
1348 exit (0);
1349 } else if (!strcmp (opt, "verbose") || !strcmp (opt, "v")) {
1350 verbose += 1;
1351 continue;
1352 } else if (!strcmp (opt, "fix-elements")) {
1353 fix_elements = TRUE;
1354 continue;
1355 } else if (!strcmp (opt, "gnetlist-arg")) {
1356 extra_gnetlist_arg_list =
1357 g_list_append (extra_gnetlist_arg_list, g_strdup (arg));
1358 i++;
1359 continue;
1360 } else if (!strcmp (opt, "help") || !strcmp (opt, "h"))
1361 usage ();
1362 else if (i < argc
1363 && ((r = parse_config (opt, (i < argc - 1) ? arg : NULL))
1364 >= 0)
1366 i += r;
1367 continue;
1369 printf ("gsch2pcb: bad or incomplete arg: %s\n", argv[i]);
1370 usage ();
1371 } else {
1372 if (!g_str_has_suffix (argv[i], ".sch")) {
1373 load_extra_project_files ();
1374 load_project (argv[i]);
1375 } else
1376 add_schematic (argv[i]);
1381 gint
1382 main (gint argc, gchar ** argv)
1384 gchar *pcb_file_name,
1385 *pcb_new_file_name, *bak_file_name, *pins_file_name, *net_file_name, *tmp;
1386 gint i;
1387 gboolean initial_pcb = TRUE;
1388 gboolean created_pcb_file = TRUE;
1389 char *path, *p;
1390 const char *pcbdata_path;
1391 const char *configure_m4_pcbdir = PCBM4DIR; /* do not free it */
1393 pcbdata_path = g_getenv ("PCBDATA"); /* do not free return value */
1394 if (pcbdata_path != NULL) {
1395 /* If PCBDATA is set, use the value */
1396 m4_pcbdir = g_strconcat (pcbdata_path, "/m4", NULL);
1397 } else if (configure_m4_pcbdir != NULL) {
1398 /* Use the default value passed in from the configure script
1399 * instead of trying to hard code a value which is very
1400 * likely wrong
1402 m4_pcbdir = g_strdup (configure_m4_pcbdir);
1403 } else {
1404 /* Neither PCBDATA was set nor PCBM4DIR has been configured */
1405 /* Fall back to using the "m4" subdirectory in the current directory */
1406 m4_pcbdir = g_strdup ("./m4");
1409 default_m4_pcbdir = g_strdup (m4_pcbdir);
1411 if (argc < 2)
1412 usage ();
1414 get_args (argc, argv);
1416 load_extra_project_files ();
1417 add_default_m4_files ();
1419 if (!schematics)
1420 usage ();
1423 /* Defaults for the search path if not configured in the project file */
1424 if (g_file_test ("packages", G_FILE_TEST_IS_DIR))
1425 element_directory_list = g_list_append (element_directory_list, "packages");
1427 #define PCB_PATH_DELIMETER ":"
1428 if (verbose)
1429 printf ("Processing PCBLIBPATH=\"%s\"\n", PCBLIBPATH);
1431 path = g_strdup (PCBLIBPATH);
1432 for (p = strtok (path, PCB_PATH_DELIMETER); p && *p;
1433 p = strtok (NULL, PCB_PATH_DELIMETER)) {
1434 if (g_file_test (p, G_FILE_TEST_IS_DIR)) {
1435 if (verbose)
1436 printf ("Adding %s to the newlib search path\n", p);
1437 element_directory_list = g_list_append (element_directory_list,
1438 g_strdup (p));
1441 g_free (path);
1443 pins_file_name = g_strconcat (sch_basename, ".cmd", NULL);
1444 net_file_name = g_strconcat (sch_basename, ".net", NULL);
1445 pcb_file_name = g_strconcat (sch_basename, ".pcb", NULL);
1446 bak_file_name = g_strconcat (sch_basename, ".pcb.bak", NULL);
1447 tmp = g_strdup (bak_file_name);
1449 for (i = 0; g_file_test (bak_file_name, G_FILE_TEST_EXISTS); ++i) {
1450 g_free (bak_file_name);
1451 bak_file_name = g_strdup_printf ("%s%d", tmp, i);
1453 g_free (tmp);
1455 if (g_file_test (pcb_file_name, G_FILE_TEST_EXISTS)) {
1456 initial_pcb = FALSE;
1457 pcb_new_file_name = g_strconcat (sch_basename, ".new.pcb", NULL);
1458 get_pcb_element_list (pcb_file_name);
1459 } else
1460 pcb_new_file_name = g_strdup (pcb_file_name);
1462 if (!run_gnetlist (pins_file_name, net_file_name, pcb_new_file_name,
1463 sch_basename, schematics)) {
1464 fprintf(stderr, "Failed to run gnetlist\n");
1465 exit (1);
1468 if (add_elements (pcb_new_file_name) == 0) {
1469 build_and_run_command ("rm %s", pcb_new_file_name);
1470 if (initial_pcb) {
1471 printf ("No elements found, so nothing to do.\n");
1472 exit (0);
1476 if (fix_elements)
1477 update_element_descriptions (pcb_file_name, bak_file_name);
1478 prune_elements (pcb_file_name, bak_file_name);
1480 /* Report work done during processing */
1481 if (verbose)
1482 printf ("\n");
1483 printf ("\n----------------------------------\n");
1484 printf ("Done processing. Work performed:\n");
1485 if (n_deleted > 0 || n_fixed > 0 || need_PKG_purge || n_changed_value > 0)
1486 printf ("%s is backed up as %s.\n", pcb_file_name, bak_file_name);
1487 if (pcb_element_list && n_deleted > 0)
1488 printf ("%d elements deleted from %s.\n", n_deleted, pcb_file_name);
1490 if (n_added_ef + n_added_m4 > 0)
1491 printf ("%d file elements and %d m4 elements added to %s.\n",
1492 n_added_ef, n_added_m4, pcb_new_file_name);
1493 else if (n_not_found == 0) {
1494 printf ("No elements to add so not creating %s\n", pcb_new_file_name);
1495 created_pcb_file = FALSE;
1498 if (n_not_found > 0) {
1499 printf ("%d not found elements added to %s.\n",
1500 n_not_found, pcb_new_file_name);
1502 if (n_unknown > 0)
1503 printf ("%d components had no footprint attribute and are omitted.\n",
1504 n_unknown);
1505 if (n_none > 0)
1506 printf ("%d components with footprint \"none\" omitted from %s.\n",
1507 n_none, pcb_new_file_name);
1508 if (n_empty > 0)
1509 printf ("%d components with empty footprint \"%s\" omitted from %s.\n",
1510 n_empty, empty_footprint_name, pcb_new_file_name);
1511 if (n_changed_value > 0)
1512 printf ("%d elements had a value change in %s.\n",
1513 n_changed_value, pcb_file_name);
1514 if (n_fixed > 0)
1515 printf ("%d elements fixed in %s.\n", n_fixed, pcb_file_name);
1516 if (n_PKG_removed_old > 0) {
1517 printf ("%d elements could not be found.", n_PKG_removed_old);
1518 if (created_pcb_file)
1519 printf (" So %s is incomplete.\n", pcb_file_name);
1520 else
1521 printf ("\n");
1523 if (n_PKG_removed_new > 0) {
1524 printf ("%d elements could not be found.", n_PKG_removed_new);
1525 if (created_pcb_file)
1526 printf (" So %s is incomplete.\n", pcb_new_file_name);
1527 else
1528 printf ("\n");
1530 if (n_preserved > 0)
1531 printf ("%d elements not in the schematic preserved in %s.\n",
1532 n_preserved, pcb_file_name);
1534 /* Tell user what to do next */
1535 if (verbose)
1536 printf ("\n");
1538 if (n_added_ef + n_added_m4 > 0) {
1539 if (initial_pcb) {
1540 printf ("\nNext step:\n");
1541 printf ("1. Run pcb on your file %s.\n", pcb_file_name);
1542 printf
1543 (" You will find all your footprints in a bundle ready for you to place\n");
1544 printf
1545 (" or disperse with \"Select -> Disperse all elements\" in PCB.\n\n");
1546 printf
1547 ("2. From within PCB, select \"File -> Load netlist file\" and select \n");
1548 printf (" %s to load the netlist.\n\n", net_file_name);
1549 printf ("3. From within PCB, enter\n\n");
1550 printf (" :ExecuteFile(%s)\n\n", pins_file_name);
1551 printf
1552 (" to propagate the pin names of all footprints to the layout.\n\n");
1553 } else if (quiet_mode == FALSE) {
1554 printf ("\nNext steps:\n");
1555 printf ("1. Run pcb on your file %s.\n", pcb_file_name);
1556 printf
1557 ("2. From within PCB, select \"File -> Load layout data to paste buffer\"\n");
1558 printf
1559 (" and select %s to load the new footprints into your existing layout.\n",
1560 pcb_new_file_name);
1561 printf
1562 ("3. From within PCB, select \"File -> Load netlist file\" and select \n");
1563 printf (" %s to load the updated netlist.\n\n", net_file_name);
1564 printf ("4. From within PCB, enter\n\n");
1565 printf (" :ExecuteFile(%s)\n\n", pins_file_name);
1566 printf (" to update the pin names of all footprints.\n\n");
1570 g_free (net_file_name);
1571 g_free (pins_file_name);
1572 g_free (pcb_file_name);
1573 g_free (bak_file_name);
1575 return 0;