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_command
,
71 *m4_pcbdir
, *default_m4_pcbdir
, *m4_files
, *m4_override_file
;
73 static gboolean use_m4
= TRUE
;
75 static gchar
*empty_footprint_name
;
84 n_preserved
, n_changed_value
, n_not_found
, n_unknown
, n_none
, n_empty
;
86 static gboolean remove_unfound_elements
= TRUE
,
88 force_element_files
, preserve
, fix_elements
, bak_done
, need_PKG_purge
;
92 create_m4_override_file ()
96 m4_override_file
= "gnet-gsch2pcb-tmp.scm";
97 f
= fopen (m4_override_file
, "wb");
99 m4_override_file
= NULL
;
103 fprintf (f
, "(define m4-command \"%s\")\n", m4_command
);
105 fprintf (f
, "(define m4-pcbdir \"%s\")\n", m4_pcbdir
);
107 fprintf (f
, "(define m4-files \"%s\")\n", m4_files
);
108 fprintf (f
, "(define gsch2pcb:use-m4 %s)\n", use_m4
== TRUE
? "#t" : "#f");
112 printf ("Default m4-pcbdir: %s\n", default_m4_pcbdir
);
113 printf ("--------\ngnet-gsch2pcb-tmp.scm override file:\n");
115 printf (" (define m4-command \"%s\")\n", m4_command
);
117 printf (" (define m4-pcbdir \"%s\")\n", m4_pcbdir
);
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
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
136 build_and_run_command (const gchar
*format
, ...)
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
*));
161 /* bare string, use as is */
162 tmp
= g_list_append (tmp
, chunk
);
168 /* we have something in the list, build & call command */
171 gchar
** args
= g_new0 (gchar
*, g_list_length (tmp
) + 1/* NULL terminate the list */);
174 printf ("Running command:\n\t");
176 for (p
= tmp
; p
; p
= g_list_next (p
)) {
177 args
[i
++] = (gchar
*) p
->data
;
179 printf ("%s ", (char*)p
->data
);
183 printf ("\n%s", SEP_STRING
);
185 if (g_spawn_sync (".", /* Working directory */
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)
200 fputs(standard_error
, stderr
);
204 fprintf(stderr
, "Failed to execute external program: %s\n", error
->message
);
209 printf ("\n%s", SEP_STRING
);
211 g_free(standard_error
);
214 /* free the list, but leave data untouched */
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).
229 run_gnetlist (gchar
* pins_file
, gchar
* net_file
, gchar
* pcb_file
,
230 gchar
* basename
, GList
* largs
)
234 static const gchar
*gnetlist
= NULL
;
236 GList
*verboseList
= 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";
249 verboseList
= g_list_append (verboseList
, "-q");
251 if (!build_and_run_command ("%s %l -g pcbpins -o %s %l %l",
255 extra_gnetlist_arg_list
,
259 if (!build_and_run_command ("%s %l -g PCB -o %s %l %l",
263 extra_gnetlist_arg_list
,
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",
280 extra_gnetlist_arg_list
,
282 if (stat (pcb_file
, &st
) != 0 || mtime
== st
.st_mtime
) {
284 "gsch2pcb: gnetlist command failed, `%s' not updated\n",
287 if (m4_override_file
)
289 " At least gnetlist 20030901 is required for m4-xxx options.\n");
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 ");
304 out_file
= g_strconcat (basename
, ".", s
, NULL
);
305 backend
= g_strdup (s
);
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",
316 extra_gnetlist_arg_list
,
324 g_list_free (verboseList
);
330 token (gchar
* string
, gchar
** next
, gboolean
* quoted_ret
)
334 gboolean quoted
= FALSE
;
341 return g_strdup ("");
343 while (*str
== ' ' || *str
== '\t' || *str
== ',' || *str
== '\n')
350 for (s
= str
; *s
&& *s
!= '"' && *s
!= '\n'; ++s
);
355 *s
&& (*s
!= ' ' && *s
!= '\t' && *s
!= ',' && *s
!= '\n'); ++s
);
357 ret
= g_strndup (str
, s
- str
);
358 str
= (quoted
&& *s
) ? s
+ 1 : s
;
365 fix_spaces (gchar
* str
)
371 for (s
= str
; *s
; ++s
)
372 if (*s
== ' ' || *s
== '\t')
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.
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))
402 el
= g_new0 (PcbElement
, 1);
405 while (*s
== ' ' || *s
== '\t')
409 el
->hi_res_format
= TRUE
;
410 else if (*s
!= '(') {
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
);
427 s
= token (NULL
, &t
, NULL
);
431 el
->tail
= g_strdup (t
? t
: "");
432 if ((s
= strrchr (el
->tail
, (gint
) '\n')) != NULL
)
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
) {
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
458 if (!*el
->refdes
|| !isalnum ((gint
) (*el
->refdes
)))
459 el
->still_exists
= TRUE
;
465 pcb_element_free (PcbElement
* el
)
470 g_free (el
->description
);
471 g_free (el
->changed_description
);
472 g_free (el
->changed_value
);
476 g_free (el
->pkg_name_fix
);
481 get_pcb_element_list (gchar
* pcb_file
)
487 if ((f
= fopen (pcb_file
, "r")) == NULL
)
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
;
495 if ((el
= pcb_element_line_parse (s
)) == NULL
)
497 pcb_element_list
= g_list_append (pcb_element_list
, el
);
503 pcb_element_exists (PcbElement
* el_test
, gboolean record
)
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
))
513 if (strcmp (el_test
->description
, el
->description
)) { /* footprint */
515 el
->changed_description
= g_strdup (el_test
->description
);
518 if (strcmp (el_test
->value
, el
->value
))
519 el
->changed_value
= g_strdup (el_test
->value
);
520 el
->still_exists
= TRUE
;
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().
543 simple_translate (PcbElement
* el
)
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
;
557 insert_element (FILE * f_out
, gchar
* element_file
,
558 gchar
* footprint
, gchar
* refdes
, gchar
* value
)
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
);
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",
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";
597 el
->res_char
, el
->flags
, footprint
, refdes
, value
,
598 el
->x
, el
->y
, el
->tail
);
600 } else if (*s
!= '#')
602 pcb_element_free (el
);
610 find_element (gchar
* dir_path
, gchar
* element
)
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
);
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
);
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 */
634 printf ("\t : %s\t", name
);
635 if (!strcmp (name
, element
))
636 found
= g_strdup (path
);
639 tmps
= g_strconcat (element
, ".fp", NULL
);
640 if (!strcmp (name
, tmps
))
641 found
= g_strdup (path
);
645 printf ("%s\n", found
? "Yes" : "No");
656 search_element_directories (PcbElement
* el
)
659 gchar
*s
, *elname
= NULL
, *dir_path
, *path
= NULL
;
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
);
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");
687 elname
= g_strdup (el
->description
);
689 if (!strcmp (elname
, "unknown")) {
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
;
698 printf ("\tLooking in directory: \"%s\"\n", dir_path
);
699 path
= find_element (dir_path
, elname
);
702 printf ("\tFound: %s\n", 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
732 * 100-Pin-jack -> 100 Pin jack
735 pkg_to_element (FILE * f
, gchar
* pkg_line
)
739 gint n
, n_extra_args
, n_dashes
;
741 if (strncmp (pkg_line
, "PKG_", 4)
742 || (s
= strchr (pkg_line
, (gint
) '(')) == 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
);
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
)
761 /* If the component value has a comma, eg "1k, 1%", the gnetlist generated
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
);
778 for (n_dashes
= 0; (s
= strchr (s
+ 1, '-')) != NULL
; ++n_dashes
);
781 if (n_extra_args
== n_dashes
+ 1) { /* Assume there was a comma in the value, eg "1K, 1%" */
783 el
->value
= g_strconcat (s
, ",", fix_spaces (args
[n
]), NULL
);
785 if ((s
= strchr (el
->value
, (gint
) ')')) != NULL
)
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
);
796 if ((s
= strchr (el
->pkg_name_fix
, (gint
) ')')) != NULL
)
801 if (empty_footprint_name
&& !strcmp (el
->description
, empty_footprint_name
)) {
804 ("%s: has the empty footprint attribute \"%s\" so won't be in the layout.\n",
805 el
->refdes
, el
->description
);
808 } else if (!strcmp (el
->description
, "none")) {
810 "WARNING: %s has a footprint attribute \"%s\" so won't be in the layout.\n",
811 el
->refdes
, el
->description
);
814 } else if (!strcmp (el
->description
, "unknown")) {
816 "WARNING: %s has no footprint attribute so won't be in the layout.\n",
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.
834 add_elements (gchar
* pcb_file
)
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
)
844 tmp_file
= g_strconcat (pcb_file
, ".tmp", NULL
);
845 if ((f_out
= fopen (tmp_file
, "wb")) == NULL
) {
850 while ((fgets (buf
, sizeof (buf
), f_in
)) != NULL
) {
851 for (s
= buf
; *s
== ' ' || *s
== '\t'; ++s
);
855 else if (*s
== ')' && --paren_level
<= 0)
860 if ((el
= pcb_element_line_parse (s
)) != NULL
)
863 el
= pkg_to_element (f_out
, s
);
864 if (el
&& pcb_element_exists (el
, TRUE
)) {
866 pcb_element_free (el
);
869 if (!el
|| el
->omit_PKG
) {
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
)
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
)) {
894 printf ("%s: added new file element for footprint %s (value=%s)\n",
895 el
->refdes
, el
->description
, el
->value
);
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
) {
902 "So device %s will not be in the layout.\n", el
->refdes
);
906 fputs (buf
, f_out
); /* Copy PKG_ line */
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
);
925 total
= n_added_ef
+ n_added_m4
+ n_not_found
;
927 build_and_run_command ("rm %s", tmp_file
);
929 build_and_run_command ("mv %s %s", tmp_file
, pcb_file
);
935 update_element_descriptions (gchar
* pcb_file
, gchar
* bak
)
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
)
947 if (!pcb_element_list
|| n_fixed
== 0) {
948 fprintf (stderr
, "Could not find any elements to fix.\n");
951 if ((f_in
= fopen (pcb_file
, "r")) == NULL
)
953 tmp
= g_strconcat (pcb_file
, ".tmp", NULL
);
954 if ((f_out
= fopen (tmp
, "wb")) == NULL
) {
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";
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
;
975 pcb_element_free (el
);
981 build_and_run_command ("mv %s %s", pcb_file
, bak
);
985 build_and_run_command ("mv %s %s", tmp
, pcb_file
);
990 prune_elements (gchar
* pcb_file
, gchar
* bak
)
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
) {
1005 "Preserving PCB element not in the schematic: %s (element %s)\n",
1006 el
->refdes
, el
->description
);
1009 } else if (el
->changed_value
)
1012 if (!pcb_element_list
1013 || (n_deleted
== 0 && !need_PKG_purge
&& n_changed_value
== 0)
1016 if ((f_in
= fopen (pcb_file
, "r")) == NULL
)
1018 tmp
= g_strconcat (pcb_file
, ".tmp", NULL
);
1019 if ((f_out
= fopen (tmp
, "wb")) == NULL
) {
1023 while ((fgets (buf
, sizeof (buf
), f_in
)) != NULL
) {
1024 for (s
= buf
; *s
== ' ' || *s
== '\t'; ++s
);
1028 else if (*s
== ')' && --paren_level
<= 0)
1033 if ((el
= pcb_element_line_parse (s
)) != NULL
1034 && (el_exists
= pcb_element_exists (el
, FALSE
)) != NULL
1035 && !el_exists
->still_exists
&& !preserve
) {
1038 printf ("%s: deleted element %s (value=%s)\n",
1039 el
->refdes
, el
->description
, el
->value
);
1040 pcb_element_free (el
);
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
);
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
;
1058 pcb_element_free (el
);
1064 build_and_run_command ("mv %s %s", pcb_file
, bak
);
1068 build_and_run_command ("mv %s %s", tmp
, pcb_file
);
1073 add_m4_file (gchar
* arg
)
1078 m4_files
= g_strdup (arg
);
1081 m4_files
= g_strconcat (m4_files
, " ", arg
, NULL
);
1087 expand_dir (gchar
* dir
)
1092 s
= g_build_filename ((gchar
*) g_get_home_dir (), dir
+ 1, NULL
);
1099 add_default_m4_files (void)
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
))
1109 if (g_file_test (DEFAULT_PCB_INC
, G_FILE_TEST_IS_REGULAR
))
1110 add_m4_file (DEFAULT_PCB_INC
);
1115 add_schematic (gchar
* sch
)
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
);
1124 add_multiple_schematics (gchar
* sch
)
1126 /* parse the string using shell semantics */
1128 gchar
** args
= NULL
;
1129 GError
* error
= NULL
;
1131 if (g_shell_parse_argv (sch
, &count
, &args
, &error
)) {
1133 for (i
= 0; i
< count
; ++i
)
1135 add_schematic (args
[i
]);
1140 "invalid `schematics' option: %s\n",
1142 g_error_free (error
);
1147 parse_config (gchar
* config
, gchar
* arg
)
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
))
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
;
1167 if (!strcmp (config
, "keep-unfound") || !strcmp (config
, "k")) {
1168 remove_unfound_elements
= FALSE
;
1171 if (!strcmp (config
, "quiet") || !strcmp (config
, "q")) {
1175 if (!strcmp (config
, "preserve") || !strcmp (config
, "p")) {
1179 if (!strcmp (config
, "use-files") || !strcmp (config
, "f")) {
1180 force_element_files
= TRUE
;
1183 if (!strcmp (config
, "skip-m4") || !strcmp (config
, "s")) {
1187 if (!strcmp (config
, "elements-dir") || !strcmp (config
, "d")) {
1189 printf ("\tAdding directory to file element directory list: %s\n",
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"))
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
);
1214 load_project (gchar
* path
)
1217 gchar
*s
, buf
[1024], config
[32], arg
[768];
1219 f
= fopen (path
, "r");
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
== ';')
1229 sscanf (s
, "%31s %767[^\n]", config
, arg
);
1230 parse_config (config
, arg
);
1236 load_extra_project_files (void)
1239 static gboolean done
= FALSE
;
1244 load_project ("/etc/gsch2pcb");
1245 load_project ("/usr/local/etc/gsch2pcb");
1247 path
= g_build_filename ((gchar
*) g_get_home_dir (), ".gEDA",
1249 load_project (path
);
1255 static gchar
*usage_string0
=
1256 "usage: gsch2pcb [options] {project | foo.sch [foo1.sch ...]}\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"
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"
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"
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"
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"
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"
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"
1332 "Additional Resources:\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";
1341 puts (usage_string0
);
1342 printf (" %s\n", default_m4_pcbdir
);
1343 puts (usage_string1
);
1348 get_args (gint argc
, gchar
** argv
)
1350 gchar
*opt
, *arg
, *s
;
1353 for (i
= 1; i
< argc
; ++i
) {
1360 if (!strcmp (opt
, "version") || !strcmp (opt
, "V")) {
1361 printf ("gsch2pcb %s\n", GSC2PCB_VERSION
);
1363 } else if (!strcmp (opt
, "verbose") || !strcmp (opt
, "v")) {
1366 } else if (!strcmp (opt
, "fix-elements")) {
1367 fix_elements
= TRUE
;
1369 } else if (!strcmp (opt
, "gnetlist-arg")) {
1370 extra_gnetlist_arg_list
=
1371 g_list_append (extra_gnetlist_arg_list
, g_strdup (arg
));
1374 } else if (!strcmp (opt
, "help") || !strcmp (opt
, "h"))
1377 && ((r
= parse_config (opt
, (i
< argc
- 1) ? arg
: NULL
))
1383 printf ("gsch2pcb: bad or incomplete arg: %s\n", argv
[i
]);
1386 if (!g_str_has_suffix (argv
[i
], ".sch")) {
1387 load_extra_project_files ();
1388 load_project (argv
[i
]);
1390 add_schematic (argv
[i
]);
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
;
1401 gboolean initial_pcb
= TRUE
;
1402 gboolean created_pcb_file
= TRUE
;
1404 const char *pcbdata_path
;
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
);
1414 /* Use the default value passed in from the configure script
1415 * instead of trying to hard code a value which is very
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 ();
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 ":"
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
)) {
1445 printf ("Adding %s to the newlib search path\n", p
);
1446 element_directory_list
= g_list_append (element_directory_list
,
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
);
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
);
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");
1477 if (add_elements (pcb_new_file_name
) == 0) {
1478 build_and_run_command ("rm %s", pcb_new_file_name
);
1480 printf ("No elements found, so nothing to do.\n");
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 */
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
);
1512 printf ("%d components had no footprint attribute and are omitted.\n",
1515 printf ("%d components with footprint \"none\" omitted from %s.\n",
1516 n_none
, pcb_new_file_name
);
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
);
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
);
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
);
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 */
1547 if (n_added_ef
+ n_added_m4
> 0) {
1549 printf ("\nNext step:\n");
1550 printf ("1. Run pcb on your file %s.\n", pcb_file_name
);
1552 (" You will find all your footprints in a bundle ready for you to place\n");
1554 (" or disperse with \"Select -> Disperse all elements\" in PCB.\n\n");
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
);
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
);
1566 ("2. From within PCB, select \"File -> Load layout data to paste buffer\"\n");
1568 (" and select %s to load the new footprints into your existing layout.\n",
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
);