1 /* gsch2pcb
2 *
3 * Bill Wilson billw@wt.net
4 *
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.
9 *
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.
15 *
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
18 */
19
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <glib.h>
26
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>
33
34 #ifdef HAVE_LIBDMALLOC
35 #include <dmalloc.h>
36 #endif
37
38 #define GSC2PCB_VERSION "1.6"
39
40 #define DEFAULT_PCB_INC "pcb.inc"
41
42 #define SEP_STRING "--------\n"
43
44 typedef struct
45 {
46 gchar *refdes, *value, *description, *changed_description, *changed_value;
47 gchar *flags, *tail;
48 gchar *x, *y;
49 gchar *pkg_name_fix;
50 gchar res_char;
51
52 gboolean still_exists, new_format, hi_res_format, quoted_flags, omit_PKG;
53 }
54 PcbElement;
55
56
57 typedef struct
58 {
59 gchar *part_number, *element_name;
60 }
61 ElementMap;
62
63 static GList *pcb_element_list,
64 *element_directory_list, *extra_gnetlist_list, *extra_gnetlist_arg_list;
65
66 static gchar *sch_basename;
67
68 static GList *schematics;
69
70 static gchar *m4_pcbdir, *default_m4_pcbdir, *m4_files, *m4_override_file;
71
72 static gboolean use_m4 = TRUE;
73
74 static gchar *empty_footprint_name;
75
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;
84
85 static gboolean remove_unfound_elements = TRUE,
86 quiet_mode = FALSE,
87 force_element_files, preserve, fix_elements, bak_done, need_PKG_purge;
88
89
90 static void
create_m4_override_file()91 create_m4_override_file ()
92 {
93 FILE *f;
94
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;
100 }
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");
106
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");
116 }
117 }
118
119 /**
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
129 */
130 static gboolean
build_and_run_command(const gchar * format,...)131 build_and_run_command (const gchar *format, ...)
132 {
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;
144
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);
159 }
160 }
161 va_end (vargs);
162
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 */);
168
169 if (verbose)
170 printf ("Running command:\n\t");
171
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);
176 }
177
178 if (verbose)
179 printf ("\n%s", SEP_STRING);
180
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);
198 }
199 }
200 else {
201 fprintf(stderr, "Failed to execute external program: %s\n", error->message);
202 g_error_free(error);
203 }
204
205 if (verbose)
206 printf ("\n%s", SEP_STRING);
207
208 g_free(standard_error);
209 g_free (standard_output);
210
211 g_free (args);
212 /* free the list, but leave data untouched */
213 g_list_free (tmp);
214 }
215
216 g_strfreev (split);
217
218 return result;
219 }
220
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).
225 */
226 static gboolean
run_gnetlist(gchar * pins_file,gchar * net_file,gchar * pcb_file,gchar * basename,GList * largs)227 run_gnetlist (gchar * pins_file, gchar * net_file, gchar * pcb_file,
228 gchar * basename, GList * largs)
229 {
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;
236
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.
240 */
241 if (gnetlist == NULL)
242 gnetlist = g_getenv ("GNETLIST");
243 if (gnetlist == NULL)
244 gnetlist = "gnetlist";
245
246 if (!verbose)
247 verboseList = g_list_append (verboseList, "-q");
248
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;
256
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 ();
265
266 if (m4_override_file) {
267 args1 = g_list_append (args1, "-m");
268 args1 = g_list_append (args1, m4_override_file);
269 }
270
271 mtime = (stat (pcb_file, &st) == 0) ? st.st_mtime : 0;
272
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
284 );
285 if (m4_override_file)
286 fprintf (stderr,
287 " At least gnetlist 20030901 is required for m4-xxx options.\n");
288 return FALSE;
289 }
290 return FALSE;
291 }
292
293 if (m4_override_file)
294 unlink (m4_override_file);
295
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);
307 }
308
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);
319 }
320
321 g_list_free (args1);
322 g_list_free (verboseList);
323
324 return TRUE;
325 }
326
327 static gchar *
token(gchar * string,gchar ** next,gboolean * quoted_ret)328 token (gchar * string, gchar ** next, gboolean * quoted_ret)
329 {
330 static gchar *str;
331 gchar *s, *ret;
332 gboolean quoted = FALSE;
333
334 if (string)
335 str = string;
336 if (!str || !*str) {
337 if (next)
338 *next = str;
339 return g_strdup ("");
340 }
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);
354 }
355 ret = g_strndup (str, s - str);
356 str = (quoted && *s) ? s + 1 : s;
357 if (next)
358 *next = str;
359 return ret;
360 }
361
362 static gchar *
fix_spaces(gchar * str)363 fix_spaces (gchar * str)
364 {
365 gchar *s;
366
367 if (!str)
368 return NULL;
369 for (s = str; *s; ++s)
370 if (*s == ' ' || *s == '\t')
371 *s = '_';
372 return str;
373 }
374
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)
384 *
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.
389 */
390 PcbElement *
pcb_element_line_parse(gchar * line)391 pcb_element_line_parse (gchar * line)
392 {
393 PcbElement *el = NULL;
394 gchar *s, *t, close_char;
395 gint state = 0, elcount = 0;
396
397 if (strncmp (line, "Element", 7))
398 return NULL;
399
400 el = g_new0 (PcbElement, 1);
401
402 s = line + 7;
403 while (*s == ' ' || *s == '\t')
404 ++s;
405
406 if (*s == '[')
407 el->hi_res_format = TRUE;
408 else if (*s != '(') {
409 g_free (el);
410 return NULL;
411 }
412
413 el->res_char = el->hi_res_format ? '[' : '(';
414 close_char = el->hi_res_format ? ']' : ')';
415
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);
420
421 el->x = token (NULL, NULL, NULL);
422 el->y = token (NULL, &t, NULL);
423
424 el->tail = g_strdup (t ? t : "");
425 if ((s = strrchr (el->tail, (gint) '\n')) != NULL)
426 *s = '\0';
427
428 /* Count the tokens in tail to decide if it's new or old format.
429 * Old format will have 3 tokens, new format will have 5 tokens.
430 */
431 for (s = el->tail; *s && *s != close_char; ++s) {
432 if (*s != ' ') {
433 if (state == 0)
434 ++elcount;
435 state = 1;
436 } else
437 state = 0;
438 }
439 if (elcount > 4)
440 el->new_format = TRUE;
441
442 fix_spaces (el->description);
443 fix_spaces (el->refdes);
444 fix_spaces (el->value);
445
446 /* Don't allow elements with no refdes to ever be deleted because
447 * they may be desired pc board elements not in schematics. So
448 * initialize still_exists to TRUE if empty or non-alphanumeric
449 * refdes.
450 */
451 if (!*el->refdes || !isalnum ((gint) (*el->refdes)))
452 el->still_exists = TRUE;
453
454 return el;
455 }
456
457 static void
pcb_element_free(PcbElement * el)458 pcb_element_free (PcbElement * el)
459 {
460 if (!el)
461 return;
462 g_free (el->flags);
463 g_free (el->description);
464 g_free (el->changed_description);
465 g_free (el->changed_value);
466 g_free (el->refdes);
467 g_free (el->value);
468 g_free (el->x);
469 g_free (el->y);
470 g_free (el->tail);
471 g_free (el->pkg_name_fix);
472 g_free (el);
473 }
474
475 static void
get_pcb_element_list(gchar * pcb_file)476 get_pcb_element_list (gchar * pcb_file)
477 {
478 FILE *f;
479 PcbElement *el;
480 gchar *s, buf[1024];
481
482 if ((f = fopen (pcb_file, "r")) == NULL)
483 return;
484 while ((fgets (buf, sizeof (buf), f)) != NULL) {
485 for (s = buf; *s == ' ' || *s == '\t'; ++s);
486 if (!strncmp (s, "PKG_", 4)) {
487 need_PKG_purge = TRUE;
488 continue;
489 }
490 if ((el = pcb_element_line_parse (s)) == NULL)
491 continue;
492 pcb_element_list = g_list_append (pcb_element_list, el);
493 }
494 fclose (f);
495 }
496
497 static PcbElement *
pcb_element_exists(PcbElement * el_test,gboolean record)498 pcb_element_exists (PcbElement * el_test, gboolean record)
499 {
500 GList *list;
501 PcbElement *el;
502
503 for (list = pcb_element_list; list; list = g_list_next (list)) {
504 el = (PcbElement *) list->data;
505
506 if (strcmp (el_test->refdes, el->refdes))
507 continue;
508 if (strcmp (el_test->description, el->description)) { /* footprint */
509 if (record)
510 el->changed_description = g_strdup (el_test->description);
511 } else {
512 if (record) {
513 if (strcmp (el_test->value, el->value))
514 el->changed_value = g_strdup (el_test->value);
515 el->still_exists = TRUE;
516 }
517 return el;
518 }
519 }
520 return NULL;
521 }
522
523 /* A problem is that new PCB 1.7 file elements have the
524 * (mark_x,mark_y) value set to wherever the element was created and
525 * no equivalent of a gschem translate symbol was done.
526 *
527 * So, file elements inserted can be scattered over a big area and
528 * this is bad when loading a file.new.pcb into an existing PC
529 * board. So, do a simple translate if (mark_x,mark_y) is
530 * (arbitrarily) over 1000. I'll assume that for values < 1000 the
531 * element creator was concerned with a sane initial element
532 * placement. Unless someone has a better idea? Don't bother with
533 * pre PCB 1.7 formats as that would require parsing the mark().
534 * Current m4 elements use the old format but they seem to have a
535 * reasonable initial mark().
536 */
537 static void
simple_translate(PcbElement * el)538 simple_translate (PcbElement * el)
539 {
540
541 el->x=strdup("0");
542 el->y=strdup("0");
543 }
544
545 static gboolean
insert_element(FILE * f_out,gchar * element_file,gchar * footprint,gchar * refdes,gchar * value)546 insert_element (FILE * f_out, gchar * element_file,
547 gchar * footprint, gchar * refdes, gchar * value)
548 {
549 FILE *f_in;
550 PcbElement *el;
551 gchar *fmt, *s, buf[1024];
552 gboolean retval = FALSE;
553
554 if ((f_in = fopen (element_file, "r")) == NULL) {
555 s = g_strdup_printf ("insert_element() can't open %s", element_file);
556 perror (s);
557 g_free (s);
558 return FALSE;
559 }
560 /* Scan the file to detect whether it's actually a PCB
561 * layout. Assumes that a PCB layout will have a "PCB" line. */
562 while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
563 for (s = buf; *s == ' ' || *s == '\t'; ++s);
564 s[3] = 0; /* Truncate line */
565 if (strncmp ("PCB", s, sizeof (buf)) == 0) {
566 printf ("Warning: %s appears to be a PCB layout file. Skipping.\n",
567 element_file);
568 fclose (f_in);
569 return FALSE;
570 }
571 }
572 rewind (f_in);
573
574 /* Copy the file element lines. Substitute new parameters into the
575 * Element() or Element[] line and strip comments.
576 */
577 while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
578 for (s = buf; *s == ' ' || *s == '\t'; ++s);
579 if ((el = pcb_element_line_parse (s)) != NULL) {
580 simple_translate (el);
581 fmt = el->quoted_flags ?
582 "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" :
583 "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n";
584
585 fprintf (f_out, fmt,
586 el->res_char, el->flags, footprint, refdes, value,
587 el->x, el->y, el->tail);
588 retval = TRUE;
589 } else if (*s != '#')
590 fputs (buf, f_out);
591 pcb_element_free (el);
592 }
593 fclose (f_in);
594 return retval;
595 }
596
597
598 gchar *
find_element(gchar * dir_path,gchar * element)599 find_element (gchar * dir_path, gchar * element)
600 {
601 GDir *dir;
602 gchar *path, *name, *s, *found = NULL;
603
604 if ((dir = g_dir_open (dir_path, 0, NULL)) == NULL) {
605 s = g_strdup_printf ("find_element can't open dir \"%s\"", dir_path);
606 perror (s);
607 g_free (s);
608 return NULL;
609 }
610 if (verbose > 1)
611 printf ("\t Searching: \"%s\" for \"%s\"\n", dir_path, element);
612 while ((name = (gchar *) g_dir_read_name (dir)) != NULL) {
613 path = g_strconcat (dir_path, "/", name, NULL);
614 found = NULL;
615
616 /* if we got a directory name, then recurse down into it */
617 if (g_file_test (path, G_FILE_TEST_IS_DIR))
618 found = find_element (path, element);
619
620 /* otherwise assume it is a file and see if it is the one we want */
621 else {
622 if (verbose > 1)
623 printf ("\t : %s\t", name);
624 if (!strcmp (name, element))
625 found = g_strdup (path);
626 else {
627 gchar *tmps;
628 tmps = g_strconcat (element, ".fp", NULL);
629 if (!strcmp (name, tmps))
630 found = g_strdup (path);
631 g_free (tmps);
632 }
633 if (verbose > 1)
634 printf ("%s\n", found ? "Yes" : "No");
635 }
636 g_free (path);
637 if (found)
638 break;
639 }
640 g_dir_close (dir);
641 return found;
642 }
643
644 gchar *
search_element_directories(PcbElement * el)645 search_element_directories (PcbElement * el)
646 {
647 GList *list;
648 gchar *s, *elname = NULL, *dir_path, *path = NULL;
649 gint n1, n2;
650
651 /* See comment before pkg_to_element() */
652 if (el->pkg_name_fix) {
653 if (strchr (el->description, '-')) {
654 n1 = strlen (el->description);
655 n2 = strlen (el->pkg_name_fix);
656 s = el->description + n1 - n2 - 1;
657
658 // printf("n1=%d n2=%d desc:%s fix:%s s:%s\n",
659 // n1, n2, el->description, el->pkg_name_fix, s);
660
661 if (n1 > 0 && n2 < n1 && *s == '-' && *(s + 1) == *el->pkg_name_fix) {
662 s = g_strndup (el->description, n1 - n2 - 1);
663 elname = g_strconcat (s, " ", el->pkg_name_fix, NULL);
664 g_free (s);
665 }
666 }
667 if (!elname) {
668 printf ("Warning: argument passing may have been confused by\n");
669 printf (" a comma in a component value:\n");
670 printf (" Check %s %s %s\n",
671 el->refdes, el->description, el->value);
672 printf (" Maybe just use a space instead of a comma?\n");
673 }
674 }
675 if (!elname)
676 elname = g_strdup (el->description);
677
678 if (!strcmp (elname, "unknown")) {
679 g_free (elname);
680 return NULL;
681 }
682 if (verbose > 1)
683 printf ("\tSearching directories looking for file element: %s\n", elname);
684 for (list = element_directory_list; list; list = g_list_next (list)) {
685 dir_path = (gchar *) list->data;
686 if (verbose > 1)
687 printf ("\tLooking in directory: \"%s\"\n", dir_path);
688 path = find_element (dir_path, elname);
689 if (path) {
690 if (verbose)
691 printf ("\tFound: %s\n", path);
692 break;
693 }
694 }
695 g_free (elname);
696 return path;
697 }
698
699 /* The gnetlist backend gnet-gsch2pcb.scm generates PKG_ lines:
700 *
701 * PKG_footprint(footprint{-fp0-fp1},refdes,value{,fp0,fp1})
702 *
703 * where fp1 and fp2 (if they exist) are the extra footprint
704 * components when specifying footprints like "DIL 14 300". This is
705 * needed for m4 macros.
706 *
707 * A complication is if the footprint references a file element with
708 * spaces embedded in the name. The gnetlist backend will interpret
709 * these as fp0, fp1, ... args and the footprint will in this case
710 * incorrectly have '-' inserted where the spaces should be. So, if
711 * there are additional args, reconstruct the portion of the name
712 * given by the args with spaces for later use. Eg. if the footprint
713 * is "100 Pin jack", we will have
714 *
715 * PKG_100-Pin-jack(100-Pin-jack,refdes,value,Pin,jack)
716 *
717 * So put "Pin jack" into pkg_name_fix so if this element is searched
718 * as a file element we can munge the description to what it should
719 * be, eg:
720 *
721 * 100-Pin-jack -> 100 Pin jack
722 */
723 static PcbElement *
pkg_to_element(FILE * f,gchar * pkg_line)724 pkg_to_element (FILE * f, gchar * pkg_line)
725 {
726 PcbElement *el;
727 gchar **args, *s;
728 gint n, n_extra_args, n_dashes;
729
730 if (strncmp (pkg_line, "PKG_", 4)
731 || (s = strchr (pkg_line, (gint) '(')) == NULL)
732 return NULL;
733
734 args = g_strsplit (s + 1, ",", 12);
735 if (!args[0] || !args[1] || !args[2]) {
736 fprintf (stderr, "Bad package line: %s\n", pkg_line);
737 return NULL;
738 }
739 fix_spaces (args[0]);
740 fix_spaces (args[1]);
741 fix_spaces (args[2]);
742
743 el = g_new0 (PcbElement, 1);
744 el->description = g_strdup (args[0]);
745 el->refdes = g_strdup (args[1]);
746 el->value = g_strdup (args[2]);
747 if ((s = strchr (el->value, (gint) ')')) != NULL)
748 *s = '\0';
749
750 /* If the component value has a comma, eg "1k, 1%", the gnetlist generated
751 * PKG line will be
752 *
753 * PKG_XXX(`R0w8',`R100',`1k, 1%'),
754 *
755 * but after processed by m4, the input to gsch2pcb will be
756 *
757 * PKG_XXX(R0w8,R100,1k, 1%).
758 *
759 * So the quoting info has been lost when processing for file
760 * elements. So here try to detect and fix this. But I can't
761 * handle the situation where the description has a '-' and the
762 * value has a comma because gnet-gsch2pcb.scm munges the
763 * description with '-' when there are extra args.
764 */
765 for (n_extra_args = 0; args[3 + n_extra_args] != NULL; ++n_extra_args);
766 s = el->description;
767 for (n_dashes = 0; (s = strchr (s + 1, '-')) != NULL; ++n_dashes);
768
769 n = 3;
770 if (n_extra_args == n_dashes + 1) { /* Assume there was a comma in the value, eg "1K, 1%" */
771 s = el->value;
772 el->value = g_strconcat (s, ",", fix_spaces (args[n]), NULL);
773 g_free (s);
774 if ((s = strchr (el->value, (gint) ')')) != NULL)
775 *s = '\0';
776 n = 4;
777 }
778 if (args[n]) {
779 el->pkg_name_fix = g_strdup (args[n]);
780 for (n += 1; args[n] != NULL; ++n) {
781 s = el->pkg_name_fix;
782 el->pkg_name_fix = g_strconcat (s, " ", args[n], NULL);
783 g_free (s);
784 }
785 if ((s = strchr (el->pkg_name_fix, (gint) ')')) != NULL)
786 *s = '\0';
787 }
788 g_strfreev (args);
789
790 if (empty_footprint_name && !strcmp (el->description, empty_footprint_name)) {
791 if (verbose)
792 printf
793 ("%s: has the empty footprint attribute \"%s\" so won't be in the layout.\n",
794 el->refdes, el->description);
795 n_empty += 1;
796 el->omit_PKG = TRUE;
797 } else if (!strcmp (el->description, "none")) {
798 fprintf (stderr,
799 "WARNING: %s has a footprint attribute \"%s\" so won't be in the layout.\n",
800 el->refdes, el->description);
801 n_none += 1;
802 el->omit_PKG = TRUE;
803 } else if (!strcmp (el->description, "unknown")) {
804 fprintf (stderr,
805 "WARNING: %s has no footprint attribute so won't be in the layout.\n",
806 el->refdes);
807 n_unknown += 1;
808 el->omit_PKG = TRUE;
809 }
810 return el;
811 }
812
813 /* Process the newly created pcb file which is the output from
814 * gnetlist -g gsch2pcb ...
815 *
816 * It will have elements found via the m4 interface and PKG_ lines for
817 * elements not found. Insert pcb file elements for PKG_ lines if
818 * file elements can be found. If there was an existing pcb file,
819 * strip out any elements if they are already present so that the new
820 * pcb file will only have new elements.
821 */
822 static gint
add_elements(gchar * pcb_file)823 add_elements (gchar * pcb_file)
824 {
825 FILE *f_in, *f_out;
826 PcbElement *el = NULL;
827 gchar *command, *p, *tmp_file, *s, buf[1024];
828 gint total, paren_level = 0;
829 gboolean is_m4, skipping = FALSE;
830
831 if ((f_in = fopen (pcb_file, "r")) == NULL)
832 return 0;
833 tmp_file = g_strconcat (pcb_file, ".tmp", NULL);
834 if ((f_out = fopen (tmp_file, "wb")) == NULL) {
835 fclose (f_in);
836 g_free (tmp_file);
837 return 0;
838 }
839 while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
840 for (s = buf; *s == ' ' || *s == '\t'; ++s);
841 if (skipping) {
842 if (*s == '(')
843 ++paren_level;
844 else if (*s == ')' && --paren_level <= 0)
845 skipping = FALSE;
846 continue;
847 }
848 is_m4 = FALSE;
849 if ((el = pcb_element_line_parse (s)) != NULL)
850 is_m4 = TRUE;
851 else
852 el = pkg_to_element (f_out, s);
853 if (el && pcb_element_exists (el, TRUE)) {
854 skipping = is_m4;
855 pcb_element_free (el);
856 continue;
857 }
858 if (!el || el->omit_PKG) {
859 if (el) {
860
861 } else
862 fputs (buf, f_out);
863 continue;
864 }
865 if (!is_m4 || (is_m4 && force_element_files)) {
866 if (verbose && !is_m4)
867 printf ("%s: need new file element for footprint %s (value=%s)\n",
868 el->refdes, el->description, el->value);
869 if (verbose && is_m4 && force_element_files)
870 printf
871 ("%s: have m4 element %s, but trying to replace with a file element.\n",
872 el->refdes, el->description);
873 p = search_element_directories (el);
874 if (!p && verbose && is_m4 && force_element_files)
875 printf ("\tNo file element found.\n");
876
877 if (p && insert_element (f_out, p,
878 el->description, el->refdes, el->value)) {
879 skipping = is_m4;
880 is_m4 = FALSE;
881 ++n_added_ef;
882 if (verbose)
883 printf ("%s: added new file element for footprint %s (value=%s)\n",
884 el->refdes, el->description, el->value);
885 } else if (!is_m4) {
886 fprintf (stderr,
887 "%s: can't find PCB element for footprint %s (value=%s)\n",
888 el->refdes, el->description, el->value);
889 if (remove_unfound_elements && !fix_elements) {
890 fprintf (stderr,
891 "So device %s will not be in the layout.\n", el->refdes);
892 ++n_PKG_removed_new;
893 } else {
894 ++n_not_found;
895 fputs (buf, f_out); /* Copy PKG_ line */
896 }
897 }
898 g_free (p);
899 }
900 if (is_m4) {
901 fputs (buf, f_out);
902 ++n_added_m4;
903 if (verbose)
904 printf ("%s: added new m4 element for footprint %s (value=%s)\n",
905 el->refdes, el->description, el->value);
906 }
907 pcb_element_free (el);
908 if (verbose)
909 printf ("----\n");
910 }
911 fclose (f_in);
912 fclose (f_out);
913
914 total = n_added_ef + n_added_m4 + n_not_found;
915 if (total == 0)
916 build_and_run_command ("rm %s", tmp_file);
917 else
918 build_and_run_command ("mv %s %s", tmp_file, pcb_file);
919 g_free (tmp_file);
920 return total;
921 }
922
923 static void
update_element_descriptions(gchar * pcb_file,gchar * bak)924 update_element_descriptions (gchar * pcb_file, gchar * bak)
925 {
926 FILE *f_in, *f_out;
927 GList *list;
928 PcbElement *el, *el_exists;
929 gchar *fmt, *command, *tmp, *s, buf[1024];
930
931 for (list = pcb_element_list; list; list = g_list_next (list)) {
932 el = (PcbElement *) list->data;
933 if (el->changed_description)
934 ++n_fixed;
935 }
936 if (!pcb_element_list || n_fixed == 0) {
937 fprintf (stderr, "Could not find any elements to fix.\n");
938 return;
939 }
940 if ((f_in = fopen (pcb_file, "r")) == NULL)
941 return;
942 tmp = g_strconcat (pcb_file, ".tmp", NULL);
943 if ((f_out = fopen (tmp, "wb")) == NULL) {
944 fclose (f_in);
945 return;
946 }
947 while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
948 for (s = buf; *s == ' ' || *s == '\t'; ++s);
949 if ((el = pcb_element_line_parse (s)) != NULL
950 && (el_exists = pcb_element_exists (el, FALSE)) != NULL
951 && el_exists->changed_description) {
952 fmt = el->quoted_flags ?
953 "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" :
954 "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n";
955 fprintf (f_out, fmt,
956 el->res_char,
957 el->flags, el_exists->changed_description,
958 el->refdes, el->value, el->x, el->y, el->tail);
959 printf ("%s: updating element Description: %s -> %s\n",
960 el->refdes, el->description, el_exists->changed_description);
961 el_exists->still_exists = TRUE;
962 } else
963 fputs (buf, f_out);
964 pcb_element_free (el);
965 }
966 fclose (f_in);
967 fclose (f_out);
968
969 if (!bak_done) {
970 build_and_run_command ("mv %s %s", pcb_file, bak);
971 bak_done = TRUE;
972 }
973
974 build_and_run_command ("mv %s %s", tmp, pcb_file);
975 g_free (tmp);
976 }
977
978 static void
prune_elements(gchar * pcb_file,gchar * bak)979 prune_elements (gchar * pcb_file, gchar * bak)
980 {
981 FILE *f_in, *f_out;
982 GList *list;
983 PcbElement *el, *el_exists;
984 gchar *fmt, *command, *tmp, *s, buf[1024];
985 gint paren_level = 0;
986 gboolean skipping = FALSE;
987
988 for (list = pcb_element_list; list; list = g_list_next (list)) {
989 el = (PcbElement *) list->data;
990 if (!el->still_exists) {
991 if (preserve) {
992 ++n_preserved;
993 fprintf (stderr,
994 "Preserving PCB element not in the schematic: %s (element %s)\n",
995 el->refdes, el->description);
996 } else
997 ++n_deleted;
998 } else if (el->changed_value)
999 ++n_changed_value;
1000 }
1001 if (!pcb_element_list
1002 || (n_deleted == 0 && !need_PKG_purge && n_changed_value == 0)
1003 )
1004 return;
1005 if ((f_in = fopen (pcb_file, "r")) == NULL)
1006 return;
1007 tmp = g_strconcat (pcb_file, ".tmp", NULL);
1008 if ((f_out = fopen (tmp, "wb")) == NULL) {
1009 fclose (f_in);
1010 return;
1011 }
1012 while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
1013 for (s = buf; *s == ' ' || *s == '\t'; ++s);
1014 if (skipping) {
1015 if (*s == '(')
1016 ++paren_level;
1017 else if (*s == ')' && --paren_level <= 0)
1018 skipping = FALSE;
1019 continue;
1020 }
1021 el_exists = NULL;
1022 if ((el = pcb_element_line_parse (s)) != NULL
1023 && (el_exists = pcb_element_exists (el, FALSE)) != NULL
1024 && !el_exists->still_exists && !preserve) {
1025 skipping = TRUE;
1026 if (verbose)
1027 printf ("%s: deleted element %s (value=%s)\n",
1028 el->refdes, el->description, el->value);
1029 pcb_element_free (el);
1030 continue;
1031 }
1032 if (el_exists && el_exists->changed_value) {
1033 fmt = el->quoted_flags ?
1034 "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" :
1035 "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n";
1036 fprintf (f_out, fmt,
1037 el->res_char, el->flags, el->description, el->refdes,
1038 el_exists->changed_value, el->x, el->y, el->tail);
1039 if (verbose)
1040 printf ("%s: changed element %s value: %s -> %s\n",
1041 el->refdes, el->description,
1042 el->value, el_exists->changed_value);
1043 } else if (!strncmp (s, "PKG_", 4))
1044 ++n_PKG_removed_old;
1045 else
1046 fputs (buf, f_out);
1047 pcb_element_free (el);
1048 }
1049 fclose (f_in);
1050 fclose (f_out);
1051
1052 if (!bak_done) {
1053 build_and_run_command ("mv %s %s", pcb_file, bak);
1054 bak_done = TRUE;
1055 }
1056
1057 build_and_run_command ("mv %s %s", tmp, pcb_file);
1058 g_free (tmp);
1059 }
1060
1061 static void
add_m4_file(gchar * arg)1062 add_m4_file (gchar * arg)
1063 {
1064 gchar *s;
1065
1066 if (!m4_files)
1067 m4_files = g_strdup (arg);
1068 else {
1069 s = m4_files;
1070 m4_files = g_strconcat (m4_files, " ", arg, NULL);
1071 g_free (s);
1072 }
1073 }
1074
1075 static gchar *
expand_dir(gchar * dir)1076 expand_dir (gchar * dir)
1077 {
1078 gchar *s;
1079
1080 if (*dir == '~')
1081 s = g_build_filename ((gchar *) g_get_home_dir (), dir + 1, NULL);
1082 else
1083 s = g_strdup (dir);
1084 return s;
1085 }
1086
1087 static void
add_default_m4_files(void)1088 add_default_m4_files (void)
1089 {
1090 gchar *path;
1091
1092 path = g_build_filename ((gchar *) g_get_home_dir (),
1093 ".pcb", DEFAULT_PCB_INC, NULL);
1094 if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
1095 add_m4_file (path);
1096 g_free (path);
1097
1098 if (g_file_test (DEFAULT_PCB_INC, G_FILE_TEST_IS_REGULAR))
1099 add_m4_file (DEFAULT_PCB_INC);
1100
1101 }
1102
1103 static void
add_schematic(gchar * sch)1104 add_schematic (gchar * sch)
1105 {
1106 const gchar* s;
1107 schematics = g_list_append (schematics, g_strdup (sch));
1108 if (!sch_basename && (s = g_strrstr (sch, ".sch")) != NULL && strlen(s) == 4)
1109 sch_basename = g_strndup (sch, s - sch);
1110 }
1111
1112 static void
add_multiple_schematics(gchar * sch)1113 add_multiple_schematics (gchar * sch)
1114 {
1115 /* parse the string using shell semantics */
1116 gint count;
1117 gchar** args = NULL;
1118 GError* error = NULL;
1119
1120 if (g_shell_parse_argv (sch, &count, &args, &error)) {
1121 int i;
1122 for (i = 0; i < count; ++i)
1123 {
1124 add_schematic (args[i]);
1125 }
1126 g_strfreev (args);
1127 } else {
1128 fprintf (stderr,
1129 "invalid `schematics' option: %s\n",
1130 error->message);
1131 g_error_free (error);
1132 }
1133 }
1134
1135 static gint
parse_config(gchar * config,gchar * arg)1136 parse_config (gchar * config, gchar * arg)
1137 {
1138 gchar *s;
1139
1140 /* remove trailing white space otherwise strange things can happen */
1141 if ((arg != NULL) && (strlen (arg) >= 1)) {
1142 s = arg + strlen (arg) - 1;
1143 while ((*s == ' ' || *s == '\t') && (s != arg))
1144 s--;
1145 s++;
1146 *s = '\0';
1147 }
1148 if (verbose)
1149 printf (" %s \"%s\"\n", config, arg ? arg : "");
1150
1151 if (!strcmp (config, "remove-unfound") || !strcmp (config, "r")) {
1152 /* This is default behavior set in header section */
1153 remove_unfound_elements = TRUE;
1154 return 0;
1155 }
1156 if (!strcmp (config, "keep-unfound") || !strcmp (config, "k")) {
1157 remove_unfound_elements = FALSE;
1158 return 0;
1159 }
1160 if (!strcmp (config, "quiet") || !strcmp (config, "q")) {
1161 quiet_mode = TRUE;
1162 return 0;
1163 }
1164 if (!strcmp (config, "preserve") || !strcmp (config, "p")) {
1165 preserve = TRUE;
1166 return 0;
1167 }
1168 if (!strcmp (config, "use-files") || !strcmp (config, "f")) {
1169 force_element_files = TRUE;
1170 return 0;
1171 }
1172 if (!strcmp (config, "skip-m4") || !strcmp (config, "s")) {
1173 use_m4 = FALSE;
1174 return 0;
1175 }
1176 if (!strcmp (config, "elements-dir") || !strcmp (config, "d")) {
1177 if (verbose > 1)
1178 printf ("\tAdding directory to file element directory list: %s\n",
1179 expand_dir (arg));
1180 element_directory_list =
1181 g_list_prepend (element_directory_list, expand_dir (arg));
1182 } else if (!strcmp (config, "output-name") || !strcmp (config, "o"))
1183 sch_basename = g_strdup (arg);
1184 else if (!strcmp (config, "schematics"))
1185 add_multiple_schematics (arg);
1186 else if (!strcmp (config, "m4-pcbdir")) {
1187 g_free (m4_pcbdir);
1188 m4_pcbdir = g_strdup (arg);
1189 } else if (!strcmp (config, "m4-file"))
1190 add_m4_file (arg);
1191 else if (!strcmp (config, "gnetlist"))
1192 extra_gnetlist_list = g_list_append (extra_gnetlist_list, g_strdup (arg));
1193 else if (!strcmp (config, "empty-footprint"))
1194 empty_footprint_name = g_strdup (arg);
1195 else
1196 return -1;
1197
1198 return 1;
1199 }
1200
1201 static void
load_project(gchar * path)1202 load_project (gchar * path)
1203 {
1204 FILE *f;
1205 gchar *s, buf[1024], config[32], arg[768];
1206
1207 f = fopen (path, "r");
1208 if (!f)
1209 return;
1210 if (verbose)
1211 printf ("Reading project file: %s\n", path);
1212 while (fgets (buf, sizeof (buf), f)) {
1213 for (s = buf; *s == ' ' || *s == '\t' || *s == '\n'; ++s);
1214 if (!*s || *s == '#' || *s == '/' || *s == ';')
1215 continue;
1216 arg[0] = '\0';
1217 sscanf (s, "%31s %767[^\n]", config, arg);
1218 parse_config (config, arg);
1219 }
1220 fclose (f);
1221 }
1222
1223 static void
load_extra_project_files(void)1224 load_extra_project_files (void)
1225 {
1226 gchar *path;
1227 static gboolean done = FALSE;
1228
1229 if (done)
1230 return;
1231
1232 load_project ("/etc/gsch2pcb");
1233 load_project ("/usr/local/etc/gsch2pcb");
1234
1235 path = g_build_filename ((gchar *) g_get_home_dir (), ".gEDA",
1236 "gsch2pcb", NULL);
1237 load_project (path);
1238 g_free (path);
1239
1240 done = TRUE;
1241 }
1242
1243 static gchar *usage_string0 =
1244 "usage: gsch2pcb [options] {project | foo.sch [foo1.sch ...]}\n"
1245 "\n"
1246 "Generate a PCB layout file from a set of gschem schematics.\n"
1247 " gnetlist -g PCB is run to generate foo.net from the schematics.\n"
1248 "\n"
1249 " gnetlist -g gsch2pcb is run to get PCB m4 derived elements which\n"
1250 " match schematic footprints. For schematic footprints which don't match\n"
1251 " any PCB m4 layout elements, search a set of file element directories in\n"
1252 " an attempt to find matching PCB file elements.\n"
1253 " Output to foo.pcb if it doesn't exist. If there is a current foo.pcb,\n"
1254 " output only new elements to foo.new.pcb.\n"
1255 " If any elements with a non-empty element name in the current foo.pcb\n"
1256 " have no matching schematic component, then remove those elements from\n"
1257 " foo.pcb and rename foo.pcb to a foo.pcb.bak sequence.\n"
1258 "\n"
1259 " gnetlist -g pcbpins is run to get a PCB actions file which will rename all\n"
1260 " of the pins in a .pcb file to match pin names from the schematic.\n"
1261 "\n"
1262 " \"project\" is a file (not ending in .sch) containing a list of\n"
1263 " schematics to process and some options. A schematics line is like:\n"
1264 " schematics foo1.sch foo2.sch ...\n"
1265 " Options in a project file are like command line args without the \"-\":\n"
1266 " output-name myproject\n"
1267 "\n"
1268 "options (may be included in a project file):\n"
1269 " -d, --elements-dir D Search D for PCB file elements. These defaults\n"
1270 " are searched if they exist: ./packages,\n"
1271 " /usr/local/share/pcb/newlib, /usr/share/pcb/newlib,\n"
1272 " (old pcb) /usr/local/lib/pcb_lib, /usr/lib/pcb_lib,\n"
1273 " (old pcb) /usr/local/pcb_lib\n"
1274 " -o, --output-name N Use output file names N.net, N.pcb, and N.new.pcb\n"
1275 " instead of foo.net, ... where foo is the basename\n"
1276 " of the first command line .sch file.\n"
1277 " -f, --use-files Force using file elements over m4 PCB elements\n"
1278 " for new footprints even though m4 elements are\n"
1279 " searched for first and may have been found.\n"
1280 " -r, --remove-unfound Don't include references to unfound elements in\n"
1281 " the generated .pcb files. Use if you want PCB to\n"
1282 " be able to load the (incomplete) .pcb file.\n"
1283 " This is the default behavior.\n"
1284 " -k, --keep-unfound Keep include references to unfound elements in\n"
1285 " the generated .pcb files. Use if you want to hand\n"
1286 " edit or otherwise preprocess the generated .pcb file\n"
1287 " before running pcb.\n"
1288 " -p, --preserve Preserve elements in PCB files which are not found\n"
1289 " in the schematics. Note that elements with an empty\n"
1290 " element name (schematic refdes) are never deleted,\n"
1291 " so you really shouldn't need this option.\n"
1292 " -q, --quiet Don't tell the user what to do next after running gsch2pcb.\n"
1293 "\n"
1294 " -s, --skip-m4 Skip m4 when looking for footprints. The default is to use\n"
1295 " m4 (which is what previous versions did).\n"
1296 " --m4-file F.inc Use m4 file F.inc in addition to the default m4\n"
1297 " files ./pcb.inc and ~/.pcb/pcb.inc.\n"
1298 " --m4-pcbdir D Use D as the PCB m4 files install directory\n"
1299 " instead of the default:\n";
1300
1301 static gchar *usage_string1 =
1302 " --gnetlist backend A convenience run of extra gnetlist -g commands.\n"
1303 " Example: gnetlist partslist3\n"
1304 " Creates: myproject.partslist3\n"
1305 " --empty-footprint name See the project.sample file.\n"
1306 "\n"
1307 "options (not recognized in a project file):\n"
1308 " --gnetlist-arg arg Allows additional arguments to be passed to gnetlist.\n"
1309 " --fix-elements If a schematic component footprint is not equal\n"
1310 " to its PCB element Description, update the\n"
1311 " Description instead of replacing the element.\n"
1312 " Do this the first time gsch2pcb is used with\n"
1313 " PCB files originally created with gschem2pcb.\n"
1314 " -v, --verbose Use -v -v for additional file element debugging.\n"
1315 " -V, --version\n\n"
1316 "environment variables:\n"
1317 " GNETLIST If set, this specifies the name of the gnetlist program\n"
1318 " to execute.\n"
1319 "\n"
1320 "Additional Resources:\n"
1321 "\n"
1322 " gnetlist user guide: http://geda.seul.org/wiki/geda:gnetlist_ug\n"
1323 " gEDA homepage: http://www.gpleda.org\n"
1324 " PCB homepage: http://pcb.gpleda.org\n" "\n";
1325
1326 static void
usage()1327 usage ()
1328 {
1329 puts (usage_string0);
1330 printf (" %s\n", default_m4_pcbdir);
1331 puts (usage_string1);
1332 exit (0);
1333 }
1334
1335 static void
get_args(gint argc,gchar ** argv)1336 get_args (gint argc, gchar ** argv)
1337 {
1338 gchar *opt, *arg, *s;
1339 gint i, r;
1340
1341 for (i = 1; i < argc; ++i) {
1342 opt = argv[i];
1343 arg = argv[i + 1];
1344 if (*opt == '-') {
1345 ++opt;
1346 if (*opt == '-')
1347 ++opt;
1348 if (!strcmp (opt, "version") || !strcmp (opt, "V")) {
1349 printf ("gsch2pcb %s\n", GSC2PCB_VERSION);
1350 exit (0);
1351 } else if (!strcmp (opt, "verbose") || !strcmp (opt, "v")) {
1352 verbose += 1;
1353 continue;
1354 } else if (!strcmp (opt, "fix-elements")) {
1355 fix_elements = TRUE;
1356 continue;
1357 } else if (!strcmp (opt, "gnetlist-arg")) {
1358 extra_gnetlist_arg_list =
1359 g_list_append (extra_gnetlist_arg_list, g_strdup (arg));
1360 i++;
1361 continue;
1362 } else if (!strcmp (opt, "help") || !strcmp (opt, "h"))
1363 usage ();
1364 else if (i < argc
1365 && ((r = parse_config (opt, (i < argc - 1) ? arg : NULL))
1366 >= 0)
1367 ) {
1368 i += r;
1369 continue;
1370 }
1371 printf ("gsch2pcb: bad or incomplete arg: %s\n", argv[i]);
1372 usage ();
1373 } else {
1374 if (!g_str_has_suffix (argv[i], ".sch")) {
1375 load_extra_project_files ();
1376 load_project (argv[i]);
1377 } else
1378 add_schematic (argv[i]);
1379 }
1380 }
1381 }
1382
1383 gint
main(gint argc,gchar ** argv)1384 main (gint argc, gchar ** argv)
1385 {
1386 gchar *pcb_file_name,
1387 *pcb_new_file_name, *bak_file_name, *pins_file_name, *net_file_name, *tmp;
1388 gint i;
1389 gboolean initial_pcb = TRUE;
1390 gboolean created_pcb_file = TRUE;
1391 char *path, *p;
1392 const char *pcbdata_path;
1393
1394 if (argc < 2)
1395 usage ();
1396
1397 pcbdata_path = g_getenv ("PCBDATA"); /* do not free return value */
1398 if (pcbdata_path != NULL) {
1399 /* If PCBDATA is set, use the value */
1400 m4_pcbdir = g_strconcat (pcbdata_path, "/m4", NULL);
1401 } else {
1402 /* Use the default value passed in from the configure script
1403 * instead of trying to hard code a value which is very
1404 * likely wrong
1405 */
1406 m4_pcbdir = g_strconcat (PCBDATADIR, "/m4", NULL);
1407 }
1408
1409 default_m4_pcbdir = g_strdup (m4_pcbdir);
1410
1411 get_args (argc, argv);
1412
1413 load_extra_project_files ();
1414 add_default_m4_files ();
1415
1416 if (!schematics)
1417 usage ();
1418
1419
1420 /* Defaults for the search path if not configured in the project file */
1421 if (g_file_test ("packages", G_FILE_TEST_IS_DIR))
1422 element_directory_list = g_list_append (element_directory_list, "packages");
1423
1424 #define PCB_PATH_DELIMETER ":"
1425 if (verbose)
1426 printf ("Processing PCBLIBPATH=\"%s\"\n", PCBLIBPATH);
1427
1428 path = g_strdup (PCBLIBPATH);
1429 for (p = strtok (path, PCB_PATH_DELIMETER); p && *p;
1430 p = strtok (NULL, PCB_PATH_DELIMETER)) {
1431 if (g_file_test (p, G_FILE_TEST_IS_DIR)) {
1432 if (verbose)
1433 printf ("Adding %s to the newlib search path\n", p);
1434 element_directory_list = g_list_append (element_directory_list,
1435 g_strdup (p));
1436 }
1437 }
1438 g_free (path);
1439
1440 pins_file_name = g_strconcat (sch_basename, ".cmd", NULL);
1441 net_file_name = g_strconcat (sch_basename, ".net", NULL);
1442 pcb_file_name = g_strconcat (sch_basename, ".pcb", NULL);
1443 bak_file_name = g_strconcat (sch_basename, ".pcb.bak", NULL);
1444 tmp = g_strdup (bak_file_name);
1445
1446 for (i = 0; g_file_test (bak_file_name, G_FILE_TEST_EXISTS); ++i) {
1447 g_free (bak_file_name);
1448 bak_file_name = g_strdup_printf ("%s%d", tmp, i);
1449 }
1450 g_free (tmp);
1451
1452 if (g_file_test (pcb_file_name, G_FILE_TEST_EXISTS)) {
1453 initial_pcb = FALSE;
1454 pcb_new_file_name = g_strconcat (sch_basename, ".new.pcb", NULL);
1455 get_pcb_element_list (pcb_file_name);
1456 } else
1457 pcb_new_file_name = g_strdup (pcb_file_name);
1458
1459 if (!run_gnetlist (pins_file_name, net_file_name, pcb_new_file_name,
1460 sch_basename, schematics)) {
1461 fprintf(stderr, "Failed to run gnetlist\n");
1462 exit (1);
1463 }
1464
1465 if (add_elements (pcb_new_file_name) == 0) {
1466 build_and_run_command ("rm %s", pcb_new_file_name);
1467 if (initial_pcb) {
1468 printf ("No elements found, so nothing to do.\n");
1469 exit (0);
1470 }
1471 }
1472
1473 if (fix_elements)
1474 update_element_descriptions (pcb_file_name, bak_file_name);
1475 prune_elements (pcb_file_name, bak_file_name);
1476
1477 /* Report work done during processing */
1478 if (verbose)
1479 printf ("\n");
1480 printf ("\n----------------------------------\n");
1481 printf ("Done processing. Work performed:\n");
1482 if (n_deleted > 0 || n_fixed > 0 || need_PKG_purge || n_changed_value > 0)
1483 printf ("%s is backed up as %s.\n", pcb_file_name, bak_file_name);
1484 if (pcb_element_list && n_deleted > 0)
1485 printf ("%d elements deleted from %s.\n", n_deleted, pcb_file_name);
1486
1487 if (n_added_ef + n_added_m4 > 0)
1488 printf ("%d file elements and %d m4 elements added to %s.\n",
1489 n_added_ef, n_added_m4, pcb_new_file_name);
1490 else if (n_not_found == 0) {
1491 printf ("No elements to add so not creating %s\n", pcb_new_file_name);
1492 created_pcb_file = FALSE;
1493 }
1494
1495 if (n_not_found > 0) {
1496 printf ("%d not found elements added to %s.\n",
1497 n_not_found, pcb_new_file_name);
1498 }
1499 if (n_unknown > 0)
1500 printf ("%d components had no footprint attribute and are omitted.\n",
1501 n_unknown);
1502 if (n_none > 0)
1503 printf ("%d components with footprint \"none\" omitted from %s.\n",
1504 n_none, pcb_new_file_name);
1505 if (n_empty > 0)
1506 printf ("%d components with empty footprint \"%s\" omitted from %s.\n",
1507 n_empty, empty_footprint_name, pcb_new_file_name);
1508 if (n_changed_value > 0)
1509 printf ("%d elements had a value change in %s.\n",
1510 n_changed_value, pcb_file_name);
1511 if (n_fixed > 0)
1512 printf ("%d elements fixed in %s.\n", n_fixed, pcb_file_name);
1513 if (n_PKG_removed_old > 0) {
1514 printf ("%d elements could not be found.", n_PKG_removed_old);
1515 if (created_pcb_file)
1516 printf (" So %s is incomplete.\n", pcb_file_name);
1517 else
1518 printf ("\n");
1519 }
1520 if (n_PKG_removed_new > 0) {
1521 printf ("%d elements could not be found.", n_PKG_removed_new);
1522 if (created_pcb_file)
1523 printf (" So %s is incomplete.\n", pcb_new_file_name);
1524 else
1525 printf ("\n");
1526 }
1527 if (n_preserved > 0)
1528 printf ("%d elements not in the schematic preserved in %s.\n",
1529 n_preserved, pcb_file_name);
1530
1531 /* Tell user what to do next */
1532 if (verbose)
1533 printf ("\n");
1534
1535 if (n_added_ef + n_added_m4 > 0) {
1536 if (initial_pcb) {
1537 printf ("\nNext step:\n");
1538 printf ("1. Run pcb on your file %s.\n", pcb_file_name);
1539 printf
1540 (" You will find all your footprints in a bundle ready for you to place\n");
1541 printf
1542 (" or disperse with \"Select -> Disperse all elements\" in PCB.\n\n");
1543 printf
1544 ("2. From within PCB, select \"File -> Load netlist file\" and select \n");
1545 printf (" %s to load the netlist.\n\n", net_file_name);
1546 printf ("3. From within PCB, enter\n\n");
1547 printf (" :ExecuteFile(%s)\n\n", pins_file_name);
1548 printf
1549 (" to propagate the pin names of all footprints to the layout.\n\n");
1550 } else if (quiet_mode == FALSE) {
1551 printf ("\nNext steps:\n");
1552 printf ("1. Run pcb on your file %s.\n", pcb_file_name);
1553 printf
1554 ("2. From within PCB, select \"File -> Load layout data to paste buffer\"\n");
1555 printf
1556 (" and select %s to load the new footprints into your existing layout.\n",
1557 pcb_new_file_name);
1558 printf
1559 ("3. From within PCB, select \"File -> Load netlist file\" and select \n");
1560 printf (" %s to load the updated netlist.\n\n", net_file_name);
1561 printf ("4. From within PCB, enter\n\n");
1562 printf (" :ExecuteFile(%s)\n\n", pins_file_name);
1563 printf (" to update the pin names of all footprints.\n\n");
1564 }
1565 }
1566
1567 g_free (net_file_name);
1568 g_free (pins_file_name);
1569 g_free (pcb_file_name);
1570 g_free (bak_file_name);
1571
1572 return 0;
1573 }
1574