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
34 #ifdef HAVE_LIBDMALLOC
38 #define GSC2PCB_VERSION "1.6"
40 #define DEFAULT_PCB_INC "pcb.inc"
42 #define SEP_STRING "--------\n"
46 gchar
*refdes
, *value
, *description
, *changed_description
, *changed_value
;
52 gboolean still_exists
, new_format
, hi_res_format
, quoted_flags
, omit_PKG
;
59 gchar
*part_number
, *element_name
;
63 static GList
*pcb_element_list
,
64 *element_directory_list
, *extra_gnetlist_list
, *extra_gnetlist_arg_list
;
66 static gchar
*sch_basename
;
68 static GList
*schematics
;
70 static gchar
*m4_pcbdir
, *default_m4_pcbdir
, *m4_files
, *m4_override_file
;
72 static gboolean use_m4
= TRUE
;
74 static gchar
*empty_footprint_name
;
83 n_preserved
, n_changed_value
, n_not_found
, n_unknown
, n_none
, n_empty
;
85 static gboolean remove_unfound_elements
= TRUE
,
87 force_element_files
, preserve
, fix_elements
, bak_done
, need_PKG_purge
;
91 create_m4_override_file ()
95 m4_override_file
= "gnet-gsch2pcb-tmp.scm";
96 f
= fopen (m4_override_file
, "wb");
98 m4_override_file
= NULL
;
102 fprintf (f
, "(define gsch2pcb:pcb-m4-dir \"%s\")\n", m4_pcbdir
);
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");
109 printf ("Default m4-pcbdir: %s\n", default_m4_pcbdir
);
110 printf ("--------\ngnet-gsch2pcb-tmp.scm override file:\n");
112 printf (" (define gsch2pcb:pcb-m4-dir \"%s\")\n", m4_pcbdir
);
114 printf (" (define gsch2pcb:m4-files \"%s\")\n", m4_files
);
115 printf (" (define gsch2pcb:use-m4 %s)\n", use_m4
== TRUE
? "#t" : "#f");
120 * Build and run a command. No redirection or error handling is
121 * done. Format string is split on whitespace. Specifiers %l and %s
122 * are replaced with contents of positional args. To be recognized,
123 * specifiers must be separated from other arguments in the format by
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
131 build_and_run_command (const gchar
*format
, ...)
139 gboolean result
= FALSE
;
140 gboolean spawn_result
;
141 gchar
*standard_output
= NULL
;
142 gchar
*standard_error
= NULL
;
143 GError
* error
= NULL
;
145 va_start (vargs
, format
);
146 split
= g_strsplit_set (format
, " \t\n\v", 0);
147 num_split
= g_strv_length (split
);
148 for (i
= 0; i
< num_split
; ++i
) {
149 gchar
*chunk
= split
[i
];
150 if (strcmp (chunk
, "%l") == 0) {
151 /* append contents of list into command args - shared data */
152 tmp
= g_list_concat (tmp
, g_list_copy (va_arg (vargs
, GList
*)));
153 } else if (strcmp (chunk
, "%s") == 0) {
154 /* insert contents of string into output */
155 tmp
= g_list_append (tmp
, va_arg (vargs
, gchar
*));
157 /* bare string, use as is */
158 tmp
= g_list_append (tmp
, chunk
);
164 /* we have something in the list, build & call command */
167 gchar
** args
= g_new0 (gchar
*, g_list_length (tmp
) + 1/* NULL terminate the list */);
170 printf ("Running command:\n\t");
172 for (p
= tmp
; p
; p
= g_list_next (p
)) {
173 args
[i
++] = (gchar
*) p
->data
;
175 printf ("%s ", (char*)p
->data
);
179 printf ("\n%s", SEP_STRING
);
181 if (g_spawn_sync (".", /* Working directory */
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 */
192 fputs(standard_output
, stdout
);
197 fputs(standard_error
, stderr
);
201 fprintf(stderr
, "Failed to execute external program: %s\n", error
->message
);
206 printf ("\n%s", SEP_STRING
);
208 g_free(standard_error
);
209 g_free (standard_output
);
212 /* free the list, but leave data untouched */
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).
227 run_gnetlist (gchar
* pins_file
, gchar
* net_file
, gchar
* pcb_file
,
228 gchar
* basename
, GList
* largs
)
232 static const gchar
*gnetlist
= NULL
;
234 GList
*verboseList
= NULL
;
237 /* Allow the user to specify a full path or a different name for
238 * the gnetlist command. Especially useful if multiple copies
239 * are installed at once.
241 if (gnetlist
== NULL
)
242 gnetlist
= g_getenv ("GNETLIST");
243 if (gnetlist
== NULL
)
244 gnetlist
= "gnetlist";
247 verboseList
= g_list_append (verboseList
, "-q");
249 if (!build_and_run_command ("%s %l -g pcbpins -o %s %l %l",
253 extra_gnetlist_arg_list
,
257 if (!build_and_run_command ("%s %l -g PCB -o %s %l %l",
261 extra_gnetlist_arg_list
,
264 create_m4_override_file ();
266 if (m4_override_file
) {
267 args1
= g_list_append (args1
, "-m");
268 args1
= g_list_append (args1
, m4_override_file
);
271 mtime
= (stat (pcb_file
, &st
) == 0) ? st
.st_mtime
: 0;
273 if (!build_and_run_command ("%s %l -g gsch2pcb -o %s %l %l %l",
278 extra_gnetlist_arg_list
,
280 if (stat (pcb_file
, &st
) != 0 || mtime
== st
.st_mtime
) {
282 "gsch2pcb: gnetlist command failed, `%s' not updated\n",
285 if (m4_override_file
)
287 " At least gnetlist 20030901 is required for m4-xxx options.\n");
293 if (m4_override_file
)
294 unlink (m4_override_file
);
296 for (list
= extra_gnetlist_list
; list
; list
= g_list_next (list
)) {
297 const gchar
*s
= (gchar
*) list
->data
;
298 const gchar
*s2
= strstr (s
, " -o ");
302 out_file
= g_strconcat (basename
, ".", s
, NULL
);
303 backend
= g_strdup (s
);
305 out_file
= g_strdup (s2
+ 4);
306 backend
= g_strndup (s
, s2
- s
);
309 if (!build_and_run_command ("%s %l -g %s -o %s %l %l",
314 extra_gnetlist_arg_list
,
322 g_list_free (verboseList
);
328 token (gchar
* string
, gchar
** next
, gboolean
* quoted_ret
)
332 gboolean quoted
= FALSE
;
339 return g_strdup ("");
341 while (*str
== ' ' || *str
== '\t' || *str
== ',' || *str
== '\n')
348 for (s
= str
; *s
&& *s
!= '"' && *s
!= '\n'; ++s
);
353 *s
&& (*s
!= ' ' && *s
!= '\t' && *s
!= ',' && *s
!= '\n'); ++s
);
355 ret
= g_strndup (str
, s
- str
);
356 str
= (quoted
&& *s
) ? s
+ 1 : s
;
363 fix_spaces (gchar
* str
)
369 for (s
= str
; *s
; ++s
)
370 if (*s
== ' ' || *s
== '\t')
375 /* As of 1/9/2004 CVS hi_res Element[] line format:
376 * Element[element_flags, description, pcb-name, value, mark_x, mark_y,
377 * text_x, text_y, text_direction, text_scale, text_flags]
378 * New PCB 1.7 / 1.99 Element() line format:
379 * Element(element_flags, description, pcb-name, value, mark_x, mark_y,
380 * text_x, text_y, text_direction, text_scale, text_flags)
381 * Old PCB 1.6 Element() line format:
382 * Element(element_flags, description, pcb-name, value,
383 * text_x, text_y, text_direction, text_scale, text_flags)
385 * (mark_x, mark_y) is the element position (mark) and (text_x,text_y)
386 * is the description text position which is absolute in pre 1.7 and
387 * is now relative. The hi_res mark_x,mark_y and text_x,text_y resolutions
388 * are 100x the other formats.
391 pcb_element_line_parse (gchar
* line
)
393 PcbElement
*el
= NULL
;
394 gchar
*s
, *t
, close_char
;
395 gint state
= 0, elcount
= 0;
397 if (strncmp (line
, "Element", 7))
400 el
= g_new0 (PcbElement
, 1);
403 while (*s
== ' ' || *s
== '\t')
407 el
->hi_res_format
= TRUE
;
408 else if (*s
!= '(') {
413 el
->res_char
= el
->hi_res_format
? '[' : '(';
414 close_char
= el
->hi_res_format
? ']' : ')';
416 el
->flags
= token (s
+ 1, NULL
, &el
->quoted_flags
);
417 el
->description
= token (NULL
, NULL
, NULL
);
418 el
->refdes
= token (NULL
, NULL
, NULL
);
419 el
->value
= token (NULL
, NULL
, NULL
);
421 s
= token (NULL
, NULL
, NULL
);
425 s
= token (NULL
, &t
, NULL
);
429 el
->tail
= g_strdup (t
? t
: "");
430 if ((s
= strrchr (el
->tail
, (gint
) '\n')) != NULL
)
433 /* Count the tokens in tail to decide if it's new or old format.
434 * Old format will have 3 tokens, new format will have 5 tokens.
436 for (s
= el
->tail
; *s
&& *s
!= close_char
; ++s
) {
445 el
->new_format
= TRUE
;
447 fix_spaces (el
->description
);
448 fix_spaces (el
->refdes
);
449 fix_spaces (el
->value
);
451 /* Don't allow elements with no refdes to ever be deleted because
452 * they may be desired pc board elements not in schematics. So
453 * initialize still_exists to TRUE if empty or non-alphanumeric
456 if (!*el
->refdes
|| !isalnum ((gint
) (*el
->refdes
)))
457 el
->still_exists
= TRUE
;
463 pcb_element_free (PcbElement
* el
)
468 g_free (el
->description
);
469 g_free (el
->changed_description
);
470 g_free (el
->changed_value
);
474 g_free (el
->pkg_name_fix
);
479 get_pcb_element_list (gchar
* pcb_file
)
485 if ((f
= fopen (pcb_file
, "r")) == NULL
)
487 while ((fgets (buf
, sizeof (buf
), f
)) != NULL
) {
488 for (s
= buf
; *s
== ' ' || *s
== '\t'; ++s
);
489 if (!strncmp (s
, "PKG_", 4)) {
490 need_PKG_purge
= TRUE
;
493 if ((el
= pcb_element_line_parse (s
)) == NULL
)
495 pcb_element_list
= g_list_append (pcb_element_list
, el
);
501 pcb_element_exists (PcbElement
* el_test
, gboolean record
)
506 for (list
= pcb_element_list
; list
; list
= g_list_next (list
)) {
507 el
= (PcbElement
*) list
->data
;
509 if (strcmp (el_test
->refdes
, el
->refdes
))
511 if (strcmp (el_test
->description
, el
->description
)) { /* footprint */
513 el
->changed_description
= g_strdup (el_test
->description
);
516 if (strcmp (el_test
->value
, el
->value
))
517 el
->changed_value
= g_strdup (el_test
->value
);
518 el
->still_exists
= TRUE
;
526 /* A problem is that new PCB 1.7 file elements have the
527 * (mark_x,mark_y) value set to wherever the element was created and
528 * no equivalent of a gschem translate symbol was done.
530 * So, file elements inserted can be scattered over a big area and
531 * this is bad when loading a file.new.pcb into an existing PC
532 * board. So, do a simple translate if (mark_x,mark_y) is
533 * (arbitrarily) over 1000. I'll assume that for values < 1000 the
534 * element creator was concerned with a sane initial element
535 * placement. Unless someone has a better idea? Don't bother with
536 * pre PCB 1.7 formats as that would require parsing the mark().
537 * Current m4 elements use the old format but they seem to have a
538 * reasonable initial mark().
541 simple_translate (PcbElement
* el
)
545 if (el
->new_format
) {
546 factor
= el
->hi_res_format
? 100 : 1;
547 if (el
->x
> 1000 * factor
)
548 el
->x
= 500 * factor
;
549 if (el
->y
> 1000 * factor
)
550 el
->y
= 500 * factor
;
555 insert_element (FILE * f_out
, gchar
* element_file
,
556 gchar
* footprint
, gchar
* refdes
, gchar
* value
)
560 gchar
*fmt
, *s
, buf
[1024];
561 gboolean retval
= FALSE
;
563 if ((f_in
= fopen (element_file
, "r")) == NULL
) {
564 s
= g_strdup_printf ("insert_element() can't open %s", element_file
);
569 /* Scan the file to detect whether it's actually a PCB
570 * layout. Assumes that a PCB layout will have a "PCB" line. */
571 while ((fgets (buf
, sizeof (buf
), f_in
)) != NULL
) {
572 for (s
= buf
; *s
== ' ' || *s
== '\t'; ++s
);
573 s
[3] = 0; /* Truncate line */
574 if (strncmp ("PCB", s
, sizeof (buf
)) == 0) {
575 printf ("Warning: %s appears to be a PCB layout file. Skipping.\n",
583 /* Copy the file element lines. Substitute new parameters into the
584 * Element() or Element[] line and strip comments.
586 while ((fgets (buf
, sizeof (buf
), f_in
)) != NULL
) {
587 for (s
= buf
; *s
== ' ' || *s
== '\t'; ++s
);
588 if ((el
= pcb_element_line_parse (s
)) != NULL
) {
589 simple_translate (el
);
590 fmt
= el
->quoted_flags
?
591 "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %d %d%s\n" :
592 "Element%c%s \"%s\" \"%s\" \"%s\" %d %d%s\n";
595 el
->res_char
, el
->flags
, footprint
, refdes
, value
,
596 el
->x
, el
->y
, el
->tail
);
598 } else if (*s
!= '#')
600 pcb_element_free (el
);
608 find_element (gchar
* dir_path
, gchar
* element
)
611 gchar
*path
, *name
, *s
, *found
= NULL
;
613 if ((dir
= g_dir_open (dir_path
, 0, NULL
)) == NULL
) {
614 s
= g_strdup_printf ("find_element can't open dir \"%s\"", dir_path
);
620 printf ("\t Searching: \"%s\" for \"%s\"\n", dir_path
, element
);
621 while ((name
= (gchar
*) g_dir_read_name (dir
)) != NULL
) {
622 path
= g_strconcat (dir_path
, "/", name
, NULL
);
625 /* if we got a directory name, then recurse down into it */
626 if (g_file_test (path
, G_FILE_TEST_IS_DIR
))
627 found
= find_element (path
, element
);
629 /* otherwise assume it is a file and see if it is the one we want */
632 printf ("\t : %s\t", name
);
633 if (!strcmp (name
, element
))
634 found
= g_strdup (path
);
637 tmps
= g_strconcat (element
, ".fp", NULL
);
638 if (!strcmp (name
, tmps
))
639 found
= g_strdup (path
);
643 printf ("%s\n", found
? "Yes" : "No");
654 search_element_directories (PcbElement
* el
)
657 gchar
*s
, *elname
= NULL
, *dir_path
, *path
= NULL
;
660 /* See comment before pkg_to_element() */
661 if (el
->pkg_name_fix
) {
662 if (strchr (el
->description
, '-')) {
663 n1
= strlen (el
->description
);
664 n2
= strlen (el
->pkg_name_fix
);
665 s
= el
->description
+ n1
- n2
- 1;
667 // printf("n1=%d n2=%d desc:%s fix:%s s:%s\n",
668 // n1, n2, el->description, el->pkg_name_fix, s);
670 if (n1
> 0 && n2
< n1
&& *s
== '-' && *(s
+ 1) == *el
->pkg_name_fix
) {
671 s
= g_strndup (el
->description
, n1
- n2
- 1);
672 elname
= g_strconcat (s
, " ", el
->pkg_name_fix
, NULL
);
677 printf ("Warning: argument passing may have been confused by\n");
678 printf (" a comma in a component value:\n");
679 printf (" Check %s %s %s\n",
680 el
->refdes
, el
->description
, el
->value
);
681 printf (" Maybe just use a space instead of a comma?\n");
685 elname
= g_strdup (el
->description
);
687 if (!strcmp (elname
, "unknown")) {
692 printf ("\tSearching directories looking for file element: %s\n", elname
);
693 for (list
= element_directory_list
; list
; list
= g_list_next (list
)) {
694 dir_path
= (gchar
*) list
->data
;
696 printf ("\tLooking in directory: \"%s\"\n", dir_path
);
697 path
= find_element (dir_path
, elname
);
700 printf ("\tFound: %s\n", path
);
708 /* The gnetlist backend gnet-gsch2pcb.scm generates PKG_ lines:
710 * PKG_footprint(footprint{-fp0-fp1},refdes,value{,fp0,fp1})
712 * where fp1 and fp2 (if they exist) are the extra footprint
713 * components when specifying footprints like "DIL 14 300". This is
714 * needed for m4 macros.
716 * A complication is if the footprint references a file element with
717 * spaces embedded in the name. The gnetlist backend will interpret
718 * these as fp0, fp1, ... args and the footprint will in this case
719 * incorrectly have '-' inserted where the spaces should be. So, if
720 * there are additional args, reconstruct the portion of the name
721 * given by the args with spaces for later use. Eg. if the footprint
722 * is "100 Pin jack", we will have
724 * PKG_100-Pin-jack(100-Pin-jack,refdes,value,Pin,jack)
726 * So put "Pin jack" into pkg_name_fix so if this element is searched
727 * as a file element we can munge the description to what it should
730 * 100-Pin-jack -> 100 Pin jack
733 pkg_to_element (FILE * f
, gchar
* pkg_line
)
737 gint n
, n_extra_args
, n_dashes
;
739 if (strncmp (pkg_line
, "PKG_", 4)
740 || (s
= strchr (pkg_line
, (gint
) '(')) == NULL
)
743 args
= g_strsplit (s
+ 1, ",", 12);
744 if (!args
[0] || !args
[1] || !args
[2]) {
745 fprintf (stderr
, "Bad package line: %s\n", pkg_line
);
748 fix_spaces (args
[0]);
749 fix_spaces (args
[1]);
750 fix_spaces (args
[2]);
752 el
= g_new0 (PcbElement
, 1);
753 el
->description
= g_strdup (args
[0]);
754 el
->refdes
= g_strdup (args
[1]);
755 el
->value
= g_strdup (args
[2]);
756 if ((s
= strchr (el
->value
, (gint
) ')')) != NULL
)
759 /* If the component value has a comma, eg "1k, 1%", the gnetlist generated
762 * PKG_XXX(`R0w8',`R100',`1k, 1%'),
764 * but after processed by m4, the input to gsch2pcb will be
766 * PKG_XXX(R0w8,R100,1k, 1%).
768 * So the quoting info has been lost when processing for file
769 * elements. So here try to detect and fix this. But I can't
770 * handle the situation where the description has a '-' and the
771 * value has a comma because gnet-gsch2pcb.scm munges the
772 * description with '-' when there are extra args.
774 for (n_extra_args
= 0; args
[3 + n_extra_args
] != NULL
; ++n_extra_args
);
776 for (n_dashes
= 0; (s
= strchr (s
+ 1, '-')) != NULL
; ++n_dashes
);
779 if (n_extra_args
== n_dashes
+ 1) { /* Assume there was a comma in the value, eg "1K, 1%" */
781 el
->value
= g_strconcat (s
, ",", fix_spaces (args
[n
]), NULL
);
783 if ((s
= strchr (el
->value
, (gint
) ')')) != NULL
)
788 el
->pkg_name_fix
= g_strdup (args
[n
]);
789 for (n
+= 1; args
[n
] != NULL
; ++n
) {
790 s
= el
->pkg_name_fix
;
791 el
->pkg_name_fix
= g_strconcat (s
, " ", args
[n
], NULL
);
794 if ((s
= strchr (el
->pkg_name_fix
, (gint
) ')')) != NULL
)
799 if (empty_footprint_name
&& !strcmp (el
->description
, empty_footprint_name
)) {
802 ("%s: has the empty footprint attribute \"%s\" so won't be in the layout.\n",
803 el
->refdes
, el
->description
);
806 } else if (!strcmp (el
->description
, "none")) {
808 "WARNING: %s has a footprint attribute \"%s\" so won't be in the layout.\n",
809 el
->refdes
, el
->description
);
812 } else if (!strcmp (el
->description
, "unknown")) {
814 "WARNING: %s has no footprint attribute so won't be in the layout.\n",
822 /* Process the newly created pcb file which is the output from
823 * gnetlist -g gsch2pcb ...
825 * It will have elements found via the m4 interface and PKG_ lines for
826 * elements not found. Insert pcb file elements for PKG_ lines if
827 * file elements can be found. If there was an existing pcb file,
828 * strip out any elements if they are already present so that the new
829 * pcb file will only have new elements.
832 add_elements (gchar
* pcb_file
)
835 PcbElement
*el
= NULL
;
836 gchar
*command
, *p
, *tmp_file
, *s
, buf
[1024];
837 gint total
, paren_level
= 0;
838 gboolean is_m4
, skipping
= FALSE
;
840 if ((f_in
= fopen (pcb_file
, "r")) == NULL
)
842 tmp_file
= g_strconcat (pcb_file
, ".tmp", NULL
);
843 if ((f_out
= fopen (tmp_file
, "wb")) == NULL
) {
848 while ((fgets (buf
, sizeof (buf
), f_in
)) != NULL
) {
849 for (s
= buf
; *s
== ' ' || *s
== '\t'; ++s
);
853 else if (*s
== ')' && --paren_level
<= 0)
858 if ((el
= pcb_element_line_parse (s
)) != NULL
)
861 el
= pkg_to_element (f_out
, s
);
862 if (el
&& pcb_element_exists (el
, TRUE
)) {
864 pcb_element_free (el
);
867 if (!el
|| el
->omit_PKG
) {
874 if (!is_m4
|| (is_m4
&& force_element_files
)) {
875 if (verbose
&& !is_m4
)
876 printf ("%s: need new file element for footprint %s (value=%s)\n",
877 el
->refdes
, el
->description
, el
->value
);
878 if (verbose
&& is_m4
&& force_element_files
)
880 ("%s: have m4 element %s, but trying to replace with a file element.\n",
881 el
->refdes
, el
->description
);
882 p
= search_element_directories (el
);
883 if (!p
&& verbose
&& is_m4
&& force_element_files
)
884 printf ("\tNo file element found.\n");
886 if (p
&& insert_element (f_out
, p
,
887 el
->description
, el
->refdes
, el
->value
)) {
892 printf ("%s: added new file element for footprint %s (value=%s)\n",
893 el
->refdes
, el
->description
, el
->value
);
896 "%s: can't find PCB element for footprint %s (value=%s)\n",
897 el
->refdes
, el
->description
, el
->value
);
898 if (remove_unfound_elements
&& !fix_elements
) {
900 "So device %s will not be in the layout.\n", el
->refdes
);
904 fputs (buf
, f_out
); /* Copy PKG_ line */
913 printf ("%s: added new m4 element for footprint %s (value=%s)\n",
914 el
->refdes
, el
->description
, el
->value
);
916 pcb_element_free (el
);
923 total
= n_added_ef
+ n_added_m4
+ n_not_found
;
925 build_and_run_command ("rm %s", tmp_file
);
927 build_and_run_command ("mv %s %s", tmp_file
, pcb_file
);
933 update_element_descriptions (gchar
* pcb_file
, gchar
* bak
)
937 PcbElement
*el
, *el_exists
;
938 gchar
*fmt
, *command
, *tmp
, *s
, buf
[1024];
940 for (list
= pcb_element_list
; list
; list
= g_list_next (list
)) {
941 el
= (PcbElement
*) list
->data
;
942 if (el
->changed_description
)
945 if (!pcb_element_list
|| n_fixed
== 0) {
946 fprintf (stderr
, "Could not find any elements to fix.\n");
949 if ((f_in
= fopen (pcb_file
, "r")) == NULL
)
951 tmp
= g_strconcat (pcb_file
, ".tmp", NULL
);
952 if ((f_out
= fopen (tmp
, "wb")) == NULL
) {
956 while ((fgets (buf
, sizeof (buf
), f_in
)) != NULL
) {
957 for (s
= buf
; *s
== ' ' || *s
== '\t'; ++s
);
958 if ((el
= pcb_element_line_parse (s
)) != NULL
959 && (el_exists
= pcb_element_exists (el
, FALSE
)) != NULL
960 && el_exists
->changed_description
) {
961 fmt
= el
->quoted_flags
?
962 "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %d %d%s\n" :
963 "Element%c%s \"%s\" \"%s\" \"%s\" %d %d%s\n";
966 el
->flags
, el_exists
->changed_description
,
967 el
->refdes
, el
->value
, el
->x
, el
->y
, el
->tail
);
968 printf ("%s: updating element Description: %s -> %s\n",
969 el
->refdes
, el
->description
, el_exists
->changed_description
);
970 el_exists
->still_exists
= TRUE
;
973 pcb_element_free (el
);
979 build_and_run_command ("mv %s %s", pcb_file
, bak
);
983 build_and_run_command ("mv %s %s", tmp
, pcb_file
);
988 prune_elements (gchar
* pcb_file
, gchar
* bak
)
992 PcbElement
*el
, *el_exists
;
993 gchar
*fmt
, *command
, *tmp
, *s
, buf
[1024];
994 gint paren_level
= 0;
995 gboolean skipping
= FALSE
;
997 for (list
= pcb_element_list
; list
; list
= g_list_next (list
)) {
998 el
= (PcbElement
*) list
->data
;
999 if (!el
->still_exists
) {
1003 "Preserving PCB element not in the schematic: %s (element %s)\n",
1004 el
->refdes
, el
->description
);
1007 } else if (el
->changed_value
)
1010 if (!pcb_element_list
1011 || (n_deleted
== 0 && !need_PKG_purge
&& n_changed_value
== 0)
1014 if ((f_in
= fopen (pcb_file
, "r")) == NULL
)
1016 tmp
= g_strconcat (pcb_file
, ".tmp", NULL
);
1017 if ((f_out
= fopen (tmp
, "wb")) == NULL
) {
1021 while ((fgets (buf
, sizeof (buf
), f_in
)) != NULL
) {
1022 for (s
= buf
; *s
== ' ' || *s
== '\t'; ++s
);
1026 else if (*s
== ')' && --paren_level
<= 0)
1031 if ((el
= pcb_element_line_parse (s
)) != NULL
1032 && (el_exists
= pcb_element_exists (el
, FALSE
)) != NULL
1033 && !el_exists
->still_exists
&& !preserve
) {
1036 printf ("%s: deleted element %s (value=%s)\n",
1037 el
->refdes
, el
->description
, el
->value
);
1038 pcb_element_free (el
);
1041 if (el_exists
&& el_exists
->changed_value
) {
1042 fmt
= el
->quoted_flags
?
1043 "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %d %d%s\n" :
1044 "Element%c%s \"%s\" \"%s\" \"%s\" %d %d%s\n";
1045 fprintf (f_out
, fmt
,
1046 el
->res_char
, el
->flags
, el
->description
, el
->refdes
,
1047 el_exists
->changed_value
, el
->x
, el
->y
, el
->tail
);
1049 printf ("%s: changed element %s value: %s -> %s\n",
1050 el
->refdes
, el
->description
,
1051 el
->value
, el_exists
->changed_value
);
1052 } else if (!strncmp (s
, "PKG_", 4))
1053 ++n_PKG_removed_old
;
1056 pcb_element_free (el
);
1062 build_and_run_command ("mv %s %s", pcb_file
, bak
);
1066 build_and_run_command ("mv %s %s", tmp
, pcb_file
);
1071 add_m4_file (gchar
* arg
)
1076 m4_files
= g_strdup (arg
);
1079 m4_files
= g_strconcat (m4_files
, " ", arg
, NULL
);
1085 expand_dir (gchar
* dir
)
1090 s
= g_build_filename ((gchar
*) g_get_home_dir (), dir
+ 1, NULL
);
1097 add_default_m4_files (void)
1101 path
= g_build_filename ((gchar
*) g_get_home_dir (),
1102 ".pcb", DEFAULT_PCB_INC
, NULL
);
1103 if (g_file_test (path
, G_FILE_TEST_IS_REGULAR
))
1107 if (g_file_test (DEFAULT_PCB_INC
, G_FILE_TEST_IS_REGULAR
))
1108 add_m4_file (DEFAULT_PCB_INC
);
1113 add_schematic (gchar
* sch
)
1116 schematics
= g_list_append (schematics
, g_strdup (sch
));
1117 if (!sch_basename
&& (s
= g_strrstr (sch
, ".sch")) != NULL
&& strlen(s
) == 4)
1118 sch_basename
= g_strndup (sch
, s
- sch
);
1122 add_multiple_schematics (gchar
* sch
)
1124 /* parse the string using shell semantics */
1126 gchar
** args
= NULL
;
1127 GError
* error
= NULL
;
1129 if (g_shell_parse_argv (sch
, &count
, &args
, &error
)) {
1131 for (i
= 0; i
< count
; ++i
)
1133 add_schematic (args
[i
]);
1138 "invalid `schematics' option: %s\n",
1140 g_error_free (error
);
1145 parse_config (gchar
* config
, gchar
* arg
)
1149 /* remove trailing white space otherwise strange things can happen */
1150 if ((arg
!= NULL
) && (strlen (arg
) >= 1)) {
1151 s
= arg
+ strlen (arg
) - 1;
1152 while ((*s
== ' ' || *s
== '\t') && (s
!= arg
))
1158 printf (" %s \"%s\"\n", config
, arg
? arg
: "");
1160 if (!strcmp (config
, "remove-unfound") || !strcmp (config
, "r")) {
1161 /* This is default behavior set in header section */
1162 remove_unfound_elements
= TRUE
;
1165 if (!strcmp (config
, "keep-unfound") || !strcmp (config
, "k")) {
1166 remove_unfound_elements
= FALSE
;
1169 if (!strcmp (config
, "quiet") || !strcmp (config
, "q")) {
1173 if (!strcmp (config
, "preserve") || !strcmp (config
, "p")) {
1177 if (!strcmp (config
, "use-files") || !strcmp (config
, "f")) {
1178 force_element_files
= TRUE
;
1181 if (!strcmp (config
, "skip-m4") || !strcmp (config
, "s")) {
1185 if (!strcmp (config
, "elements-dir") || !strcmp (config
, "d")) {
1187 printf ("\tAdding directory to file element directory list: %s\n",
1189 element_directory_list
=
1190 g_list_prepend (element_directory_list
, expand_dir (arg
));
1191 } else if (!strcmp (config
, "output-name") || !strcmp (config
, "o"))
1192 sch_basename
= g_strdup (arg
);
1193 else if (!strcmp (config
, "schematics"))
1194 add_multiple_schematics (arg
);
1195 else if (!strcmp (config
, "m4-pcbdir")) {
1197 m4_pcbdir
= g_strdup (arg
);
1198 } else if (!strcmp (config
, "m4-file"))
1200 else if (!strcmp (config
, "gnetlist"))
1201 extra_gnetlist_list
= g_list_append (extra_gnetlist_list
, g_strdup (arg
));
1202 else if (!strcmp (config
, "empty-footprint"))
1203 empty_footprint_name
= g_strdup (arg
);
1211 load_project (gchar
* path
)
1214 gchar
*s
, buf
[1024], config
[32], arg
[768];
1216 f
= fopen (path
, "r");
1220 printf ("Reading project file: %s\n", path
);
1221 while (fgets (buf
, sizeof (buf
), f
)) {
1222 for (s
= buf
; *s
== ' ' || *s
== '\t' || *s
== '\n'; ++s
);
1223 if (!*s
|| *s
== '#' || *s
== '/' || *s
== ';')
1226 sscanf (s
, "%31s %767[^\n]", config
, arg
);
1227 parse_config (config
, arg
);
1233 load_extra_project_files (void)
1236 static gboolean done
= FALSE
;
1241 load_project ("/etc/gsch2pcb");
1242 load_project ("/usr/local/etc/gsch2pcb");
1244 path
= g_build_filename ((gchar
*) g_get_home_dir (), ".gEDA",
1246 load_project (path
);
1252 static gchar
*usage_string0
=
1253 "usage: gsch2pcb [options] {project | foo.sch [foo1.sch ...]}\n"
1255 "Generate a PCB layout file from a set of gschem schematics.\n"
1256 " gnetlist -g PCB is run to generate foo.net from the schematics.\n"
1258 " gnetlist -g gsch2pcb is run to get PCB m4 derived elements which\n"
1259 " match schematic footprints. For schematic footprints which don't match\n"
1260 " any PCB m4 layout elements, search a set of file element directories in\n"
1261 " an attempt to find matching PCB file elements.\n"
1262 " Output to foo.pcb if it doesn't exist. If there is a current foo.pcb,\n"
1263 " output only new elements to foo.new.pcb.\n"
1264 " If any elements with a non-empty element name in the current foo.pcb\n"
1265 " have no matching schematic component, then remove those elements from\n"
1266 " foo.pcb and rename foo.pcb to a foo.pcb.bak sequence.\n"
1268 " gnetlist -g pcbpins is run to get a PCB actions file which will rename all\n"
1269 " of the pins in a .pcb file to match pin names from the schematic.\n"
1271 " \"project\" is a file (not ending in .sch) containing a list of\n"
1272 " schematics to process and some options. A schematics line is like:\n"
1273 " schematics foo1.sch foo2.sch ...\n"
1274 " Options in a project file are like command line args without the \"-\":\n"
1275 " output-name myproject\n"
1277 "options (may be included in a project file):\n"
1278 " -d, --elements-dir D Search D for PCB file elements. These defaults\n"
1279 " are searched if they exist: ./packages,\n"
1280 " /usr/local/share/pcb/newlib, /usr/share/pcb/newlib,\n"
1281 " (old pcb) /usr/local/lib/pcb_lib, /usr/lib/pcb_lib,\n"
1282 " (old pcb) /usr/local/pcb_lib\n"
1283 " -o, --output-name N Use output file names N.net, N.pcb, and N.new.pcb\n"
1284 " instead of foo.net, ... where foo is the basename\n"
1285 " of the first command line .sch file.\n"
1286 " -f, --use-files Force using file elements over m4 PCB elements\n"
1287 " for new footprints even though m4 elements are\n"
1288 " searched for first and may have been found.\n"
1289 " -r, --remove-unfound Don't include references to unfound elements in\n"
1290 " the generated .pcb files. Use if you want PCB to\n"
1291 " be able to load the (incomplete) .pcb file.\n"
1292 " This is the default behavior.\n"
1293 " -k, --keep-unfound Keep include references to unfound elements in\n"
1294 " the generated .pcb files. Use if you want to hand\n"
1295 " edit or otherwise preprocess the generated .pcb file\n"
1296 " before running pcb.\n"
1297 " -p, --preserve Preserve elements in PCB files which are not found\n"
1298 " in the schematics. Note that elements with an empty\n"
1299 " element name (schematic refdes) are never deleted,\n"
1300 " so you really shouldn't need this option.\n"
1301 " -q, --quiet Don't tell the user what to do next after running gsch2pcb.\n"
1303 " -s, --skip-m4 Skip m4 when looking for footprints. The default is to use\n"
1304 " m4 (which is what previous versions did).\n"
1305 " --m4-file F.inc Use m4 file F.inc in addition to the default m4\n"
1306 " files ./pcb.inc and ~/.pcb/pcb.inc.\n"
1307 " --m4-pcbdir D Use D as the PCB m4 files install directory\n"
1308 " instead of the default:\n";
1310 static gchar
*usage_string1
=
1311 " --gnetlist backend A convenience run of extra gnetlist -g commands.\n"
1312 " Example: gnetlist partslist3\n"
1313 " Creates: myproject.partslist3\n"
1314 " --empty-footprint name See the project.sample file.\n"
1316 "options (not recognized in a project file):\n"
1317 " --gnetlist-arg arg Allows additional arguments to be passed to gnetlist.\n"
1318 " --fix-elements If a schematic component footprint is not equal\n"
1319 " to its PCB element Description, update the\n"
1320 " Description instead of replacing the element.\n"
1321 " Do this the first time gsch2pcb is used with\n"
1322 " PCB files originally created with gschem2pcb.\n"
1323 " -v, --verbose Use -v -v for additional file element debugging.\n"
1324 " -V, --version\n\n"
1325 "environment variables:\n"
1326 " GNETLIST If set, this specifies the name of the gnetlist program\n"
1329 "Additional Resources:\n"
1331 " gnetlist user guide: http://geda.seul.org/wiki/geda:gnetlist_ug\n"
1332 " gEDA homepage: http://www.gpleda.org\n"
1333 " PCB homepage: http://pcb.gpleda.org\n" "\n";
1338 puts (usage_string0
);
1339 printf (" %s\n", default_m4_pcbdir
);
1340 puts (usage_string1
);
1345 get_args (gint argc
, gchar
** argv
)
1347 gchar
*opt
, *arg
, *s
;
1350 for (i
= 1; i
< argc
; ++i
) {
1357 if (!strcmp (opt
, "version") || !strcmp (opt
, "V")) {
1358 printf ("gsch2pcb %s\n", GSC2PCB_VERSION
);
1360 } else if (!strcmp (opt
, "verbose") || !strcmp (opt
, "v")) {
1363 } else if (!strcmp (opt
, "fix-elements")) {
1364 fix_elements
= TRUE
;
1366 } else if (!strcmp (opt
, "gnetlist-arg")) {
1367 extra_gnetlist_arg_list
=
1368 g_list_append (extra_gnetlist_arg_list
, g_strdup (arg
));
1371 } else if (!strcmp (opt
, "help") || !strcmp (opt
, "h"))
1374 && ((r
= parse_config (opt
, (i
< argc
- 1) ? arg
: NULL
))
1380 printf ("gsch2pcb: bad or incomplete arg: %s\n", argv
[i
]);
1383 if (!g_str_has_suffix (argv
[i
], ".sch")) {
1384 load_extra_project_files ();
1385 load_project (argv
[i
]);
1387 add_schematic (argv
[i
]);
1393 main (gint argc
, gchar
** argv
)
1395 gchar
*pcb_file_name
,
1396 *pcb_new_file_name
, *bak_file_name
, *pins_file_name
, *net_file_name
, *tmp
;
1398 gboolean initial_pcb
= TRUE
;
1399 gboolean created_pcb_file
= TRUE
;
1401 const char *pcbdata_path
;
1406 pcbdata_path
= g_getenv ("PCBDATA"); /* do not free return value */
1407 if (pcbdata_path
!= NULL
) {
1408 /* If PCBDATA is set, use the value */
1409 m4_pcbdir
= g_strconcat (pcbdata_path
, "/pcb/m4", NULL
);
1411 /* Use the default value passed in from the configure script
1412 * instead of trying to hard code a value which is very
1415 m4_pcbdir
= g_strconcat (PCBDATADIR
, "/pcb/m4", NULL
);
1418 default_m4_pcbdir
= g_strdup (m4_pcbdir
);
1420 get_args (argc
, argv
);
1422 load_extra_project_files ();
1423 add_default_m4_files ();
1429 /* Defaults for the search path if not configured in the project file */
1430 if (g_file_test ("packages", G_FILE_TEST_IS_DIR
))
1431 element_directory_list
= g_list_append (element_directory_list
, "packages");
1433 #define PCB_PATH_DELIMETER ":"
1435 printf ("Processing PCBLIBPATH=\"%s\"\n", PCBLIBPATH
);
1437 path
= g_strdup (PCBLIBPATH
);
1438 for (p
= strtok (path
, PCB_PATH_DELIMETER
); p
&& *p
;
1439 p
= strtok (NULL
, PCB_PATH_DELIMETER
)) {
1440 if (g_file_test (p
, G_FILE_TEST_IS_DIR
)) {
1442 printf ("Adding %s to the newlib search path\n", p
);
1443 element_directory_list
= g_list_append (element_directory_list
,
1449 pins_file_name
= g_strconcat (sch_basename
, ".cmd", NULL
);
1450 net_file_name
= g_strconcat (sch_basename
, ".net", NULL
);
1451 pcb_file_name
= g_strconcat (sch_basename
, ".pcb", NULL
);
1452 bak_file_name
= g_strconcat (sch_basename
, ".pcb.bak", NULL
);
1453 tmp
= g_strdup (bak_file_name
);
1455 for (i
= 0; g_file_test (bak_file_name
, G_FILE_TEST_EXISTS
); ++i
) {
1456 g_free (bak_file_name
);
1457 bak_file_name
= g_strdup_printf ("%s%d", tmp
, i
);
1461 if (g_file_test (pcb_file_name
, G_FILE_TEST_EXISTS
)) {
1462 initial_pcb
= FALSE
;
1463 pcb_new_file_name
= g_strconcat (sch_basename
, ".new.pcb", NULL
);
1464 get_pcb_element_list (pcb_file_name
);
1466 pcb_new_file_name
= g_strdup (pcb_file_name
);
1468 if (!run_gnetlist (pins_file_name
, net_file_name
, pcb_new_file_name
,
1469 sch_basename
, schematics
)) {
1470 fprintf(stderr
, "Failed to run gnetlist\n");
1474 if (add_elements (pcb_new_file_name
) == 0) {
1475 build_and_run_command ("rm %s", pcb_new_file_name
);
1477 printf ("No elements found, so nothing to do.\n");
1483 update_element_descriptions (pcb_file_name
, bak_file_name
);
1484 prune_elements (pcb_file_name
, bak_file_name
);
1486 /* Report work done during processing */
1489 printf ("\n----------------------------------\n");
1490 printf ("Done processing. Work performed:\n");
1491 if (n_deleted
> 0 || n_fixed
> 0 || need_PKG_purge
|| n_changed_value
> 0)
1492 printf ("%s is backed up as %s.\n", pcb_file_name
, bak_file_name
);
1493 if (pcb_element_list
&& n_deleted
> 0)
1494 printf ("%d elements deleted from %s.\n", n_deleted
, pcb_file_name
);
1496 if (n_added_ef
+ n_added_m4
> 0)
1497 printf ("%d file elements and %d m4 elements added to %s.\n",
1498 n_added_ef
, n_added_m4
, pcb_new_file_name
);
1499 else if (n_not_found
== 0) {
1500 printf ("No elements to add so not creating %s\n", pcb_new_file_name
);
1501 created_pcb_file
= FALSE
;
1504 if (n_not_found
> 0) {
1505 printf ("%d not found elements added to %s.\n",
1506 n_not_found
, pcb_new_file_name
);
1509 printf ("%d components had no footprint attribute and are omitted.\n",
1512 printf ("%d components with footprint \"none\" omitted from %s.\n",
1513 n_none
, pcb_new_file_name
);
1515 printf ("%d components with empty footprint \"%s\" omitted from %s.\n",
1516 n_empty
, empty_footprint_name
, pcb_new_file_name
);
1517 if (n_changed_value
> 0)
1518 printf ("%d elements had a value change in %s.\n",
1519 n_changed_value
, pcb_file_name
);
1521 printf ("%d elements fixed in %s.\n", n_fixed
, pcb_file_name
);
1522 if (n_PKG_removed_old
> 0) {
1523 printf ("%d elements could not be found.", n_PKG_removed_old
);
1524 if (created_pcb_file
)
1525 printf (" So %s is incomplete.\n", pcb_file_name
);
1529 if (n_PKG_removed_new
> 0) {
1530 printf ("%d elements could not be found.", n_PKG_removed_new
);
1531 if (created_pcb_file
)
1532 printf (" So %s is incomplete.\n", pcb_new_file_name
);
1536 if (n_preserved
> 0)
1537 printf ("%d elements not in the schematic preserved in %s.\n",
1538 n_preserved
, pcb_file_name
);
1540 /* Tell user what to do next */
1544 if (n_added_ef
+ n_added_m4
> 0) {
1546 printf ("\nNext step:\n");
1547 printf ("1. Run pcb on your file %s.\n", pcb_file_name
);
1549 (" You will find all your footprints in a bundle ready for you to place\n");
1551 (" or disperse with \"Select -> Disperse all elements\" in PCB.\n\n");
1553 ("2. From within PCB, select \"File -> Load netlist file\" and select \n");
1554 printf (" %s to load the netlist.\n\n", net_file_name
);
1555 printf ("3. From within PCB, enter\n\n");
1556 printf (" :ExecuteFile(%s)\n\n", pins_file_name
);
1558 (" to propagate the pin names of all footprints to the layout.\n\n");
1559 } else if (quiet_mode
== FALSE
) {
1560 printf ("\nNext steps:\n");
1561 printf ("1. Run pcb on your file %s.\n", pcb_file_name
);
1563 ("2. From within PCB, select \"File -> Load layout data to paste buffer\"\n");
1565 (" and select %s to load the new footprints into your existing layout.\n",
1568 ("3. From within PCB, select \"File -> Load netlist file\" and select \n");
1569 printf (" %s to load the updated netlist.\n\n", net_file_name
);
1570 printf ("4. From within PCB, enter\n\n");
1571 printf (" :ExecuteFile(%s)\n\n", pins_file_name
);
1572 printf (" to update the pin names of all footprints.\n\n");
1576 g_free (net_file_name
);
1577 g_free (pins_file_name
);
1578 g_free (pcb_file_name
);
1579 g_free (bak_file_name
);