gsch2pcb: whitespace fix
[geda-gaf/whiteaudio.git] / utils / src / gsch2pcb.c
blob2a61e429bc29c144697337e45cc75f964ab4b421
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_command,
71 *m4_pcbdir, *default_m4_pcbdir, *m4_files, *m4_override_file;
73 static gboolean use_m4 = TRUE;
75 static gchar *empty_footprint_name;
77 static gint verbose,
78 n_deleted,
79 n_added_m4,
80 n_added_ef,
81 n_fixed,
82 n_PKG_removed_new,
83 n_PKG_removed_old,
84 n_preserved, n_changed_value, n_not_found, n_unknown, n_none, n_empty;
86 static gboolean remove_unfound_elements = TRUE,
87 quiet_mode = FALSE,
88 force_element_files, preserve, fix_elements, bak_done, need_PKG_purge;
91 static void
92 create_m4_override_file ()
94 FILE *f;
96 m4_override_file = "gnet-gsch2pcb-tmp.scm";
97 f = fopen (m4_override_file, "wb");
98 if (!f) {
99 m4_override_file = NULL;
100 return;
102 if (m4_command)
103 fprintf (f, "(define m4-command \"%s\")\n", m4_command);
104 if (m4_pcbdir)
105 fprintf (f, "(define m4-pcbdir \"%s\")\n", m4_pcbdir);
106 if (m4_files)
107 fprintf (f, "(define m4-files \"%s\")\n", m4_files);
108 fprintf (f, "(define gsch2pcb:use-m4 %s)\n", use_m4 == TRUE ? "#t" : "#f");
110 fclose (f);
111 if (verbose) {
112 printf ("Default m4-pcbdir: %s\n", default_m4_pcbdir);
113 printf ("--------\ngnet-gsch2pcb-tmp.scm override file:\n");
114 if (m4_command)
115 printf (" (define m4-command \"%s\")\n", m4_command);
116 if (m4_pcbdir)
117 printf (" (define m4-pcbdir \"%s\")\n", m4_pcbdir);
118 if (m4_files)
119 printf (" (define m4-files \"%s\")\n", m4_files);
120 printf (" (define gsch2pcb:use-m4 %s)\n", use_m4 == TRUE ? "#t" : "#f");
125 * Build and run a command. No redirection or error handling is
126 * done. Format string is split on whitespace. Specifiers %l and %s
127 * are replaced with contents of positional args. To be recognized,
128 * specifiers must be separated from other arguments in the format by
129 * whitespace.
130 * - %l expects a GList, contents used as separate arguments
131 * - %s expects a gchar*, contents used as a single argument
132 * @param[in] format used to specify command to be executed
133 * @param[in] ... positional parameters
135 static gboolean
136 build_and_run_command (const gchar *format, ...)
138 va_list vargs;
139 gchar ** split;
140 GList *tmp = NULL;
141 gint num_split;
142 gint i;
143 gint status;
144 gboolean result = FALSE;
145 gboolean spawn_result;
146 gchar *standard_error = NULL;
147 GError * error = NULL;
149 va_start (vargs, format);
150 split = g_strsplit_set (format, " \t\n\v", 0);
151 num_split = g_strv_length (split);
152 for (i = 0; i < num_split; ++i) {
153 gchar *chunk = split[i];
154 if (strcmp (chunk, "%l") == 0) {
155 /* append contents of list into command args - shared data */
156 tmp = g_list_concat (tmp, g_list_copy (va_arg (vargs, GList*)));
157 } else if (strcmp (chunk, "%s") == 0) {
158 /* insert contents of string into output */
159 tmp = g_list_append (tmp, va_arg (vargs, gchar*));
160 } else {
161 /* bare string, use as is */
162 tmp = g_list_append (tmp, chunk);
165 va_end (vargs);
167 if (tmp) {
168 /* we have something in the list, build & call command */
169 GList *p;
170 gint i = 0;
171 gchar ** args = g_new0 (gchar*, g_list_length (tmp) + 1/* NULL terminate the list */);
173 if (verbose)
174 printf ("Running command:\n\t");
176 for (p = tmp; p; p = g_list_next (p)) {
177 args[i++] = (gchar*) p->data;
178 if (verbose)
179 printf ("%s ", (char*)p->data);
182 if (verbose)
183 printf ("\n%s", SEP_STRING);
185 if (g_spawn_sync (".", /* Working directory */
186 args, /* argv */
187 NULL, /* envp */
188 G_SPAWN_SEARCH_PATH | /* flags */
189 G_SPAWN_STDOUT_TO_DEV_NULL,
190 NULL, /* child_setup */
191 NULL, /* user data */
192 NULL, /* standard output */
193 &standard_error, /* standard error */
194 &status, /* exit status return */
195 &error)) { /* GError return */
196 if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
197 result = TRUE;
198 else {
199 if (standard_error)
200 fputs(standard_error, stderr);
203 else {
204 fprintf(stderr, "Failed to execute external program: %s\n", error->message);
205 g_error_free(error);
208 if (verbose)
209 printf ("\n%s", SEP_STRING);
211 g_free(standard_error);
213 g_free (args);
214 /* free the list, but leave data untouched */
215 g_list_free (tmp);
218 g_strfreev (split);
220 return result;
223 /* Run gnetlist to generate a netlist and a PCB board file. gnetlist
224 * has exit status of 0 even if it's given an invalid arg, so do some
225 * stat() hoops to decide if gnetlist successfully generated the PCB
226 * board file (only gnetlist >= 20030901 recognizes -m).
228 static gboolean
229 run_gnetlist (gchar * pins_file, gchar * net_file, gchar * pcb_file,
230 gchar * basename, GList * largs)
232 struct stat st;
233 time_t mtime;
234 static const gchar *gnetlist = NULL;
235 GList *list = NULL;
236 GList *verboseList = NULL;
237 GList *args1 = NULL;
239 /* Allow the user to specify a full path or a different name for
240 * the gnetlist command. Especially useful if multiple copies
241 * are installed at once.
243 if (gnetlist == NULL)
244 gnetlist = g_getenv ("GNETLIST");
245 if (gnetlist == NULL)
246 gnetlist = "gnetlist";
248 if (!verbose)
249 verboseList = g_list_append (verboseList, "-q");
251 if (!build_and_run_command ("%s %l -g pcbpins -o %s %l %l",
252 gnetlist,
253 verboseList,
254 pins_file,
255 extra_gnetlist_arg_list,
256 largs))
257 return FALSE;
259 if (!build_and_run_command ("%s %l -g PCB -o %s %l %l",
260 gnetlist,
261 verboseList,
262 net_file,
263 extra_gnetlist_arg_list,
264 largs))
265 return FALSE;
266 create_m4_override_file ();
268 if (m4_override_file) {
269 args1 = g_list_append (args1, "-m");
270 args1 = g_list_append (args1, m4_override_file);
273 mtime = (stat (pcb_file, &st) == 0) ? st.st_mtime : 0;
275 if (!build_and_run_command ("%s %l -g gsch2pcb -o %s %l %l %l",
276 gnetlist,
277 verboseList,
278 pcb_file,
279 args1,
280 extra_gnetlist_arg_list,
281 largs)) {
282 if (stat (pcb_file, &st) != 0 || mtime == st.st_mtime) {
283 fprintf (stderr,
284 "gsch2pcb: gnetlist command failed, `%s' not updated\n",
285 pcb_file
287 if (m4_override_file)
288 fprintf (stderr,
289 " At least gnetlist 20030901 is required for m4-xxx options.\n");
290 return FALSE;
292 return FALSE;
295 if (m4_override_file)
296 unlink (m4_override_file);
298 for (list = extra_gnetlist_list; list; list = g_list_next (list)) {
299 const gchar *s = (gchar *) list->data;
300 const gchar *s2 = strstr (s, " -o ");
301 gchar *out_file;
302 gchar *backend;
303 if (!s2) {
304 out_file = g_strconcat (basename, ".", s, NULL);
305 backend = g_strdup (s);
306 } else {
307 out_file = g_strdup (s2 + 4);
308 backend = g_strndup (s, s2 - s);
311 if (!build_and_run_command ("%s %l -g %s -o %s %l %l",
312 gnetlist,
313 verboseList,
314 backend,
315 out_file,
316 extra_gnetlist_arg_list,
317 largs))
318 return FALSE;
319 g_free (out_file);
320 g_free (backend);
323 g_list_free (args1);
324 g_list_free (verboseList);
326 return TRUE;
329 static gchar *
330 token (gchar * string, gchar ** next, gboolean * quoted_ret)
332 static gchar *str;
333 gchar *s, *ret;
334 gboolean quoted = FALSE;
336 if (string)
337 str = string;
338 if (!str || !*str) {
339 if (next)
340 *next = str;
341 return g_strdup ("");
343 while (*str == ' ' || *str == '\t' || *str == ',' || *str == '\n')
344 ++str;
345 if (*str == '"') {
346 quoted = TRUE;
347 if (quoted_ret)
348 *quoted_ret = TRUE;
349 ++str;
350 for (s = str; *s && *s != '"' && *s != '\n'; ++s);
351 } else {
352 if (quoted_ret)
353 *quoted_ret = FALSE;
354 for (s = str;
355 *s && (*s != ' ' && *s != '\t' && *s != ',' && *s != '\n'); ++s);
357 ret = g_strndup (str, s - str);
358 str = (quoted && *s) ? s + 1 : s;
359 if (next)
360 *next = str;
361 return ret;
364 static gchar *
365 fix_spaces (gchar * str)
367 gchar *s;
369 if (!str)
370 return NULL;
371 for (s = str; *s; ++s)
372 if (*s == ' ' || *s == '\t')
373 *s = '_';
374 return str;
377 /* As of 1/9/2004 CVS hi_res Element[] line format:
378 * Element[element_flags, description, pcb-name, value, mark_x, mark_y,
379 * text_x, text_y, text_direction, text_scale, text_flags]
380 * New PCB 1.7 / 1.99 Element() line format:
381 * Element(element_flags, description, pcb-name, value, mark_x, mark_y,
382 * text_x, text_y, text_direction, text_scale, text_flags)
383 * Old PCB 1.6 Element() line format:
384 * Element(element_flags, description, pcb-name, value,
385 * text_x, text_y, text_direction, text_scale, text_flags)
387 * (mark_x, mark_y) is the element position (mark) and (text_x,text_y)
388 * is the description text position which is absolute in pre 1.7 and
389 * is now relative. The hi_res mark_x,mark_y and text_x,text_y resolutions
390 * are 100x the other formats.
392 PcbElement *
393 pcb_element_line_parse (gchar * line)
395 PcbElement *el = NULL;
396 gchar *s, *t, close_char;
397 gint state = 0, elcount = 0;
399 if (strncmp (line, "Element", 7))
400 return NULL;
402 el = g_new0 (PcbElement, 1);
404 s = line + 7;
405 while (*s == ' ' || *s == '\t')
406 ++s;
408 if (*s == '[')
409 el->hi_res_format = TRUE;
410 else if (*s != '(') {
411 g_free (el);
412 return NULL;
415 el->res_char = el->hi_res_format ? '[' : '(';
416 close_char = el->hi_res_format ? ']' : ')';
418 el->flags = token (s + 1, NULL, &el->quoted_flags);
419 el->description = token (NULL, NULL, NULL);
420 el->refdes = token (NULL, NULL, NULL);
421 el->value = token (NULL, NULL, NULL);
423 s = token (NULL, NULL, NULL);
424 el->x = atoi (s);
425 g_free (s);
427 s = token (NULL, &t, NULL);
428 el->y = atoi (s);
429 g_free (s);
431 el->tail = g_strdup (t ? t : "");
432 if ((s = strrchr (el->tail, (gint) '\n')) != NULL)
433 *s = '\0';
435 /* Count the tokens in tail to decide if it's new or old format.
436 * Old format will have 3 tokens, new format will have 5 tokens.
438 for (s = el->tail; *s && *s != close_char; ++s) {
439 if (*s != ' ') {
440 if (state == 0)
441 ++elcount;
442 state = 1;
443 } else
444 state = 0;
446 if (elcount > 4)
447 el->new_format = TRUE;
449 fix_spaces (el->description);
450 fix_spaces (el->refdes);
451 fix_spaces (el->value);
453 /* Don't allow elements with no refdes to ever be deleted because
454 * they may be desired pc board elements not in schematics. So
455 * initialize still_exists to TRUE if empty or non-alphanumeric
456 * refdes.
458 if (!*el->refdes || !isalnum ((gint) (*el->refdes)))
459 el->still_exists = TRUE;
461 return el;
464 static void
465 pcb_element_free (PcbElement * el)
467 if (!el)
468 return;
469 g_free (el->flags);
470 g_free (el->description);
471 g_free (el->changed_description);
472 g_free (el->changed_value);
473 g_free (el->refdes);
474 g_free (el->value);
475 g_free (el->tail);
476 g_free (el->pkg_name_fix);
477 g_free (el);
480 static void
481 get_pcb_element_list (gchar * pcb_file)
483 FILE *f;
484 PcbElement *el;
485 gchar *s, buf[1024];
487 if ((f = fopen (pcb_file, "r")) == NULL)
488 return;
489 while ((fgets (buf, sizeof (buf), f)) != NULL) {
490 for (s = buf; *s == ' ' || *s == '\t'; ++s);
491 if (!strncmp (s, "PKG_", 4)) {
492 need_PKG_purge = TRUE;
493 continue;
495 if ((el = pcb_element_line_parse (s)) == NULL)
496 continue;
497 pcb_element_list = g_list_append (pcb_element_list, el);
499 fclose (f);
502 static PcbElement *
503 pcb_element_exists (PcbElement * el_test, gboolean record)
505 GList *list;
506 PcbElement *el;
508 for (list = pcb_element_list; list; list = g_list_next (list)) {
509 el = (PcbElement *) list->data;
511 if (strcmp (el_test->refdes, el->refdes))
512 continue;
513 if (strcmp (el_test->description, el->description)) { /* footprint */
514 if (record)
515 el->changed_description = g_strdup (el_test->description);
516 } else {
517 if (record) {
518 if (strcmp (el_test->value, el->value))
519 el->changed_value = g_strdup (el_test->value);
520 el->still_exists = TRUE;
522 return el;
525 return NULL;
528 /* A problem is that new PCB 1.7 file elements have the
529 * (mark_x,mark_y) value set to wherever the element was created and
530 * no equivalent of a gschem translate symbol was done.
532 * So, file elements inserted can be scattered over a big area and
533 * this is bad when loading a file.new.pcb into an existing PC
534 * board. So, do a simple translate if (mark_x,mark_y) is
535 * (arbitrarily) over 1000. I'll assume that for values < 1000 the
536 * element creator was concerned with a sane initial element
537 * placement. Unless someone has a better idea? Don't bother with
538 * pre PCB 1.7 formats as that would require parsing the mark().
539 * Current m4 elements use the old format but they seem to have a
540 * reasonable initial mark().
542 static void
543 simple_translate (PcbElement * el)
545 gint factor;
547 if (el->new_format) {
548 factor = el->hi_res_format ? 100 : 1;
549 if (el->x > 1000 * factor)
550 el->x = 500 * factor;
551 if (el->y > 1000 * factor)
552 el->y = 500 * factor;
556 static gboolean
557 insert_element (FILE * f_out, gchar * element_file,
558 gchar * footprint, gchar * refdes, gchar * value)
560 FILE *f_in;
561 PcbElement *el;
562 gchar *fmt, *s, buf[1024];
563 gboolean retval = FALSE;
565 if ((f_in = fopen (element_file, "r")) == NULL) {
566 s = g_strdup_printf ("insert_element() can't open %s", element_file);
567 perror (s);
568 g_free (s);
569 return FALSE;
571 /* Scan the file to detect whether it's actually a PCB
572 * layout. Assumes that a PCB layout will have a "PCB" line. */
573 while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
574 for (s = buf; *s == ' ' || *s == '\t'; ++s);
575 s[3] = 0; /* Truncate line */
576 if (strncmp ("PCB", s, sizeof (buf)) == 0) {
577 printf ("Warning: %s appears to be a PCB layout file. Skipping.\n",
578 element_file);
579 fclose (f_in);
580 return FALSE;
583 rewind (f_in);
585 /* Copy the file element lines. Substitute new parameters into the
586 * Element() or Element[] line and strip comments.
588 while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
589 for (s = buf; *s == ' ' || *s == '\t'; ++s);
590 if ((el = pcb_element_line_parse (s)) != NULL) {
591 simple_translate (el);
592 fmt = el->quoted_flags ?
593 "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %d %d%s\n" :
594 "Element%c%s \"%s\" \"%s\" \"%s\" %d %d%s\n";
596 fprintf (f_out, fmt,
597 el->res_char, el->flags, footprint, refdes, value,
598 el->x, el->y, el->tail);
599 retval = TRUE;;
600 } else if (*s != '#')
601 fputs (buf, f_out);
602 pcb_element_free (el);
604 fclose (f_in);
605 return retval;
609 gchar *
610 find_element (gchar * dir_path, gchar * element)
612 GDir *dir;
613 gchar *path, *name, *s, *found = NULL;
615 if ((dir = g_dir_open (dir_path, 0, NULL)) == NULL) {
616 s = g_strdup_printf ("find_element can't open dir \"%s\"", dir_path);
617 perror (s);
618 g_free (s);
619 return NULL;
621 if (verbose > 1)
622 printf ("\t Searching: \"%s\" for \"%s\"\n", dir_path, element);
623 while ((name = (gchar *) g_dir_read_name (dir)) != NULL) {
624 path = g_strconcat (dir_path, "/", name, NULL);
625 found = NULL;
627 /* if we got a directory name, then recurse down into it */
628 if (g_file_test (path, G_FILE_TEST_IS_DIR))
629 found = find_element (path, element);
631 /* otherwise assume it is a file and see if it is the one we want */
632 else {
633 if (verbose > 1)
634 printf ("\t : %s\t", name);
635 if (!strcmp (name, element))
636 found = g_strdup (path);
637 else {
638 gchar *tmps;
639 tmps = g_strconcat (element, ".fp", NULL);
640 if (!strcmp (name, tmps))
641 found = g_strdup (path);
642 g_free (tmps);
644 if (verbose > 1)
645 printf ("%s\n", found ? "Yes" : "No");
647 g_free (path);
648 if (found)
649 break;
651 g_dir_close (dir);
652 return found;
655 gchar *
656 search_element_directories (PcbElement * el)
658 GList *list;
659 gchar *s, *elname = NULL, *dir_path, *path = NULL;
660 gint n1, n2;
662 /* See comment before pkg_to_element() */
663 if (el->pkg_name_fix) {
664 if (strchr (el->description, '-')) {
665 n1 = strlen (el->description);
666 n2 = strlen (el->pkg_name_fix);
667 s = el->description + n1 - n2 - 1;
669 // printf("n1=%d n2=%d desc:%s fix:%s s:%s\n",
670 // n1, n2, el->description, el->pkg_name_fix, s);
672 if (n1 > 0 && n2 < n1 && *s == '-' && *(s + 1) == *el->pkg_name_fix) {
673 s = g_strndup (el->description, n1 - n2 - 1);
674 elname = g_strconcat (s, " ", el->pkg_name_fix, NULL);
675 g_free (s);
678 if (!elname) {
679 printf ("Warning: argument passing may have been confused by\n");
680 printf (" a comma in a component value:\n");
681 printf (" Check %s %s %s\n",
682 el->refdes, el->description, el->value);
683 printf (" Maybe just use a space instead of a comma?\n");
686 if (!elname)
687 elname = g_strdup (el->description);
689 if (!strcmp (elname, "unknown")) {
690 g_free (elname);
691 return NULL;
693 if (verbose > 1)
694 printf ("\tSearching directories looking for file element: %s\n", elname);
695 for (list = element_directory_list; list; list = g_list_next (list)) {
696 dir_path = (gchar *) list->data;
697 if (verbose > 1)
698 printf ("\tLooking in directory: \"%s\"\n", dir_path);
699 path = find_element (dir_path, elname);
700 if (path) {
701 if (verbose)
702 printf ("\tFound: %s\n", path);
703 break;
706 g_free (elname);
707 return path;
710 /* The gnetlist backend gnet-gsch2pcb.scm generates PKG_ lines:
712 * PKG_footprint(footprint{-fp0-fp1},refdes,value{,fp0,fp1})
714 * where fp1 and fp2 (if they exist) are the extra footprint
715 * components when specifying footprints like "DIL 14 300". This is
716 * needed for m4 macros.
718 * A complication is if the footprint references a file element with
719 * spaces embedded in the name. The gnetlist backend will interpret
720 * these as fp0, fp1, ... args and the footprint will in this case
721 * incorrectly have '-' inserted where the spaces should be. So, if
722 * there are additional args, reconstruct the portion of the name
723 * given by the args with spaces for later use. Eg. if the footprint
724 * is "100 Pin jack", we will have
726 * PKG_100-Pin-jack(100-Pin-jack,refdes,value,Pin,jack)
728 * So put "Pin jack" into pkg_name_fix so if this element is searched
729 * as a file element we can munge the description to what it should
730 * be, eg:
732 * 100-Pin-jack -> 100 Pin jack
734 static PcbElement *
735 pkg_to_element (FILE * f, gchar * pkg_line)
737 PcbElement *el;
738 gchar **args, *s;
739 gint n, n_extra_args, n_dashes;
741 if (strncmp (pkg_line, "PKG_", 4)
742 || (s = strchr (pkg_line, (gint) '(')) == NULL)
743 return NULL;
745 args = g_strsplit (s + 1, ",", 12);
746 if (!args[0] || !args[1] || !args[2]) {
747 fprintf (stderr, "Bad package line: %s\n", pkg_line);
748 return NULL;
750 fix_spaces (args[0]);
751 fix_spaces (args[1]);
752 fix_spaces (args[2]);
754 el = g_new0 (PcbElement, 1);
755 el->description = g_strdup (args[0]);
756 el->refdes = g_strdup (args[1]);
757 el->value = g_strdup (args[2]);
758 if ((s = strchr (el->value, (gint) ')')) != NULL)
759 *s = '\0';
761 /* If the component value has a comma, eg "1k, 1%", the gnetlist generated
762 * PKG line will be
764 * PKG_XXX(`R0w8',`R100',`1k, 1%'),
766 * but after processed by m4, the input to gsch2pcb will be
768 * PKG_XXX(R0w8,R100,1k, 1%).
770 * So the quoting info has been lost when processing for file
771 * elements. So here try to detect and fix this. But I can't
772 * handle the situation where the description has a '-' and the
773 * value has a comma because gnet-gsch2pcb.scm munges the
774 * description with '-' when there are extra args.
776 for (n_extra_args = 0; args[3 + n_extra_args] != NULL; ++n_extra_args);
777 s = el->description;
778 for (n_dashes = 0; (s = strchr (s + 1, '-')) != NULL; ++n_dashes);
780 n = 3;
781 if (n_extra_args == n_dashes + 1) { /* Assume there was a comma in the value, eg "1K, 1%" */
782 s = el->value;
783 el->value = g_strconcat (s, ",", fix_spaces (args[n]), NULL);
784 g_free (s);
785 if ((s = strchr (el->value, (gint) ')')) != NULL)
786 *s = '\0';
787 n = 4;
789 if (args[n]) {
790 el->pkg_name_fix = g_strdup (args[n]);
791 for (n += 1; args[n] != NULL; ++n) {
792 s = el->pkg_name_fix;
793 el->pkg_name_fix = g_strconcat (s, " ", args[n], NULL);
794 g_free (s);
796 if ((s = strchr (el->pkg_name_fix, (gint) ')')) != NULL)
797 *s = '\0';
799 g_strfreev (args);
801 if (empty_footprint_name && !strcmp (el->description, empty_footprint_name)) {
802 if (verbose)
803 printf
804 ("%s: has the empty footprint attribute \"%s\" so won't be in the layout.\n",
805 el->refdes, el->description);
806 n_empty += 1;
807 el->omit_PKG = TRUE;
808 } else if (!strcmp (el->description, "none")) {
809 fprintf (stderr,
810 "WARNING: %s has a footprint attribute \"%s\" so won't be in the layout.\n",
811 el->refdes, el->description);
812 n_none += 1;
813 el->omit_PKG = TRUE;
814 } else if (!strcmp (el->description, "unknown")) {
815 fprintf (stderr,
816 "WARNING: %s has no footprint attribute so won't be in the layout.\n",
817 el->refdes);
818 n_unknown += 1;
819 el->omit_PKG = TRUE;
821 return el;
824 /* Process the newly created pcb file which is the output from
825 * gnetlist -g gsch2pcb ...
827 * It will have elements found via the m4 interface and PKG_ lines for
828 * elements not found. Insert pcb file elements for PKG_ lines if
829 * file elements can be found. If there was an existing pcb file,
830 * strip out any elements if they are already present so that the new
831 * pcb file will only have new elements.
833 static gint
834 add_elements (gchar * pcb_file)
836 FILE *f_in, *f_out;
837 PcbElement *el = NULL;
838 gchar *command, *p, *tmp_file, *s, buf[1024];
839 gint total, paren_level = 0;
840 gboolean is_m4, skipping = FALSE;
842 if ((f_in = fopen (pcb_file, "r")) == NULL)
843 return 0;
844 tmp_file = g_strconcat (pcb_file, ".tmp", NULL);
845 if ((f_out = fopen (tmp_file, "wb")) == NULL) {
846 fclose (f_in);
847 g_free (tmp_file);
848 return 0;
850 while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
851 for (s = buf; *s == ' ' || *s == '\t'; ++s);
852 if (skipping) {
853 if (*s == '(')
854 ++paren_level;
855 else if (*s == ')' && --paren_level <= 0)
856 skipping = FALSE;
857 continue;
859 is_m4 = FALSE;
860 if ((el = pcb_element_line_parse (s)) != NULL)
861 is_m4 = TRUE;
862 else
863 el = pkg_to_element (f_out, s);
864 if (el && pcb_element_exists (el, TRUE)) {
865 skipping = is_m4;
866 pcb_element_free (el);
867 continue;
869 if (!el || el->omit_PKG) {
870 if (el) {
872 } else
873 fputs (buf, f_out);
874 continue;
876 if (!is_m4 || (is_m4 && force_element_files)) {
877 if (verbose && !is_m4)
878 printf ("%s: need new file element for footprint %s (value=%s)\n",
879 el->refdes, el->description, el->value);
880 if (verbose && is_m4 && force_element_files)
881 printf
882 ("%s: have m4 element %s, but trying to replace with a file element.\n",
883 el->refdes, el->description);
884 p = search_element_directories (el);
885 if (!p && verbose && is_m4 && force_element_files)
886 printf ("\tNo file element found.\n");
888 if (p && insert_element (f_out, p,
889 el->description, el->refdes, el->value)) {
890 skipping = is_m4;
891 is_m4 = FALSE;
892 ++n_added_ef;
893 if (verbose)
894 printf ("%s: added new file element for footprint %s (value=%s)\n",
895 el->refdes, el->description, el->value);
896 } else if (!is_m4) {
897 fprintf (stderr,
898 "%s: can't find PCB element for footprint %s (value=%s)\n",
899 el->refdes, el->description, el->value);
900 if (remove_unfound_elements && !fix_elements) {
901 fprintf (stderr,
902 "So device %s will not be in the layout.\n", el->refdes);
903 ++n_PKG_removed_new;
904 } else {
905 ++n_not_found;
906 fputs (buf, f_out); /* Copy PKG_ line */
909 g_free (p);
911 if (is_m4) {
912 fputs (buf, f_out);
913 ++n_added_m4;
914 if (verbose)
915 printf ("%s: added new m4 element for footprint %s (value=%s)\n",
916 el->refdes, el->description, el->value);
918 pcb_element_free (el);
919 if (verbose)
920 printf ("----\n");
922 fclose (f_in);
923 fclose (f_out);
925 total = n_added_ef + n_added_m4 + n_not_found;
926 if (total == 0)
927 build_and_run_command ("rm %s", tmp_file);
928 else
929 build_and_run_command ("mv %s %s", tmp_file, pcb_file);
930 g_free (tmp_file);
931 return total;
934 static void
935 update_element_descriptions (gchar * pcb_file, gchar * bak)
937 FILE *f_in, *f_out;
938 GList *list;
939 PcbElement *el, *el_exists;
940 gchar *fmt, *command, *tmp, *s, buf[1024];
942 for (list = pcb_element_list; list; list = g_list_next (list)) {
943 el = (PcbElement *) list->data;
944 if (el->changed_description)
945 ++n_fixed;
947 if (!pcb_element_list || n_fixed == 0) {
948 fprintf (stderr, "Could not find any elements to fix.\n");
949 return;
951 if ((f_in = fopen (pcb_file, "r")) == NULL)
952 return;
953 tmp = g_strconcat (pcb_file, ".tmp", NULL);
954 if ((f_out = fopen (tmp, "wb")) == NULL) {
955 fclose (f_in);
956 return;
958 while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
959 for (s = buf; *s == ' ' || *s == '\t'; ++s);
960 if ((el = pcb_element_line_parse (s)) != NULL
961 && (el_exists = pcb_element_exists (el, FALSE)) != NULL
962 && el_exists->changed_description) {
963 fmt = el->quoted_flags ?
964 "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %d %d%s\n" :
965 "Element%c%s \"%s\" \"%s\" \"%s\" %d %d%s\n";
966 fprintf (f_out, fmt,
967 el->res_char,
968 el->flags, el_exists->changed_description,
969 el->refdes, el->value, el->x, el->y, el->tail);
970 printf ("%s: updating element Description: %s -> %s\n",
971 el->refdes, el->description, el_exists->changed_description);
972 el_exists->still_exists = TRUE;
973 } else
974 fputs (buf, f_out);
975 pcb_element_free (el);
977 fclose (f_in);
978 fclose (f_out);
980 if (!bak_done) {
981 build_and_run_command ("mv %s %s", pcb_file, bak);
982 bak_done = TRUE;
985 build_and_run_command ("mv %s %s", tmp, pcb_file);
986 g_free (tmp);
989 static void
990 prune_elements (gchar * pcb_file, gchar * bak)
992 FILE *f_in, *f_out;
993 GList *list;
994 PcbElement *el, *el_exists;
995 gchar *fmt, *command, *tmp, *s, buf[1024];
996 gint paren_level = 0;
997 gboolean skipping = FALSE;
999 for (list = pcb_element_list; list; list = g_list_next (list)) {
1000 el = (PcbElement *) list->data;
1001 if (!el->still_exists) {
1002 if (preserve) {
1003 ++n_preserved;
1004 fprintf (stderr,
1005 "Preserving PCB element not in the schematic: %s (element %s)\n",
1006 el->refdes, el->description);
1007 } else
1008 ++n_deleted;
1009 } else if (el->changed_value)
1010 ++n_changed_value;
1012 if (!pcb_element_list
1013 || (n_deleted == 0 && !need_PKG_purge && n_changed_value == 0)
1015 return;
1016 if ((f_in = fopen (pcb_file, "r")) == NULL)
1017 return;
1018 tmp = g_strconcat (pcb_file, ".tmp", NULL);
1019 if ((f_out = fopen (tmp, "wb")) == NULL) {
1020 fclose (f_in);
1021 return;
1023 while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
1024 for (s = buf; *s == ' ' || *s == '\t'; ++s);
1025 if (skipping) {
1026 if (*s == '(')
1027 ++paren_level;
1028 else if (*s == ')' && --paren_level <= 0)
1029 skipping = FALSE;
1030 continue;
1032 el_exists = NULL;
1033 if ((el = pcb_element_line_parse (s)) != NULL
1034 && (el_exists = pcb_element_exists (el, FALSE)) != NULL
1035 && !el_exists->still_exists && !preserve) {
1036 skipping = TRUE;
1037 if (verbose)
1038 printf ("%s: deleted element %s (value=%s)\n",
1039 el->refdes, el->description, el->value);
1040 pcb_element_free (el);
1041 continue;
1043 if (el_exists && el_exists->changed_value) {
1044 fmt = el->quoted_flags ?
1045 "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %d %d%s\n" :
1046 "Element%c%s \"%s\" \"%s\" \"%s\" %d %d%s\n";
1047 fprintf (f_out, fmt,
1048 el->res_char, el->flags, el->description, el->refdes,
1049 el_exists->changed_value, el->x, el->y, el->tail);
1050 if (verbose)
1051 printf ("%s: changed element %s value: %s -> %s\n",
1052 el->refdes, el->description,
1053 el->value, el_exists->changed_value);
1054 } else if (!strncmp (s, "PKG_", 4))
1055 ++n_PKG_removed_old;
1056 else
1057 fputs (buf, f_out);
1058 pcb_element_free (el);
1060 fclose (f_in);
1061 fclose (f_out);
1063 if (!bak_done) {
1064 build_and_run_command ("mv %s %s", pcb_file, bak);
1065 bak_done = TRUE;
1068 build_and_run_command ("mv %s %s", tmp, pcb_file);
1069 g_free (tmp);
1072 static void
1073 add_m4_file (gchar * arg)
1075 gchar *s;
1077 if (!m4_files)
1078 m4_files = g_strdup (arg);
1079 else {
1080 s = m4_files;
1081 m4_files = g_strconcat (m4_files, " ", arg, NULL);
1082 g_free (s);
1086 static gchar *
1087 expand_dir (gchar * dir)
1089 gchar *s;
1091 if (*dir == '~')
1092 s = g_build_filename ((gchar *) g_get_home_dir (), dir + 1, NULL);
1093 else
1094 s = g_strdup (dir);
1095 return s;
1098 static void
1099 add_default_m4_files (void)
1101 gchar *path;
1103 path = g_build_filename ((gchar *) g_get_home_dir (),
1104 ".pcb", DEFAULT_PCB_INC, NULL);
1105 if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
1106 add_m4_file (path);
1107 g_free (path);
1109 if (g_file_test (DEFAULT_PCB_INC, G_FILE_TEST_IS_REGULAR))
1110 add_m4_file (DEFAULT_PCB_INC);
1114 static void
1115 add_schematic (gchar * sch)
1117 const gchar* s;
1118 schematics = g_list_append (schematics, g_strdup (sch));
1119 if (!sch_basename && (s = g_strrstr (sch, ".sch")) != NULL && strlen(s) == 4)
1120 sch_basename = g_strndup (sch, s - sch);
1123 static void
1124 add_multiple_schematics (gchar * sch)
1126 /* parse the string using shell semantics */
1127 gint count;
1128 gchar** args = NULL;
1129 GError* error = NULL;
1131 if (g_shell_parse_argv (sch, &count, &args, &error)) {
1132 int i;
1133 for (i = 0; i < count; ++i)
1135 add_schematic (args[i]);
1137 g_strfreev (args);
1138 } else {
1139 fprintf (stderr,
1140 "invalid `schematics' option: %s\n",
1141 error->message);
1142 g_error_free (error);
1146 static gint
1147 parse_config (gchar * config, gchar * arg)
1149 gchar *s;
1151 /* remove trailing white space otherwise strange things can happen */
1152 if ((arg != NULL) && (strlen (arg) >= 1)) {
1153 s = arg + strlen (arg) - 1;
1154 while ((*s == ' ' || *s == '\t') && (s != arg))
1155 s--;
1156 s++;
1157 *s = '\0';
1159 if (verbose)
1160 printf (" %s \"%s\"\n", config, arg ? arg : "");
1162 if (!strcmp (config, "remove-unfound") || !strcmp (config, "r")) {
1163 /* This is default behavior set in header section */
1164 remove_unfound_elements = TRUE;
1165 return 0;
1167 if (!strcmp (config, "keep-unfound") || !strcmp (config, "k")) {
1168 remove_unfound_elements = FALSE;
1169 return 0;
1171 if (!strcmp (config, "quiet") || !strcmp (config, "q")) {
1172 quiet_mode = TRUE;
1173 return 0;
1175 if (!strcmp (config, "preserve") || !strcmp (config, "p")) {
1176 preserve = TRUE;
1177 return 0;
1179 if (!strcmp (config, "use-files") || !strcmp (config, "f")) {
1180 force_element_files = TRUE;
1181 return 0;
1183 if (!strcmp (config, "skip-m4") || !strcmp (config, "s")) {
1184 use_m4 = FALSE;
1185 return 0;
1187 if (!strcmp (config, "elements-dir") || !strcmp (config, "d")) {
1188 if (verbose > 1)
1189 printf ("\tAdding directory to file element directory list: %s\n",
1190 expand_dir (arg));
1191 element_directory_list =
1192 g_list_prepend (element_directory_list, expand_dir (arg));
1193 } else if (!strcmp (config, "output-name") || !strcmp (config, "o"))
1194 sch_basename = g_strdup (arg);
1195 else if (!strcmp (config, "schematics"))
1196 add_multiple_schematics (arg);
1197 else if (!strcmp (config, "m4-command"))
1198 m4_command = g_strdup (arg);
1199 else if (!strcmp (config, "m4-pcbdir"))
1200 m4_pcbdir = g_strdup (arg);
1201 else if (!strcmp (config, "m4-file"))
1202 add_m4_file (arg);
1203 else if (!strcmp (config, "gnetlist"))
1204 extra_gnetlist_list = g_list_append (extra_gnetlist_list, g_strdup (arg));
1205 else if (!strcmp (config, "empty-footprint"))
1206 empty_footprint_name = g_strdup (arg);
1207 else
1208 return -1;
1210 return 1;
1213 static void
1214 load_project (gchar * path)
1216 FILE *f;
1217 gchar *s, buf[1024], config[32], arg[768];
1219 f = fopen (path, "r");
1220 if (!f)
1221 return;
1222 if (verbose)
1223 printf ("Reading project file: %s\n", path);
1224 while (fgets (buf, sizeof (buf), f)) {
1225 for (s = buf; *s == ' ' || *s == '\t' || *s == '\n'; ++s);
1226 if (!*s || *s == '#' || *s == '/' || *s == ';')
1227 continue;
1228 arg[0] = '\0';
1229 sscanf (s, "%31s %767[^\n]", config, arg);
1230 parse_config (config, arg);
1232 fclose (f);
1235 static void
1236 load_extra_project_files (void)
1238 gchar *path;
1239 static gboolean done = FALSE;
1241 if (done)
1242 return;
1244 load_project ("/etc/gsch2pcb");
1245 load_project ("/usr/local/etc/gsch2pcb");
1247 path = g_build_filename ((gchar *) g_get_home_dir (), ".gEDA",
1248 "gsch2pcb", NULL);
1249 load_project (path);
1250 g_free (path);
1252 done = TRUE;
1255 static gchar *usage_string0 =
1256 "usage: gsch2pcb [options] {project | foo.sch [foo1.sch ...]}\n"
1257 "\n"
1258 "Generate a PCB layout file from a set of gschem schematics.\n"
1259 " gnetlist -g PCB is run to generate foo.net from the schematics.\n"
1260 "\n"
1261 " gnetlist -g gsch2pcb is run to get PCB m4 derived elements which\n"
1262 " match schematic footprints. For schematic footprints which don't match\n"
1263 " any PCB m4 layout elements, search a set of file element directories in\n"
1264 " an attempt to find matching PCB file elements.\n"
1265 " Output to foo.pcb if it doesn't exist. If there is a current foo.pcb,\n"
1266 " output only new elements to foo.new.pcb.\n"
1267 " If any elements with a non-empty element name in the current foo.pcb\n"
1268 " have no matching schematic component, then remove those elements from\n"
1269 " foo.pcb and rename foo.pcb to a foo.pcb.bak sequence.\n"
1270 "\n"
1271 " gnetlist -g pcbpins is run to get a PCB actions file which will rename all\n"
1272 " of the pins in a .pcb file to match pin names from the schematic.\n"
1273 "\n"
1274 " \"project\" is a file (not ending in .sch) containing a list of\n"
1275 " schematics to process and some options. A schematics line is like:\n"
1276 " schematics foo1.sch foo2.sch ...\n"
1277 " Options in a project file are like command line args without the \"-\":\n"
1278 " output-name myproject\n"
1279 "\n"
1280 "options (may be included in a project file):\n"
1281 " -d, --elements-dir D Search D for PCB file elements. These defaults\n"
1282 " are searched if they exist: ./packages,\n"
1283 " /usr/local/share/pcb/newlib, /usr/share/pcb/newlib,\n"
1284 " (old pcb) /usr/local/lib/pcb_lib, /usr/lib/pcb_lib,\n"
1285 " (old pcb) /usr/local/pcb_lib\n"
1286 " -o, --output-name N Use output file names N.net, N.pcb, and N.new.pcb\n"
1287 " instead of foo.net, ... where foo is the basename\n"
1288 " of the first command line .sch file.\n"
1289 " -f, --use-files Force using file elements over m4 PCB elements\n"
1290 " for new footprints even though m4 elements are\n"
1291 " searched for first and may have been found.\n"
1292 " -r, --remove-unfound Don't include references to unfound elements in\n"
1293 " the generated .pcb files. Use if you want PCB to\n"
1294 " be able to load the (incomplete) .pcb file.\n"
1295 " This is the default behavior.\n"
1296 " -k, --keep-unfound Keep include references to unfound elements in\n"
1297 " the generated .pcb files. Use if you want to hand\n"
1298 " edit or otherwise preprocess the generated .pcb file\n"
1299 " before running pcb.\n"
1300 " -p, --preserve Preserve elements in PCB files which are not found\n"
1301 " in the schematics. Note that elements with an empty\n"
1302 " element name (schematic refdes) are never deleted,\n"
1303 " so you really shouldn't need this option.\n"
1304 " -q, --quiet Don't tell the user what to do next after running gsch2pcb.\n"
1305 "\n"
1306 " -s, --skip-m4 Skip m4 when looking for footprints. The default is to use\n"
1307 " m4 (which is what previous versions did).\n"
1308 " --m4-file F.inc Use m4 file F.inc in addition to the default m4\n"
1309 " files ./pcb.inc and ~/.pcb/pcb.inc.\n"
1310 " --m4-pcbdir D Use D as the PCB m4 files install directory\n"
1311 " instead of the default:\n";
1313 static gchar *usage_string1 =
1314 " --gnetlist backend A convenience run of extra gnetlist -g commands.\n"
1315 " Example: gnetlist partslist3\n"
1316 " Creates: myproject.partslist3\n"
1317 " --empty-footprint name See the project.sample file.\n"
1318 "\n"
1319 "options (not recognized in a project file):\n"
1320 " --gnetlist-arg arg Allows additional arguments to be passed to gnetlist.\n"
1321 " --fix-elements If a schematic component footprint is not equal\n"
1322 " to its PCB element Description, update the\n"
1323 " Description instead of replacing the element.\n"
1324 " Do this the first time gsch2pcb is used with\n"
1325 " PCB files originally created with gschem2pcb.\n"
1326 " -v, --verbose Use -v -v for additional file element debugging.\n"
1327 " -V, --version\n\n"
1328 "environment variables:\n"
1329 " GNETLIST If set, this specifies the name of the gnetlist program\n"
1330 " to execute.\n"
1331 "\n"
1332 "Additional Resources:\n"
1333 "\n"
1334 " gnetlist user guide: http://geda.seul.org/wiki/geda:gnetlist_ug\n"
1335 " gEDA homepage: http://www.gpleda.org\n"
1336 " PCB homepage: http://pcb.gpleda.org\n" "\n";
1338 static void
1339 usage ()
1341 puts (usage_string0);
1342 printf (" %s\n", default_m4_pcbdir);
1343 puts (usage_string1);
1344 exit (0);
1347 static void
1348 get_args (gint argc, gchar ** argv)
1350 gchar *opt, *arg, *s;
1351 gint i, r;
1353 for (i = 1; i < argc; ++i) {
1354 opt = argv[i];
1355 arg = argv[i + 1];
1356 if (*opt == '-') {
1357 ++opt;
1358 if (*opt == '-')
1359 ++opt;
1360 if (!strcmp (opt, "version") || !strcmp (opt, "V")) {
1361 printf ("gsch2pcb %s\n", GSC2PCB_VERSION);
1362 exit (0);
1363 } else if (!strcmp (opt, "verbose") || !strcmp (opt, "v")) {
1364 verbose += 1;
1365 continue;
1366 } else if (!strcmp (opt, "fix-elements")) {
1367 fix_elements = TRUE;
1368 continue;
1369 } else if (!strcmp (opt, "gnetlist-arg")) {
1370 extra_gnetlist_arg_list =
1371 g_list_append (extra_gnetlist_arg_list, g_strdup (arg));
1372 i++;
1373 continue;
1374 } else if (!strcmp (opt, "help") || !strcmp (opt, "h"))
1375 usage ();
1376 else if (i < argc
1377 && ((r = parse_config (opt, (i < argc - 1) ? arg : NULL))
1378 >= 0)
1380 i += r;
1381 continue;
1383 printf ("gsch2pcb: bad or incomplete arg: %s\n", argv[i]);
1384 usage ();
1385 } else {
1386 if (!g_str_has_suffix (argv[i], ".sch")) {
1387 load_extra_project_files ();
1388 load_project (argv[i]);
1389 } else
1390 add_schematic (argv[i]);
1395 gint
1396 main (gint argc, gchar ** argv)
1398 gchar *pcb_file_name,
1399 *pcb_new_file_name, *bak_file_name, *pins_file_name, *net_file_name, *tmp;
1400 gint i;
1401 gboolean initial_pcb = TRUE;
1402 gboolean created_pcb_file = TRUE;
1403 char *path, *p;
1404 const char *pcbdata_path;
1406 if (argc < 2)
1407 usage ();
1409 pcbdata_path = g_getenv ("PCBDATA"); /* do not free return value */
1410 if (pcbdata_path != NULL) {
1411 /* If PCBDATA is set, use the value */
1412 m4_pcbdir = g_strconcat (pcbdata_path, "/pcb/m4", NULL);
1413 } else {
1414 /* Use the default value passed in from the configure script
1415 * instead of trying to hard code a value which is very
1416 * likely wrong
1418 m4_pcbdir = g_strconcat (PCBDATADIR, "/pcb/m4", NULL);
1421 default_m4_pcbdir = g_strdup (m4_pcbdir);
1423 get_args (argc, argv);
1425 load_extra_project_files ();
1426 add_default_m4_files ();
1428 if (!schematics)
1429 usage ();
1432 /* Defaults for the search path if not configured in the project file */
1433 if (g_file_test ("packages", G_FILE_TEST_IS_DIR))
1434 element_directory_list = g_list_append (element_directory_list, "packages");
1436 #define PCB_PATH_DELIMETER ":"
1437 if (verbose)
1438 printf ("Processing PCBLIBPATH=\"%s\"\n", PCBLIBPATH);
1440 path = g_strdup (PCBLIBPATH);
1441 for (p = strtok (path, PCB_PATH_DELIMETER); p && *p;
1442 p = strtok (NULL, PCB_PATH_DELIMETER)) {
1443 if (g_file_test (p, G_FILE_TEST_IS_DIR)) {
1444 if (verbose)
1445 printf ("Adding %s to the newlib search path\n", p);
1446 element_directory_list = g_list_append (element_directory_list,
1447 g_strdup (p));
1450 g_free (path);
1452 pins_file_name = g_strconcat (sch_basename, ".cmd", NULL);
1453 net_file_name = g_strconcat (sch_basename, ".net", NULL);
1454 pcb_file_name = g_strconcat (sch_basename, ".pcb", NULL);
1455 bak_file_name = g_strconcat (sch_basename, ".pcb.bak", NULL);
1456 tmp = g_strdup (bak_file_name);
1458 for (i = 0; g_file_test (bak_file_name, G_FILE_TEST_EXISTS); ++i) {
1459 g_free (bak_file_name);
1460 bak_file_name = g_strdup_printf ("%s%d", tmp, i);
1462 g_free (tmp);
1464 if (g_file_test (pcb_file_name, G_FILE_TEST_EXISTS)) {
1465 initial_pcb = FALSE;
1466 pcb_new_file_name = g_strconcat (sch_basename, ".new.pcb", NULL);
1467 get_pcb_element_list (pcb_file_name);
1468 } else
1469 pcb_new_file_name = g_strdup (pcb_file_name);
1471 if (!run_gnetlist (pins_file_name, net_file_name, pcb_new_file_name,
1472 sch_basename, schematics)) {
1473 fprintf(stderr, "Failed to run gnetlist\n");
1474 exit (1);
1477 if (add_elements (pcb_new_file_name) == 0) {
1478 build_and_run_command ("rm %s", pcb_new_file_name);
1479 if (initial_pcb) {
1480 printf ("No elements found, so nothing to do.\n");
1481 exit (0);
1485 if (fix_elements)
1486 update_element_descriptions (pcb_file_name, bak_file_name);
1487 prune_elements (pcb_file_name, bak_file_name);
1489 /* Report work done during processing */
1490 if (verbose)
1491 printf ("\n");
1492 printf ("\n----------------------------------\n");
1493 printf ("Done processing. Work performed:\n");
1494 if (n_deleted > 0 || n_fixed > 0 || need_PKG_purge || n_changed_value > 0)
1495 printf ("%s is backed up as %s.\n", pcb_file_name, bak_file_name);
1496 if (pcb_element_list && n_deleted > 0)
1497 printf ("%d elements deleted from %s.\n", n_deleted, pcb_file_name);
1499 if (n_added_ef + n_added_m4 > 0)
1500 printf ("%d file elements and %d m4 elements added to %s.\n",
1501 n_added_ef, n_added_m4, pcb_new_file_name);
1502 else if (n_not_found == 0) {
1503 printf ("No elements to add so not creating %s\n", pcb_new_file_name);
1504 created_pcb_file = FALSE;
1507 if (n_not_found > 0) {
1508 printf ("%d not found elements added to %s.\n",
1509 n_not_found, pcb_new_file_name);
1511 if (n_unknown > 0)
1512 printf ("%d components had no footprint attribute and are omitted.\n",
1513 n_unknown);
1514 if (n_none > 0)
1515 printf ("%d components with footprint \"none\" omitted from %s.\n",
1516 n_none, pcb_new_file_name);
1517 if (n_empty > 0)
1518 printf ("%d components with empty footprint \"%s\" omitted from %s.\n",
1519 n_empty, empty_footprint_name, pcb_new_file_name);
1520 if (n_changed_value > 0)
1521 printf ("%d elements had a value change in %s.\n",
1522 n_changed_value, pcb_file_name);
1523 if (n_fixed > 0)
1524 printf ("%d elements fixed in %s.\n", n_fixed, pcb_file_name);
1525 if (n_PKG_removed_old > 0) {
1526 printf ("%d elements could not be found.", n_PKG_removed_old);
1527 if (created_pcb_file)
1528 printf (" So %s is incomplete.\n", pcb_file_name);
1529 else
1530 printf ("\n");
1532 if (n_PKG_removed_new > 0) {
1533 printf ("%d elements could not be found.", n_PKG_removed_new);
1534 if (created_pcb_file)
1535 printf (" So %s is incomplete.\n", pcb_new_file_name);
1536 else
1537 printf ("\n");
1539 if (n_preserved > 0)
1540 printf ("%d elements not in the schematic preserved in %s.\n",
1541 n_preserved, pcb_file_name);
1543 /* Tell user what to do next */
1544 if (verbose)
1545 printf ("\n");
1547 if (n_added_ef + n_added_m4 > 0) {
1548 if (initial_pcb) {
1549 printf ("\nNext step:\n");
1550 printf ("1. Run pcb on your file %s.\n", pcb_file_name);
1551 printf
1552 (" You will find all your footprints in a bundle ready for you to place\n");
1553 printf
1554 (" or disperse with \"Select -> Disperse all elements\" in PCB.\n\n");
1555 printf
1556 ("2. From within PCB, select \"File -> Load netlist file\" and select \n");
1557 printf (" %s to load the netlist.\n\n", net_file_name);
1558 printf ("3. From within PCB, enter\n\n");
1559 printf (" :ExecuteFile(%s)\n\n", pins_file_name);
1560 printf
1561 (" to propagate the pin names of all footprints to the layout.\n\n");
1562 } else if (quiet_mode == FALSE) {
1563 printf ("\nNext steps:\n");
1564 printf ("1. Run pcb on your file %s.\n", pcb_file_name);
1565 printf
1566 ("2. From within PCB, select \"File -> Load layout data to paste buffer\"\n");
1567 printf
1568 (" and select %s to load the new footprints into your existing layout.\n",
1569 pcb_new_file_name);
1570 printf
1571 ("3. From within PCB, select \"File -> Load netlist file\" and select \n");
1572 printf (" %s to load the updated netlist.\n\n", net_file_name);
1573 printf ("4. From within PCB, enter\n\n");
1574 printf (" :ExecuteFile(%s)\n\n", pins_file_name);
1575 printf (" to update the pin names of all footprints.\n\n");
1579 g_free (net_file_name);
1580 g_free (pins_file_name);
1581 g_free (pcb_file_name);
1582 g_free (bak_file_name);
1584 return 0;