gsch2pcb: Make --m4-file and -m4-pcbdir arguments work again.
[geda-gaf/peter-b.git] / utils / src / gsch2pcb.c
blob9f8dc427ddc85c3efbd311f71a2cdb78760ebda0
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 #ifdef HAVE_LIBDMALLOC
35 #include <dmalloc.h>
36 #endif
38 #define GSC2PCB_VERSION "1.6"
40 #define DEFAULT_PCB_INC "pcb.inc"
42 #define SEP_STRING "--------\n"
44 typedef struct
46 gchar *refdes, *value, *description, *changed_description, *changed_value;
47 gchar *flags, *tail;
48 gint x, y;
49 gchar *pkg_name_fix;
50 gchar res_char;
52 gboolean still_exists, new_format, hi_res_format, quoted_flags, omit_PKG;
54 PcbElement;
57 typedef struct
59 gchar *part_number, *element_name;
61 ElementMap;
63 static GList *pcb_element_list,
64 *element_directory_list, *extra_gnetlist_list, *extra_gnetlist_arg_list;
66 static gchar *sch_basename;
68 static GList *schematics;
70 static gchar *m4_pcbdir, *default_m4_pcbdir, *m4_files, *m4_override_file;
72 static gboolean use_m4 = TRUE;
74 static gchar *empty_footprint_name;
76 static gint verbose,
77 n_deleted,
78 n_added_m4,
79 n_added_ef,
80 n_fixed,
81 n_PKG_removed_new,
82 n_PKG_removed_old,
83 n_preserved, n_changed_value, n_not_found, n_unknown, n_none, n_empty;
85 static gboolean remove_unfound_elements = TRUE,
86 quiet_mode = FALSE,
87 force_element_files, preserve, fix_elements, bak_done, need_PKG_purge;
90 static void
91 create_m4_override_file ()
93 FILE *f;
95 m4_override_file = "gnet-gsch2pcb-tmp.scm";
96 f = fopen (m4_override_file, "wb");
97 if (!f) {
98 m4_override_file = NULL;
99 return;
101 if (m4_pcbdir)
102 fprintf (f, "(define gsch2pcb:pcb-m4-dir \"%s\")\n", m4_pcbdir);
103 if (m4_files)
104 fprintf (f, "(define gsch2pcb:m4-files \"%s\")\n", m4_files);
105 fprintf (f, "(define gsch2pcb:use-m4 %s)\n", use_m4 == TRUE ? "#t" : "#f");
107 fclose (f);
108 if (verbose) {
109 printf ("Default m4-pcbdir: %s\n", default_m4_pcbdir);
110 printf ("--------\ngnet-gsch2pcb-tmp.scm override file:\n");
111 if (m4_pcbdir)
112 printf (" (define gsch2pcb:pcb-m4-dir \"%s\")\n", m4_pcbdir);
113 if (m4_files)
114 printf (" (define gsch2pcb:m4-files \"%s\")\n", m4_files);
115 printf (" (define gsch2pcb:use-m4 %s)\n", use_m4 == TRUE ? "#t" : "#f");
120 * Build and run a command. No redirection or error handling is
121 * done. Format string is split on whitespace. Specifiers %l and %s
122 * are replaced with contents of positional args. To be recognized,
123 * specifiers must be separated from other arguments in the format by
124 * whitespace.
125 * - %l expects a GList, contents used as separate arguments
126 * - %s expects a gchar*, contents used as a single argument
127 * @param[in] format used to specify command to be executed
128 * @param[in] ... positional parameters
130 static gboolean
131 build_and_run_command (const gchar *format, ...)
133 va_list vargs;
134 gchar ** split;
135 GList *tmp = NULL;
136 gint num_split;
137 gint i;
138 gint status;
139 gboolean result = FALSE;
140 gboolean spawn_result;
141 gchar *standard_output = NULL;
142 gchar *standard_error = NULL;
143 GError * error = NULL;
145 va_start (vargs, format);
146 split = g_strsplit_set (format, " \t\n\v", 0);
147 num_split = g_strv_length (split);
148 for (i = 0; i < num_split; ++i) {
149 gchar *chunk = split[i];
150 if (strcmp (chunk, "%l") == 0) {
151 /* append contents of list into command args - shared data */
152 tmp = g_list_concat (tmp, g_list_copy (va_arg (vargs, GList*)));
153 } else if (strcmp (chunk, "%s") == 0) {
154 /* insert contents of string into output */
155 tmp = g_list_append (tmp, va_arg (vargs, gchar*));
156 } else {
157 /* bare string, use as is */
158 tmp = g_list_append (tmp, chunk);
161 va_end (vargs);
163 if (tmp) {
164 /* we have something in the list, build & call command */
165 GList *p;
166 gint i = 0;
167 gchar ** args = g_new0 (gchar*, g_list_length (tmp) + 1/* NULL terminate the list */);
169 if (verbose)
170 printf ("Running command:\n\t");
172 for (p = tmp; p; p = g_list_next (p)) {
173 args[i++] = (gchar*) p->data;
174 if (verbose)
175 printf ("%s ", (char*)p->data);
178 if (verbose)
179 printf ("\n%s", SEP_STRING);
181 if (g_spawn_sync (".", /* Working directory */
182 args, /* argv */
183 NULL, /* envp */
184 G_SPAWN_SEARCH_PATH, /* flags */
185 NULL, /* child_setup */
186 NULL, /* user data */
187 &standard_output, /* standard output */
188 &standard_error, /* standard error */
189 &status, /* exit status return */
190 &error)) { /* GError return */
191 if (verbose)
192 fputs(standard_output, stdout);
193 if (status == 0)
194 result = TRUE;
195 else {
196 if (standard_error)
197 fputs(standard_error, stderr);
200 else {
201 fprintf(stderr, "Failed to execute external program: %s\n", error->message);
202 g_error_free(error);
205 if (verbose)
206 printf ("\n%s", SEP_STRING);
208 g_free(standard_error);
209 g_free (standard_output);
211 g_free (args);
212 /* free the list, but leave data untouched */
213 g_list_free (tmp);
216 g_strfreev (split);
218 return result;
221 /* Run gnetlist to generate a netlist and a PCB board file. gnetlist
222 * has exit status of 0 even if it's given an invalid arg, so do some
223 * stat() hoops to decide if gnetlist successfully generated the PCB
224 * board file (only gnetlist >= 20030901 recognizes -m).
226 static gboolean
227 run_gnetlist (gchar * pins_file, gchar * net_file, gchar * pcb_file,
228 gchar * basename, GList * largs)
230 struct stat st;
231 time_t mtime;
232 static const gchar *gnetlist = NULL;
233 GList *list = NULL;
234 GList *verboseList = NULL;
235 GList *args1 = NULL;
237 /* Allow the user to specify a full path or a different name for
238 * the gnetlist command. Especially useful if multiple copies
239 * are installed at once.
241 if (gnetlist == NULL)
242 gnetlist = g_getenv ("GNETLIST");
243 if (gnetlist == NULL)
244 gnetlist = "gnetlist";
246 if (!verbose)
247 verboseList = g_list_append (verboseList, "-q");
249 if (!build_and_run_command ("%s %l -g pcbpins -o %s %l %l",
250 gnetlist,
251 verboseList,
252 pins_file,
253 extra_gnetlist_arg_list,
254 largs))
255 return FALSE;
257 if (!build_and_run_command ("%s %l -g PCB -o %s %l %l",
258 gnetlist,
259 verboseList,
260 net_file,
261 extra_gnetlist_arg_list,
262 largs))
263 return FALSE;
264 create_m4_override_file ();
266 if (m4_override_file) {
267 args1 = g_list_append (args1, "-m");
268 args1 = g_list_append (args1, m4_override_file);
271 mtime = (stat (pcb_file, &st) == 0) ? st.st_mtime : 0;
273 if (!build_and_run_command ("%s %l -g gsch2pcb -o %s %l %l %l",
274 gnetlist,
275 verboseList,
276 pcb_file,
277 args1,
278 extra_gnetlist_arg_list,
279 largs)) {
280 if (stat (pcb_file, &st) != 0 || mtime == st.st_mtime) {
281 fprintf (stderr,
282 "gsch2pcb: gnetlist command failed, `%s' not updated\n",
283 pcb_file
285 if (m4_override_file)
286 fprintf (stderr,
287 " At least gnetlist 20030901 is required for m4-xxx options.\n");
288 return FALSE;
290 return FALSE;
293 if (m4_override_file)
294 unlink (m4_override_file);
296 for (list = extra_gnetlist_list; list; list = g_list_next (list)) {
297 const gchar *s = (gchar *) list->data;
298 const gchar *s2 = strstr (s, " -o ");
299 gchar *out_file;
300 gchar *backend;
301 if (!s2) {
302 out_file = g_strconcat (basename, ".", s, NULL);
303 backend = g_strdup (s);
304 } else {
305 out_file = g_strdup (s2 + 4);
306 backend = g_strndup (s, s2 - s);
309 if (!build_and_run_command ("%s %l -g %s -o %s %l %l",
310 gnetlist,
311 verboseList,
312 backend,
313 out_file,
314 extra_gnetlist_arg_list,
315 largs))
316 return FALSE;
317 g_free (out_file);
318 g_free (backend);
321 g_list_free (args1);
322 g_list_free (verboseList);
324 return TRUE;
327 static gchar *
328 token (gchar * string, gchar ** next, gboolean * quoted_ret)
330 static gchar *str;
331 gchar *s, *ret;
332 gboolean quoted = FALSE;
334 if (string)
335 str = string;
336 if (!str || !*str) {
337 if (next)
338 *next = str;
339 return g_strdup ("");
341 while (*str == ' ' || *str == '\t' || *str == ',' || *str == '\n')
342 ++str;
343 if (*str == '"') {
344 quoted = TRUE;
345 if (quoted_ret)
346 *quoted_ret = TRUE;
347 ++str;
348 for (s = str; *s && *s != '"' && *s != '\n'; ++s);
349 } else {
350 if (quoted_ret)
351 *quoted_ret = FALSE;
352 for (s = str;
353 *s && (*s != ' ' && *s != '\t' && *s != ',' && *s != '\n'); ++s);
355 ret = g_strndup (str, s - str);
356 str = (quoted && *s) ? s + 1 : s;
357 if (next)
358 *next = str;
359 return ret;
362 static gchar *
363 fix_spaces (gchar * str)
365 gchar *s;
367 if (!str)
368 return NULL;
369 for (s = str; *s; ++s)
370 if (*s == ' ' || *s == '\t')
371 *s = '_';
372 return str;
375 /* As of 1/9/2004 CVS hi_res Element[] line format:
376 * Element[element_flags, description, pcb-name, value, mark_x, mark_y,
377 * text_x, text_y, text_direction, text_scale, text_flags]
378 * New PCB 1.7 / 1.99 Element() line format:
379 * Element(element_flags, description, pcb-name, value, mark_x, mark_y,
380 * text_x, text_y, text_direction, text_scale, text_flags)
381 * Old PCB 1.6 Element() line format:
382 * Element(element_flags, description, pcb-name, value,
383 * text_x, text_y, text_direction, text_scale, text_flags)
385 * (mark_x, mark_y) is the element position (mark) and (text_x,text_y)
386 * is the description text position which is absolute in pre 1.7 and
387 * is now relative. The hi_res mark_x,mark_y and text_x,text_y resolutions
388 * are 100x the other formats.
390 PcbElement *
391 pcb_element_line_parse (gchar * line)
393 PcbElement *el = NULL;
394 gchar *s, *t, close_char;
395 gint state = 0, elcount = 0;
397 if (strncmp (line, "Element", 7))
398 return NULL;
400 el = g_new0 (PcbElement, 1);
402 s = line + 7;
403 while (*s == ' ' || *s == '\t')
404 ++s;
406 if (*s == '[')
407 el->hi_res_format = TRUE;
408 else if (*s != '(') {
409 g_free (el);
410 return NULL;
413 el->res_char = el->hi_res_format ? '[' : '(';
414 close_char = el->hi_res_format ? ']' : ')';
416 el->flags = token (s + 1, NULL, &el->quoted_flags);
417 el->description = token (NULL, NULL, NULL);
418 el->refdes = token (NULL, NULL, NULL);
419 el->value = token (NULL, NULL, NULL);
421 s = token (NULL, NULL, NULL);
422 el->x = atoi (s);
423 g_free (s);
425 s = token (NULL, &t, NULL);
426 el->y = atoi (s);
427 g_free (s);
429 el->tail = g_strdup (t ? t : "");
430 if ((s = strrchr (el->tail, (gint) '\n')) != NULL)
431 *s = '\0';
433 /* Count the tokens in tail to decide if it's new or old format.
434 * Old format will have 3 tokens, new format will have 5 tokens.
436 for (s = el->tail; *s && *s != close_char; ++s) {
437 if (*s != ' ') {
438 if (state == 0)
439 ++elcount;
440 state = 1;
441 } else
442 state = 0;
444 if (elcount > 4)
445 el->new_format = TRUE;
447 fix_spaces (el->description);
448 fix_spaces (el->refdes);
449 fix_spaces (el->value);
451 /* Don't allow elements with no refdes to ever be deleted because
452 * they may be desired pc board elements not in schematics. So
453 * initialize still_exists to TRUE if empty or non-alphanumeric
454 * refdes.
456 if (!*el->refdes || !isalnum ((gint) (*el->refdes)))
457 el->still_exists = TRUE;
459 return el;
462 static void
463 pcb_element_free (PcbElement * el)
465 if (!el)
466 return;
467 g_free (el->flags);
468 g_free (el->description);
469 g_free (el->changed_description);
470 g_free (el->changed_value);
471 g_free (el->refdes);
472 g_free (el->value);
473 g_free (el->tail);
474 g_free (el->pkg_name_fix);
475 g_free (el);
478 static void
479 get_pcb_element_list (gchar * pcb_file)
481 FILE *f;
482 PcbElement *el;
483 gchar *s, buf[1024];
485 if ((f = fopen (pcb_file, "r")) == NULL)
486 return;
487 while ((fgets (buf, sizeof (buf), f)) != NULL) {
488 for (s = buf; *s == ' ' || *s == '\t'; ++s);
489 if (!strncmp (s, "PKG_", 4)) {
490 need_PKG_purge = TRUE;
491 continue;
493 if ((el = pcb_element_line_parse (s)) == NULL)
494 continue;
495 pcb_element_list = g_list_append (pcb_element_list, el);
497 fclose (f);
500 static PcbElement *
501 pcb_element_exists (PcbElement * el_test, gboolean record)
503 GList *list;
504 PcbElement *el;
506 for (list = pcb_element_list; list; list = g_list_next (list)) {
507 el = (PcbElement *) list->data;
509 if (strcmp (el_test->refdes, el->refdes))
510 continue;
511 if (strcmp (el_test->description, el->description)) { /* footprint */
512 if (record)
513 el->changed_description = g_strdup (el_test->description);
514 } else {
515 if (record) {
516 if (strcmp (el_test->value, el->value))
517 el->changed_value = g_strdup (el_test->value);
518 el->still_exists = TRUE;
520 return el;
523 return NULL;
526 /* A problem is that new PCB 1.7 file elements have the
527 * (mark_x,mark_y) value set to wherever the element was created and
528 * no equivalent of a gschem translate symbol was done.
530 * So, file elements inserted can be scattered over a big area and
531 * this is bad when loading a file.new.pcb into an existing PC
532 * board. So, do a simple translate if (mark_x,mark_y) is
533 * (arbitrarily) over 1000. I'll assume that for values < 1000 the
534 * element creator was concerned with a sane initial element
535 * placement. Unless someone has a better idea? Don't bother with
536 * pre PCB 1.7 formats as that would require parsing the mark().
537 * Current m4 elements use the old format but they seem to have a
538 * reasonable initial mark().
540 static void
541 simple_translate (PcbElement * el)
543 gint factor;
545 if (el->new_format) {
546 factor = el->hi_res_format ? 100 : 1;
547 if (el->x > 1000 * factor)
548 el->x = 500 * factor;
549 if (el->y > 1000 * factor)
550 el->y = 500 * factor;
554 static gboolean
555 insert_element (FILE * f_out, gchar * element_file,
556 gchar * footprint, gchar * refdes, gchar * value)
558 FILE *f_in;
559 PcbElement *el;
560 gchar *fmt, *s, buf[1024];
561 gboolean retval = FALSE;
563 if ((f_in = fopen (element_file, "r")) == NULL) {
564 s = g_strdup_printf ("insert_element() can't open %s", element_file);
565 perror (s);
566 g_free (s);
567 return FALSE;
569 /* Scan the file to detect whether it's actually a PCB
570 * layout. Assumes that a PCB layout will have a "PCB" line. */
571 while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
572 for (s = buf; *s == ' ' || *s == '\t'; ++s);
573 s[3] = 0; /* Truncate line */
574 if (strncmp ("PCB", s, sizeof (buf)) == 0) {
575 printf ("Warning: %s appears to be a PCB layout file. Skipping.\n",
576 element_file);
577 fclose (f_in);
578 return FALSE;
581 rewind (f_in);
583 /* Copy the file element lines. Substitute new parameters into the
584 * Element() or Element[] line and strip comments.
586 while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
587 for (s = buf; *s == ' ' || *s == '\t'; ++s);
588 if ((el = pcb_element_line_parse (s)) != NULL) {
589 simple_translate (el);
590 fmt = el->quoted_flags ?
591 "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %d %d%s\n" :
592 "Element%c%s \"%s\" \"%s\" \"%s\" %d %d%s\n";
594 fprintf (f_out, fmt,
595 el->res_char, el->flags, footprint, refdes, value,
596 el->x, el->y, el->tail);
597 retval = TRUE;;
598 } else if (*s != '#')
599 fputs (buf, f_out);
600 pcb_element_free (el);
602 fclose (f_in);
603 return retval;
607 gchar *
608 find_element (gchar * dir_path, gchar * element)
610 GDir *dir;
611 gchar *path, *name, *s, *found = NULL;
613 if ((dir = g_dir_open (dir_path, 0, NULL)) == NULL) {
614 s = g_strdup_printf ("find_element can't open dir \"%s\"", dir_path);
615 perror (s);
616 g_free (s);
617 return NULL;
619 if (verbose > 1)
620 printf ("\t Searching: \"%s\" for \"%s\"\n", dir_path, element);
621 while ((name = (gchar *) g_dir_read_name (dir)) != NULL) {
622 path = g_strconcat (dir_path, "/", name, NULL);
623 found = NULL;
625 /* if we got a directory name, then recurse down into it */
626 if (g_file_test (path, G_FILE_TEST_IS_DIR))
627 found = find_element (path, element);
629 /* otherwise assume it is a file and see if it is the one we want */
630 else {
631 if (verbose > 1)
632 printf ("\t : %s\t", name);
633 if (!strcmp (name, element))
634 found = g_strdup (path);
635 else {
636 gchar *tmps;
637 tmps = g_strconcat (element, ".fp", NULL);
638 if (!strcmp (name, tmps))
639 found = g_strdup (path);
640 g_free (tmps);
642 if (verbose > 1)
643 printf ("%s\n", found ? "Yes" : "No");
645 g_free (path);
646 if (found)
647 break;
649 g_dir_close (dir);
650 return found;
653 gchar *
654 search_element_directories (PcbElement * el)
656 GList *list;
657 gchar *s, *elname = NULL, *dir_path, *path = NULL;
658 gint n1, n2;
660 /* See comment before pkg_to_element() */
661 if (el->pkg_name_fix) {
662 if (strchr (el->description, '-')) {
663 n1 = strlen (el->description);
664 n2 = strlen (el->pkg_name_fix);
665 s = el->description + n1 - n2 - 1;
667 // printf("n1=%d n2=%d desc:%s fix:%s s:%s\n",
668 // n1, n2, el->description, el->pkg_name_fix, s);
670 if (n1 > 0 && n2 < n1 && *s == '-' && *(s + 1) == *el->pkg_name_fix) {
671 s = g_strndup (el->description, n1 - n2 - 1);
672 elname = g_strconcat (s, " ", el->pkg_name_fix, NULL);
673 g_free (s);
676 if (!elname) {
677 printf ("Warning: argument passing may have been confused by\n");
678 printf (" a comma in a component value:\n");
679 printf (" Check %s %s %s\n",
680 el->refdes, el->description, el->value);
681 printf (" Maybe just use a space instead of a comma?\n");
684 if (!elname)
685 elname = g_strdup (el->description);
687 if (!strcmp (elname, "unknown")) {
688 g_free (elname);
689 return NULL;
691 if (verbose > 1)
692 printf ("\tSearching directories looking for file element: %s\n", elname);
693 for (list = element_directory_list; list; list = g_list_next (list)) {
694 dir_path = (gchar *) list->data;
695 if (verbose > 1)
696 printf ("\tLooking in directory: \"%s\"\n", dir_path);
697 path = find_element (dir_path, elname);
698 if (path) {
699 if (verbose)
700 printf ("\tFound: %s\n", path);
701 break;
704 g_free (elname);
705 return path;
708 /* The gnetlist backend gnet-gsch2pcb.scm generates PKG_ lines:
710 * PKG_footprint(footprint{-fp0-fp1},refdes,value{,fp0,fp1})
712 * where fp1 and fp2 (if they exist) are the extra footprint
713 * components when specifying footprints like "DIL 14 300". This is
714 * needed for m4 macros.
716 * A complication is if the footprint references a file element with
717 * spaces embedded in the name. The gnetlist backend will interpret
718 * these as fp0, fp1, ... args and the footprint will in this case
719 * incorrectly have '-' inserted where the spaces should be. So, if
720 * there are additional args, reconstruct the portion of the name
721 * given by the args with spaces for later use. Eg. if the footprint
722 * is "100 Pin jack", we will have
724 * PKG_100-Pin-jack(100-Pin-jack,refdes,value,Pin,jack)
726 * So put "Pin jack" into pkg_name_fix so if this element is searched
727 * as a file element we can munge the description to what it should
728 * be, eg:
730 * 100-Pin-jack -> 100 Pin jack
732 static PcbElement *
733 pkg_to_element (FILE * f, gchar * pkg_line)
735 PcbElement *el;
736 gchar **args, *s;
737 gint n, n_extra_args, n_dashes;
739 if (strncmp (pkg_line, "PKG_", 4)
740 || (s = strchr (pkg_line, (gint) '(')) == NULL)
741 return NULL;
743 args = g_strsplit (s + 1, ",", 12);
744 if (!args[0] || !args[1] || !args[2]) {
745 fprintf (stderr, "Bad package line: %s\n", pkg_line);
746 return NULL;
748 fix_spaces (args[0]);
749 fix_spaces (args[1]);
750 fix_spaces (args[2]);
752 el = g_new0 (PcbElement, 1);
753 el->description = g_strdup (args[0]);
754 el->refdes = g_strdup (args[1]);
755 el->value = g_strdup (args[2]);
756 if ((s = strchr (el->value, (gint) ')')) != NULL)
757 *s = '\0';
759 /* If the component value has a comma, eg "1k, 1%", the gnetlist generated
760 * PKG line will be
762 * PKG_XXX(`R0w8',`R100',`1k, 1%'),
764 * but after processed by m4, the input to gsch2pcb will be
766 * PKG_XXX(R0w8,R100,1k, 1%).
768 * So the quoting info has been lost when processing for file
769 * elements. So here try to detect and fix this. But I can't
770 * handle the situation where the description has a '-' and the
771 * value has a comma because gnet-gsch2pcb.scm munges the
772 * description with '-' when there are extra args.
774 for (n_extra_args = 0; args[3 + n_extra_args] != NULL; ++n_extra_args);
775 s = el->description;
776 for (n_dashes = 0; (s = strchr (s + 1, '-')) != NULL; ++n_dashes);
778 n = 3;
779 if (n_extra_args == n_dashes + 1) { /* Assume there was a comma in the value, eg "1K, 1%" */
780 s = el->value;
781 el->value = g_strconcat (s, ",", fix_spaces (args[n]), NULL);
782 g_free (s);
783 if ((s = strchr (el->value, (gint) ')')) != NULL)
784 *s = '\0';
785 n = 4;
787 if (args[n]) {
788 el->pkg_name_fix = g_strdup (args[n]);
789 for (n += 1; args[n] != NULL; ++n) {
790 s = el->pkg_name_fix;
791 el->pkg_name_fix = g_strconcat (s, " ", args[n], NULL);
792 g_free (s);
794 if ((s = strchr (el->pkg_name_fix, (gint) ')')) != NULL)
795 *s = '\0';
797 g_strfreev (args);
799 if (empty_footprint_name && !strcmp (el->description, empty_footprint_name)) {
800 if (verbose)
801 printf
802 ("%s: has the empty footprint attribute \"%s\" so won't be in the layout.\n",
803 el->refdes, el->description);
804 n_empty += 1;
805 el->omit_PKG = TRUE;
806 } else if (!strcmp (el->description, "none")) {
807 fprintf (stderr,
808 "WARNING: %s has a footprint attribute \"%s\" so won't be in the layout.\n",
809 el->refdes, el->description);
810 n_none += 1;
811 el->omit_PKG = TRUE;
812 } else if (!strcmp (el->description, "unknown")) {
813 fprintf (stderr,
814 "WARNING: %s has no footprint attribute so won't be in the layout.\n",
815 el->refdes);
816 n_unknown += 1;
817 el->omit_PKG = TRUE;
819 return el;
822 /* Process the newly created pcb file which is the output from
823 * gnetlist -g gsch2pcb ...
825 * It will have elements found via the m4 interface and PKG_ lines for
826 * elements not found. Insert pcb file elements for PKG_ lines if
827 * file elements can be found. If there was an existing pcb file,
828 * strip out any elements if they are already present so that the new
829 * pcb file will only have new elements.
831 static gint
832 add_elements (gchar * pcb_file)
834 FILE *f_in, *f_out;
835 PcbElement *el = NULL;
836 gchar *command, *p, *tmp_file, *s, buf[1024];
837 gint total, paren_level = 0;
838 gboolean is_m4, skipping = FALSE;
840 if ((f_in = fopen (pcb_file, "r")) == NULL)
841 return 0;
842 tmp_file = g_strconcat (pcb_file, ".tmp", NULL);
843 if ((f_out = fopen (tmp_file, "wb")) == NULL) {
844 fclose (f_in);
845 g_free (tmp_file);
846 return 0;
848 while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
849 for (s = buf; *s == ' ' || *s == '\t'; ++s);
850 if (skipping) {
851 if (*s == '(')
852 ++paren_level;
853 else if (*s == ')' && --paren_level <= 0)
854 skipping = FALSE;
855 continue;
857 is_m4 = FALSE;
858 if ((el = pcb_element_line_parse (s)) != NULL)
859 is_m4 = TRUE;
860 else
861 el = pkg_to_element (f_out, s);
862 if (el && pcb_element_exists (el, TRUE)) {
863 skipping = is_m4;
864 pcb_element_free (el);
865 continue;
867 if (!el || el->omit_PKG) {
868 if (el) {
870 } else
871 fputs (buf, f_out);
872 continue;
874 if (!is_m4 || (is_m4 && force_element_files)) {
875 if (verbose && !is_m4)
876 printf ("%s: need new file element for footprint %s (value=%s)\n",
877 el->refdes, el->description, el->value);
878 if (verbose && is_m4 && force_element_files)
879 printf
880 ("%s: have m4 element %s, but trying to replace with a file element.\n",
881 el->refdes, el->description);
882 p = search_element_directories (el);
883 if (!p && verbose && is_m4 && force_element_files)
884 printf ("\tNo file element found.\n");
886 if (p && insert_element (f_out, p,
887 el->description, el->refdes, el->value)) {
888 skipping = is_m4;
889 is_m4 = FALSE;
890 ++n_added_ef;
891 if (verbose)
892 printf ("%s: added new file element for footprint %s (value=%s)\n",
893 el->refdes, el->description, el->value);
894 } else if (!is_m4) {
895 fprintf (stderr,
896 "%s: can't find PCB element for footprint %s (value=%s)\n",
897 el->refdes, el->description, el->value);
898 if (remove_unfound_elements && !fix_elements) {
899 fprintf (stderr,
900 "So device %s will not be in the layout.\n", el->refdes);
901 ++n_PKG_removed_new;
902 } else {
903 ++n_not_found;
904 fputs (buf, f_out); /* Copy PKG_ line */
907 g_free (p);
909 if (is_m4) {
910 fputs (buf, f_out);
911 ++n_added_m4;
912 if (verbose)
913 printf ("%s: added new m4 element for footprint %s (value=%s)\n",
914 el->refdes, el->description, el->value);
916 pcb_element_free (el);
917 if (verbose)
918 printf ("----\n");
920 fclose (f_in);
921 fclose (f_out);
923 total = n_added_ef + n_added_m4 + n_not_found;
924 if (total == 0)
925 build_and_run_command ("rm %s", tmp_file);
926 else
927 build_and_run_command ("mv %s %s", tmp_file, pcb_file);
928 g_free (tmp_file);
929 return total;
932 static void
933 update_element_descriptions (gchar * pcb_file, gchar * bak)
935 FILE *f_in, *f_out;
936 GList *list;
937 PcbElement *el, *el_exists;
938 gchar *fmt, *command, *tmp, *s, buf[1024];
940 for (list = pcb_element_list; list; list = g_list_next (list)) {
941 el = (PcbElement *) list->data;
942 if (el->changed_description)
943 ++n_fixed;
945 if (!pcb_element_list || n_fixed == 0) {
946 fprintf (stderr, "Could not find any elements to fix.\n");
947 return;
949 if ((f_in = fopen (pcb_file, "r")) == NULL)
950 return;
951 tmp = g_strconcat (pcb_file, ".tmp", NULL);
952 if ((f_out = fopen (tmp, "wb")) == NULL) {
953 fclose (f_in);
954 return;
956 while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
957 for (s = buf; *s == ' ' || *s == '\t'; ++s);
958 if ((el = pcb_element_line_parse (s)) != NULL
959 && (el_exists = pcb_element_exists (el, FALSE)) != NULL
960 && el_exists->changed_description) {
961 fmt = el->quoted_flags ?
962 "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %d %d%s\n" :
963 "Element%c%s \"%s\" \"%s\" \"%s\" %d %d%s\n";
964 fprintf (f_out, fmt,
965 el->res_char,
966 el->flags, el_exists->changed_description,
967 el->refdes, el->value, el->x, el->y, el->tail);
968 printf ("%s: updating element Description: %s -> %s\n",
969 el->refdes, el->description, el_exists->changed_description);
970 el_exists->still_exists = TRUE;
971 } else
972 fputs (buf, f_out);
973 pcb_element_free (el);
975 fclose (f_in);
976 fclose (f_out);
978 if (!bak_done) {
979 build_and_run_command ("mv %s %s", pcb_file, bak);
980 bak_done = TRUE;
983 build_and_run_command ("mv %s %s", tmp, pcb_file);
984 g_free (tmp);
987 static void
988 prune_elements (gchar * pcb_file, gchar * bak)
990 FILE *f_in, *f_out;
991 GList *list;
992 PcbElement *el, *el_exists;
993 gchar *fmt, *command, *tmp, *s, buf[1024];
994 gint paren_level = 0;
995 gboolean skipping = FALSE;
997 for (list = pcb_element_list; list; list = g_list_next (list)) {
998 el = (PcbElement *) list->data;
999 if (!el->still_exists) {
1000 if (preserve) {
1001 ++n_preserved;
1002 fprintf (stderr,
1003 "Preserving PCB element not in the schematic: %s (element %s)\n",
1004 el->refdes, el->description);
1005 } else
1006 ++n_deleted;
1007 } else if (el->changed_value)
1008 ++n_changed_value;
1010 if (!pcb_element_list
1011 || (n_deleted == 0 && !need_PKG_purge && n_changed_value == 0)
1013 return;
1014 if ((f_in = fopen (pcb_file, "r")) == NULL)
1015 return;
1016 tmp = g_strconcat (pcb_file, ".tmp", NULL);
1017 if ((f_out = fopen (tmp, "wb")) == NULL) {
1018 fclose (f_in);
1019 return;
1021 while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
1022 for (s = buf; *s == ' ' || *s == '\t'; ++s);
1023 if (skipping) {
1024 if (*s == '(')
1025 ++paren_level;
1026 else if (*s == ')' && --paren_level <= 0)
1027 skipping = FALSE;
1028 continue;
1030 el_exists = NULL;
1031 if ((el = pcb_element_line_parse (s)) != NULL
1032 && (el_exists = pcb_element_exists (el, FALSE)) != NULL
1033 && !el_exists->still_exists && !preserve) {
1034 skipping = TRUE;
1035 if (verbose)
1036 printf ("%s: deleted element %s (value=%s)\n",
1037 el->refdes, el->description, el->value);
1038 pcb_element_free (el);
1039 continue;
1041 if (el_exists && el_exists->changed_value) {
1042 fmt = el->quoted_flags ?
1043 "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %d %d%s\n" :
1044 "Element%c%s \"%s\" \"%s\" \"%s\" %d %d%s\n";
1045 fprintf (f_out, fmt,
1046 el->res_char, el->flags, el->description, el->refdes,
1047 el_exists->changed_value, el->x, el->y, el->tail);
1048 if (verbose)
1049 printf ("%s: changed element %s value: %s -> %s\n",
1050 el->refdes, el->description,
1051 el->value, el_exists->changed_value);
1052 } else if (!strncmp (s, "PKG_", 4))
1053 ++n_PKG_removed_old;
1054 else
1055 fputs (buf, f_out);
1056 pcb_element_free (el);
1058 fclose (f_in);
1059 fclose (f_out);
1061 if (!bak_done) {
1062 build_and_run_command ("mv %s %s", pcb_file, bak);
1063 bak_done = TRUE;
1066 build_and_run_command ("mv %s %s", tmp, pcb_file);
1067 g_free (tmp);
1070 static void
1071 add_m4_file (gchar * arg)
1073 gchar *s;
1075 if (!m4_files)
1076 m4_files = g_strdup (arg);
1077 else {
1078 s = m4_files;
1079 m4_files = g_strconcat (m4_files, " ", arg, NULL);
1080 g_free (s);
1084 static gchar *
1085 expand_dir (gchar * dir)
1087 gchar *s;
1089 if (*dir == '~')
1090 s = g_build_filename ((gchar *) g_get_home_dir (), dir + 1, NULL);
1091 else
1092 s = g_strdup (dir);
1093 return s;
1096 static void
1097 add_default_m4_files (void)
1099 gchar *path;
1101 path = g_build_filename ((gchar *) g_get_home_dir (),
1102 ".pcb", DEFAULT_PCB_INC, NULL);
1103 if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
1104 add_m4_file (path);
1105 g_free (path);
1107 if (g_file_test (DEFAULT_PCB_INC, G_FILE_TEST_IS_REGULAR))
1108 add_m4_file (DEFAULT_PCB_INC);
1112 static void
1113 add_schematic (gchar * sch)
1115 const gchar* s;
1116 schematics = g_list_append (schematics, g_strdup (sch));
1117 if (!sch_basename && (s = g_strrstr (sch, ".sch")) != NULL && strlen(s) == 4)
1118 sch_basename = g_strndup (sch, s - sch);
1121 static void
1122 add_multiple_schematics (gchar * sch)
1124 /* parse the string using shell semantics */
1125 gint count;
1126 gchar** args = NULL;
1127 GError* error = NULL;
1129 if (g_shell_parse_argv (sch, &count, &args, &error)) {
1130 int i;
1131 for (i = 0; i < count; ++i)
1133 add_schematic (args[i]);
1135 g_strfreev (args);
1136 } else {
1137 fprintf (stderr,
1138 "invalid `schematics' option: %s\n",
1139 error->message);
1140 g_error_free (error);
1144 static gint
1145 parse_config (gchar * config, gchar * arg)
1147 gchar *s;
1149 /* remove trailing white space otherwise strange things can happen */
1150 if ((arg != NULL) && (strlen (arg) >= 1)) {
1151 s = arg + strlen (arg) - 1;
1152 while ((*s == ' ' || *s == '\t') && (s != arg))
1153 s--;
1154 s++;
1155 *s = '\0';
1157 if (verbose)
1158 printf (" %s \"%s\"\n", config, arg ? arg : "");
1160 if (!strcmp (config, "remove-unfound") || !strcmp (config, "r")) {
1161 /* This is default behavior set in header section */
1162 remove_unfound_elements = TRUE;
1163 return 0;
1165 if (!strcmp (config, "keep-unfound") || !strcmp (config, "k")) {
1166 remove_unfound_elements = FALSE;
1167 return 0;
1169 if (!strcmp (config, "quiet") || !strcmp (config, "q")) {
1170 quiet_mode = TRUE;
1171 return 0;
1173 if (!strcmp (config, "preserve") || !strcmp (config, "p")) {
1174 preserve = TRUE;
1175 return 0;
1177 if (!strcmp (config, "use-files") || !strcmp (config, "f")) {
1178 force_element_files = TRUE;
1179 return 0;
1181 if (!strcmp (config, "skip-m4") || !strcmp (config, "s")) {
1182 use_m4 = FALSE;
1183 return 0;
1185 if (!strcmp (config, "elements-dir") || !strcmp (config, "d")) {
1186 if (verbose > 1)
1187 printf ("\tAdding directory to file element directory list: %s\n",
1188 expand_dir (arg));
1189 element_directory_list =
1190 g_list_prepend (element_directory_list, expand_dir (arg));
1191 } else if (!strcmp (config, "output-name") || !strcmp (config, "o"))
1192 sch_basename = g_strdup (arg);
1193 else if (!strcmp (config, "schematics"))
1194 add_multiple_schematics (arg);
1195 else if (!strcmp (config, "m4-pcbdir")) {
1196 g_free (m4_pcbdir);
1197 m4_pcbdir = g_strdup (arg);
1198 } else if (!strcmp (config, "m4-file"))
1199 add_m4_file (arg);
1200 else if (!strcmp (config, "gnetlist"))
1201 extra_gnetlist_list = g_list_append (extra_gnetlist_list, g_strdup (arg));
1202 else if (!strcmp (config, "empty-footprint"))
1203 empty_footprint_name = g_strdup (arg);
1204 else
1205 return -1;
1207 return 1;
1210 static void
1211 load_project (gchar * path)
1213 FILE *f;
1214 gchar *s, buf[1024], config[32], arg[768];
1216 f = fopen (path, "r");
1217 if (!f)
1218 return;
1219 if (verbose)
1220 printf ("Reading project file: %s\n", path);
1221 while (fgets (buf, sizeof (buf), f)) {
1222 for (s = buf; *s == ' ' || *s == '\t' || *s == '\n'; ++s);
1223 if (!*s || *s == '#' || *s == '/' || *s == ';')
1224 continue;
1225 arg[0] = '\0';
1226 sscanf (s, "%31s %767[^\n]", config, arg);
1227 parse_config (config, arg);
1229 fclose (f);
1232 static void
1233 load_extra_project_files (void)
1235 gchar *path;
1236 static gboolean done = FALSE;
1238 if (done)
1239 return;
1241 load_project ("/etc/gsch2pcb");
1242 load_project ("/usr/local/etc/gsch2pcb");
1244 path = g_build_filename ((gchar *) g_get_home_dir (), ".gEDA",
1245 "gsch2pcb", NULL);
1246 load_project (path);
1247 g_free (path);
1249 done = TRUE;
1252 static gchar *usage_string0 =
1253 "usage: gsch2pcb [options] {project | foo.sch [foo1.sch ...]}\n"
1254 "\n"
1255 "Generate a PCB layout file from a set of gschem schematics.\n"
1256 " gnetlist -g PCB is run to generate foo.net from the schematics.\n"
1257 "\n"
1258 " gnetlist -g gsch2pcb is run to get PCB m4 derived elements which\n"
1259 " match schematic footprints. For schematic footprints which don't match\n"
1260 " any PCB m4 layout elements, search a set of file element directories in\n"
1261 " an attempt to find matching PCB file elements.\n"
1262 " Output to foo.pcb if it doesn't exist. If there is a current foo.pcb,\n"
1263 " output only new elements to foo.new.pcb.\n"
1264 " If any elements with a non-empty element name in the current foo.pcb\n"
1265 " have no matching schematic component, then remove those elements from\n"
1266 " foo.pcb and rename foo.pcb to a foo.pcb.bak sequence.\n"
1267 "\n"
1268 " gnetlist -g pcbpins is run to get a PCB actions file which will rename all\n"
1269 " of the pins in a .pcb file to match pin names from the schematic.\n"
1270 "\n"
1271 " \"project\" is a file (not ending in .sch) containing a list of\n"
1272 " schematics to process and some options. A schematics line is like:\n"
1273 " schematics foo1.sch foo2.sch ...\n"
1274 " Options in a project file are like command line args without the \"-\":\n"
1275 " output-name myproject\n"
1276 "\n"
1277 "options (may be included in a project file):\n"
1278 " -d, --elements-dir D Search D for PCB file elements. These defaults\n"
1279 " are searched if they exist: ./packages,\n"
1280 " /usr/local/share/pcb/newlib, /usr/share/pcb/newlib,\n"
1281 " (old pcb) /usr/local/lib/pcb_lib, /usr/lib/pcb_lib,\n"
1282 " (old pcb) /usr/local/pcb_lib\n"
1283 " -o, --output-name N Use output file names N.net, N.pcb, and N.new.pcb\n"
1284 " instead of foo.net, ... where foo is the basename\n"
1285 " of the first command line .sch file.\n"
1286 " -f, --use-files Force using file elements over m4 PCB elements\n"
1287 " for new footprints even though m4 elements are\n"
1288 " searched for first and may have been found.\n"
1289 " -r, --remove-unfound Don't include references to unfound elements in\n"
1290 " the generated .pcb files. Use if you want PCB to\n"
1291 " be able to load the (incomplete) .pcb file.\n"
1292 " This is the default behavior.\n"
1293 " -k, --keep-unfound Keep include references to unfound elements in\n"
1294 " the generated .pcb files. Use if you want to hand\n"
1295 " edit or otherwise preprocess the generated .pcb file\n"
1296 " before running pcb.\n"
1297 " -p, --preserve Preserve elements in PCB files which are not found\n"
1298 " in the schematics. Note that elements with an empty\n"
1299 " element name (schematic refdes) are never deleted,\n"
1300 " so you really shouldn't need this option.\n"
1301 " -q, --quiet Don't tell the user what to do next after running gsch2pcb.\n"
1302 "\n"
1303 " -s, --skip-m4 Skip m4 when looking for footprints. The default is to use\n"
1304 " m4 (which is what previous versions did).\n"
1305 " --m4-file F.inc Use m4 file F.inc in addition to the default m4\n"
1306 " files ./pcb.inc and ~/.pcb/pcb.inc.\n"
1307 " --m4-pcbdir D Use D as the PCB m4 files install directory\n"
1308 " instead of the default:\n";
1310 static gchar *usage_string1 =
1311 " --gnetlist backend A convenience run of extra gnetlist -g commands.\n"
1312 " Example: gnetlist partslist3\n"
1313 " Creates: myproject.partslist3\n"
1314 " --empty-footprint name See the project.sample file.\n"
1315 "\n"
1316 "options (not recognized in a project file):\n"
1317 " --gnetlist-arg arg Allows additional arguments to be passed to gnetlist.\n"
1318 " --fix-elements If a schematic component footprint is not equal\n"
1319 " to its PCB element Description, update the\n"
1320 " Description instead of replacing the element.\n"
1321 " Do this the first time gsch2pcb is used with\n"
1322 " PCB files originally created with gschem2pcb.\n"
1323 " -v, --verbose Use -v -v for additional file element debugging.\n"
1324 " -V, --version\n\n"
1325 "environment variables:\n"
1326 " GNETLIST If set, this specifies the name of the gnetlist program\n"
1327 " to execute.\n"
1328 "\n"
1329 "Additional Resources:\n"
1330 "\n"
1331 " gnetlist user guide: http://geda.seul.org/wiki/geda:gnetlist_ug\n"
1332 " gEDA homepage: http://www.gpleda.org\n"
1333 " PCB homepage: http://pcb.gpleda.org\n" "\n";
1335 static void
1336 usage ()
1338 puts (usage_string0);
1339 printf (" %s\n", default_m4_pcbdir);
1340 puts (usage_string1);
1341 exit (0);
1344 static void
1345 get_args (gint argc, gchar ** argv)
1347 gchar *opt, *arg, *s;
1348 gint i, r;
1350 for (i = 1; i < argc; ++i) {
1351 opt = argv[i];
1352 arg = argv[i + 1];
1353 if (*opt == '-') {
1354 ++opt;
1355 if (*opt == '-')
1356 ++opt;
1357 if (!strcmp (opt, "version") || !strcmp (opt, "V")) {
1358 printf ("gsch2pcb %s\n", GSC2PCB_VERSION);
1359 exit (0);
1360 } else if (!strcmp (opt, "verbose") || !strcmp (opt, "v")) {
1361 verbose += 1;
1362 continue;
1363 } else if (!strcmp (opt, "fix-elements")) {
1364 fix_elements = TRUE;
1365 continue;
1366 } else if (!strcmp (opt, "gnetlist-arg")) {
1367 extra_gnetlist_arg_list =
1368 g_list_append (extra_gnetlist_arg_list, g_strdup (arg));
1369 i++;
1370 continue;
1371 } else if (!strcmp (opt, "help") || !strcmp (opt, "h"))
1372 usage ();
1373 else if (i < argc
1374 && ((r = parse_config (opt, (i < argc - 1) ? arg : NULL))
1375 >= 0)
1377 i += r;
1378 continue;
1380 printf ("gsch2pcb: bad or incomplete arg: %s\n", argv[i]);
1381 usage ();
1382 } else {
1383 if (!g_str_has_suffix (argv[i], ".sch")) {
1384 load_extra_project_files ();
1385 load_project (argv[i]);
1386 } else
1387 add_schematic (argv[i]);
1392 gint
1393 main (gint argc, gchar ** argv)
1395 gchar *pcb_file_name,
1396 *pcb_new_file_name, *bak_file_name, *pins_file_name, *net_file_name, *tmp;
1397 gint i;
1398 gboolean initial_pcb = TRUE;
1399 gboolean created_pcb_file = TRUE;
1400 char *path, *p;
1401 const char *pcbdata_path;
1403 if (argc < 2)
1404 usage ();
1406 pcbdata_path = g_getenv ("PCBDATA"); /* do not free return value */
1407 if (pcbdata_path != NULL) {
1408 /* If PCBDATA is set, use the value */
1409 m4_pcbdir = g_strconcat (pcbdata_path, "/pcb/m4", NULL);
1410 } else {
1411 /* Use the default value passed in from the configure script
1412 * instead of trying to hard code a value which is very
1413 * likely wrong
1415 m4_pcbdir = g_strconcat (PCBDATADIR, "/pcb/m4", NULL);
1418 default_m4_pcbdir = g_strdup (m4_pcbdir);
1420 get_args (argc, argv);
1422 load_extra_project_files ();
1423 add_default_m4_files ();
1425 if (!schematics)
1426 usage ();
1429 /* Defaults for the search path if not configured in the project file */
1430 if (g_file_test ("packages", G_FILE_TEST_IS_DIR))
1431 element_directory_list = g_list_append (element_directory_list, "packages");
1433 #define PCB_PATH_DELIMETER ":"
1434 if (verbose)
1435 printf ("Processing PCBLIBPATH=\"%s\"\n", PCBLIBPATH);
1437 path = g_strdup (PCBLIBPATH);
1438 for (p = strtok (path, PCB_PATH_DELIMETER); p && *p;
1439 p = strtok (NULL, PCB_PATH_DELIMETER)) {
1440 if (g_file_test (p, G_FILE_TEST_IS_DIR)) {
1441 if (verbose)
1442 printf ("Adding %s to the newlib search path\n", p);
1443 element_directory_list = g_list_append (element_directory_list,
1444 g_strdup (p));
1447 g_free (path);
1449 pins_file_name = g_strconcat (sch_basename, ".cmd", NULL);
1450 net_file_name = g_strconcat (sch_basename, ".net", NULL);
1451 pcb_file_name = g_strconcat (sch_basename, ".pcb", NULL);
1452 bak_file_name = g_strconcat (sch_basename, ".pcb.bak", NULL);
1453 tmp = g_strdup (bak_file_name);
1455 for (i = 0; g_file_test (bak_file_name, G_FILE_TEST_EXISTS); ++i) {
1456 g_free (bak_file_name);
1457 bak_file_name = g_strdup_printf ("%s%d", tmp, i);
1459 g_free (tmp);
1461 if (g_file_test (pcb_file_name, G_FILE_TEST_EXISTS)) {
1462 initial_pcb = FALSE;
1463 pcb_new_file_name = g_strconcat (sch_basename, ".new.pcb", NULL);
1464 get_pcb_element_list (pcb_file_name);
1465 } else
1466 pcb_new_file_name = g_strdup (pcb_file_name);
1468 if (!run_gnetlist (pins_file_name, net_file_name, pcb_new_file_name,
1469 sch_basename, schematics)) {
1470 fprintf(stderr, "Failed to run gnetlist\n");
1471 exit (1);
1474 if (add_elements (pcb_new_file_name) == 0) {
1475 build_and_run_command ("rm %s", pcb_new_file_name);
1476 if (initial_pcb) {
1477 printf ("No elements found, so nothing to do.\n");
1478 exit (0);
1482 if (fix_elements)
1483 update_element_descriptions (pcb_file_name, bak_file_name);
1484 prune_elements (pcb_file_name, bak_file_name);
1486 /* Report work done during processing */
1487 if (verbose)
1488 printf ("\n");
1489 printf ("\n----------------------------------\n");
1490 printf ("Done processing. Work performed:\n");
1491 if (n_deleted > 0 || n_fixed > 0 || need_PKG_purge || n_changed_value > 0)
1492 printf ("%s is backed up as %s.\n", pcb_file_name, bak_file_name);
1493 if (pcb_element_list && n_deleted > 0)
1494 printf ("%d elements deleted from %s.\n", n_deleted, pcb_file_name);
1496 if (n_added_ef + n_added_m4 > 0)
1497 printf ("%d file elements and %d m4 elements added to %s.\n",
1498 n_added_ef, n_added_m4, pcb_new_file_name);
1499 else if (n_not_found == 0) {
1500 printf ("No elements to add so not creating %s\n", pcb_new_file_name);
1501 created_pcb_file = FALSE;
1504 if (n_not_found > 0) {
1505 printf ("%d not found elements added to %s.\n",
1506 n_not_found, pcb_new_file_name);
1508 if (n_unknown > 0)
1509 printf ("%d components had no footprint attribute and are omitted.\n",
1510 n_unknown);
1511 if (n_none > 0)
1512 printf ("%d components with footprint \"none\" omitted from %s.\n",
1513 n_none, pcb_new_file_name);
1514 if (n_empty > 0)
1515 printf ("%d components with empty footprint \"%s\" omitted from %s.\n",
1516 n_empty, empty_footprint_name, pcb_new_file_name);
1517 if (n_changed_value > 0)
1518 printf ("%d elements had a value change in %s.\n",
1519 n_changed_value, pcb_file_name);
1520 if (n_fixed > 0)
1521 printf ("%d elements fixed in %s.\n", n_fixed, pcb_file_name);
1522 if (n_PKG_removed_old > 0) {
1523 printf ("%d elements could not be found.", n_PKG_removed_old);
1524 if (created_pcb_file)
1525 printf (" So %s is incomplete.\n", pcb_file_name);
1526 else
1527 printf ("\n");
1529 if (n_PKG_removed_new > 0) {
1530 printf ("%d elements could not be found.", n_PKG_removed_new);
1531 if (created_pcb_file)
1532 printf (" So %s is incomplete.\n", pcb_new_file_name);
1533 else
1534 printf ("\n");
1536 if (n_preserved > 0)
1537 printf ("%d elements not in the schematic preserved in %s.\n",
1538 n_preserved, pcb_file_name);
1540 /* Tell user what to do next */
1541 if (verbose)
1542 printf ("\n");
1544 if (n_added_ef + n_added_m4 > 0) {
1545 if (initial_pcb) {
1546 printf ("\nNext step:\n");
1547 printf ("1. Run pcb on your file %s.\n", pcb_file_name);
1548 printf
1549 (" You will find all your footprints in a bundle ready for you to place\n");
1550 printf
1551 (" or disperse with \"Select -> Disperse all elements\" in PCB.\n\n");
1552 printf
1553 ("2. From within PCB, select \"File -> Load netlist file\" and select \n");
1554 printf (" %s to load the netlist.\n\n", net_file_name);
1555 printf ("3. From within PCB, enter\n\n");
1556 printf (" :ExecuteFile(%s)\n\n", pins_file_name);
1557 printf
1558 (" to propagate the pin names of all footprints to the layout.\n\n");
1559 } else if (quiet_mode == FALSE) {
1560 printf ("\nNext steps:\n");
1561 printf ("1. Run pcb on your file %s.\n", pcb_file_name);
1562 printf
1563 ("2. From within PCB, select \"File -> Load layout data to paste buffer\"\n");
1564 printf
1565 (" and select %s to load the new footprints into your existing layout.\n",
1566 pcb_new_file_name);
1567 printf
1568 ("3. From within PCB, select \"File -> Load netlist file\" and select \n");
1569 printf (" %s to load the updated netlist.\n\n", net_file_name);
1570 printf ("4. From within PCB, enter\n\n");
1571 printf (" :ExecuteFile(%s)\n\n", pins_file_name);
1572 printf (" to update the pin names of all footprints.\n\n");
1576 g_free (net_file_name);
1577 g_free (pins_file_name);
1578 g_free (pcb_file_name);
1579 g_free (bak_file_name);
1581 return 0;