5 | Bill Wilson billw@wt.net
7 | This program is free software which I release under the GNU General Public
8 | License. You may redistribute and/or modify this program under the terms
9 | of that license as published by the Free Software Foundation; either
10 | version 2 of the License, or (at your option) any later version.
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details. Version 2 is in the
16 | COPYRIGHT file in the top level directory of this distribution.
18 | To get a copy of the GNU General Puplic License, write to the Free Software
19 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
36 #if !GLIB_CHECK_VERSION(2,0,0)
37 #include "glib12-compat.c"
40 #ifdef HAVE_LIBDMALLOC
44 #define GSC2PCB_VERSION "1.6"
46 #define DEFAULT_PCB_INC "pcb.inc"
48 #define SEP_STRING "--------\n"
64 gboolean still_exists
,
80 static GList
*pcb_element_list
,
81 *element_directory_list
,
84 static gchar
*schematics
,
87 static gchar
*m4_command
,
93 static gboolean use_m4
= TRUE
;
95 static gchar
*empty_footprint_name
;
111 static gboolean remove_unfound_elements
= TRUE
,
121 create_m4_override_file()
125 m4_override_file
= "gnet-gsch2pcb-tmp.scm";
126 f
= fopen(m4_override_file
, "wb");
129 m4_override_file
= NULL
;
133 fprintf(f
, "(define m4-command \"%s\")\n", m4_command
);
135 fprintf(f
, "(define m4-pcbdir \"%s\")\n", m4_pcbdir
);
137 fprintf(f
, "(define m4-files \"%s\")\n", m4_files
);
138 fprintf(f
, "(define gsch2pcb:use-m4 %s)\n", use_m4
== TRUE
? "#t" : "#f");
143 printf("Default m4-pcbdir: %s\n", default_m4_pcbdir
);
144 printf("--------\ngnet-gsch2pcb-tmp.scm override file:\n");
146 printf(" (define m4-command \"%s\")\n", m4_command
);
148 printf(" (define m4-pcbdir \"%s\")\n", m4_pcbdir
);
150 printf(" (define m4-files \"%s\")\n", m4_files
);
151 printf(" (define gsch2pcb:use-m4 %s)\n", use_m4
== TRUE
? "#t" : "#f");
155 /* Run gnetlist to generate a netlist and a PCB board file. gnetlist
156 | has exit status of 0 even if it's given an invalid arg, so do some
157 | stat() hoops to decide if gnetlist successfully generated the PCB
158 | board file (only gnetlist >= 20030901 recognizes -m).
161 run_gnetlist(gchar
*pins_file
, gchar
*net_file
, gchar
*pcb_file
, gchar
*basename
, gchar
*args
)
170 static const gchar
*gnetlist
= NULL
;
173 * Allow the user to specify a full path or a different name for
174 * the gnetlist command. Especially useful if multiple copies
175 * are installed at once.
177 if (gnetlist
== NULL
)
178 gnetlist
= g_getenv ("GNETLIST");
179 if (gnetlist
== NULL
)
180 gnetlist
= "gnetlist";
184 command
= g_strconcat(
185 gnetlist
, " -g pcbpins -o ", pins_file
, " ", args
, NULL
);
186 printf("Running command:\n\t%s\n", command
);
190 command
= g_strconcat(
191 gnetlist
, " -q -g pcbpins -o ", pins_file
, " ", args
, NULL
);
192 g_spawn_command_line_sync(command
, NULL
, NULL
, NULL
, NULL
);
197 command
= g_strconcat(
198 gnetlist
, " -g PCB -o ", net_file
, " ", args
, NULL
);
199 printf("Running command:\n\t%s\n", command
);
203 command
= g_strconcat(
204 gnetlist
, " -q -g PCB -o ", net_file
, " ", args
, NULL
);
205 g_spawn_command_line_sync(command
, NULL
, NULL
, NULL
, NULL
);
208 create_m4_override_file();
209 if (m4_override_file
)
210 args1
= g_strconcat("-m ", m4_override_file
, " ", args
, NULL
);
212 args1
= g_strdup(args
);
214 mtime
= (stat(pcb_file
, &st
) == 0) ? st
.st_mtime
: 0;
219 command
= g_strconcat(gnetlist
, " -g gsch2pcb -o ", pcb_file
,
221 printf("Running command:\n\t%s\n", command
);
225 command
= g_strconcat(gnetlist
, " -q -g gsch2pcb -o ", pcb_file
,
228 g_spawn_command_line_sync(command
, NULL
, NULL
, NULL
, NULL
);
233 if ( stat(pcb_file
, &st
) != 0
234 || mtime
== st
.st_mtime
237 fprintf(stderr
, "gsch2pcb: gnetlist command (%s) failed.\n", command
);
238 if (m4_override_file
)
240 " At least gnetlist 20030901 is required for m4-xxx options.\n");
245 if (m4_override_file
)
246 unlink(m4_override_file
);
248 for (list
= extra_gnetlist_list
; list
; list
= g_list_next(list
))
250 s
= (gchar
*) list
->data
;
251 if (!strstr(s
, " -o "))
252 out_file
= g_strconcat(" -o ", basename
, ".", s
, NULL
);
254 out_file
= g_strdup(" ");
259 command
= g_strconcat(gnetlist
, " -g ", s
, out_file
,
261 printf("Running command:\n\t%s\n", command
);
265 command
= g_strconcat(gnetlist
, " -q -g ", s
, out_file
,
268 g_spawn_command_line_sync(command
, NULL
, NULL
, NULL
, NULL
);
277 token(gchar
*string
, gchar
**next
, gboolean
*quoted_ret
)
281 gboolean quoted
= FALSE
;
291 while (*str
== ' ' || *str
== '\t' || *str
== ',' || *str
== '\n')
299 for (s
= str
; *s
&& *s
!= '"' && *s
!= '\n'; ++s
)
307 *s
&& (*s
!= ' ' && *s
!= '\t' && *s
!= ',' && *s
!= '\n');
311 ret
= g_strndup(str
, s
- str
);
312 str
= (quoted
&& *s
) ? s
+ 1 : s
;
319 fix_spaces(gchar
*str
)
325 for (s
= str
; *s
; ++s
)
326 if (*s
== ' ' || *s
== '\t')
331 /* As of 1/9/2004 CVS hi_res Element[] line format:
332 | Element[element_flags, description, pcb-name, value, mark_x, mark_y,
333 | text_x, text_y, text_direction, text_scale, text_flags]
334 | New PCB 1.7 / 1.99 Element() line format:
335 | Element(element_flags, description, pcb-name, value, mark_x, mark_y,
336 | text_x, text_y, text_direction, text_scale, text_flags)
337 | Old PCB 1.6 Element() line format:
338 | Element(element_flags, description, pcb-name, value,
339 | text_x, text_y, text_direction, text_scale, text_flags)
341 | (mark_x, mark_y) is the element position (mark) and (text_x,text_y)
342 | is the description text position which is absolute in pre 1.7 and
343 | is now relative. The hi_res mark_x,mark_y and text_x,text_y resolutions
344 | are 100x the other formats.
347 pcb_element_line_parse(gchar
*line
)
349 PcbElement
*el
= NULL
;
350 gchar
*s
, *t
, close_char
;
351 gint state
= 0, elcount
= 0;
353 if (strncmp(line
, "Element", 7))
356 el
= g_new0(PcbElement
, 1);
359 while (*s
== ' ' || *s
== '\t')
363 el
->hi_res_format
= TRUE
;
370 el
->res_char
= el
->hi_res_format
? '[' : '(';
371 close_char
= el
->hi_res_format
? ']' : ')';
373 el
->flags
= token(s
+ 1, NULL
, &el
->quoted_flags
);
374 el
->description
= token(NULL
, NULL
, NULL
);
375 el
->refdes
= token(NULL
, NULL
, NULL
);
376 el
->value
= token(NULL
, NULL
, NULL
);
378 s
= token(NULL
, NULL
, NULL
);
382 s
= token(NULL
, &t
, NULL
);
386 el
->tail
= g_strdup(t
? t
: "");
387 if ((s
= strrchr(el
->tail
, (gint
) '\n')) != NULL
)
390 /* Count the tokens in tail to decide if it's new or old format.
391 | Old format will have 3 tokens, new format will have 5 tokens.
393 for (s
= el
->tail
; *s
&& *s
!= close_char
; ++s
)
405 el
->new_format
= TRUE
;
407 fix_spaces(el
->description
);
408 fix_spaces(el
->refdes
);
409 fix_spaces(el
->value
);
411 /* Don't allow elements with no refdes to ever be deleted
412 | because they may be desired pc board elements not in schematics.
413 | So initialize still_exists to TRUE if empty or non-alphanumeric refdes.
415 if (!*el
->refdes
|| !isalnum((gint
) (*el
->refdes
)))
416 el
->still_exists
= TRUE
;
422 pcb_element_free(PcbElement
*el
)
427 g_free(el
->description
);
428 g_free(el
->changed_description
);
429 g_free(el
->changed_value
);
433 g_free(el
->pkg_name_fix
);
438 get_pcb_element_list(gchar
*pcb_file
)
444 if ((f
= fopen(pcb_file
, "r")) == NULL
)
446 while ((fgets(buf
, sizeof(buf
), f
)) != NULL
)
448 for (s
= buf
; *s
== ' ' || *s
== '\t'; ++s
)
450 if (!strncmp(s
, "PKG_", 4))
452 need_PKG_purge
= TRUE
;
455 if ((el
= pcb_element_line_parse(s
)) == NULL
)
457 pcb_element_list
= g_list_append(pcb_element_list
, el
);
463 pcb_element_exists(PcbElement
*el_test
, gboolean record
)
468 for (list
= pcb_element_list
; list
; list
= g_list_next(list
))
470 el
= (PcbElement
*) list
->data
;
472 if (strcmp(el_test
->refdes
, el
->refdes
))
474 if (strcmp(el_test
->description
, el
->description
)) /* footprint */
477 el
->changed_description
= g_strdup(el_test
->description
);
483 if (strcmp(el_test
->value
, el
->value
))
484 el
->changed_value
= g_strdup(el_test
->value
);
485 el
->still_exists
= TRUE
;
493 /* A problem is that new PCB 1.7 file elements have the (mark_x,mark_y)
494 | value set to wherever the element was created and no equivalent of a
495 | gschem translate symbol was done.
496 | So, file elements inserted can be scattered over a big area and this is
497 | bad when loading a file.new.pcb into an existing PC board. So, do a
498 | simple translate if (mark_x,mark_y) is (arbitrarily) over 1000. I'll
499 | assume that for values < 1000 the element creator was concerned with a
500 | sane initial element placement. Unless someone has a better idea?
501 | Don't bother with pre PCB 1.7 formats as that would require parsing
502 | the mark(). Current m4 elements use the old format but they seem to
503 | have a reasonable initial mark().
506 simple_translate(PcbElement
*el
)
512 factor
= el
->hi_res_format
? 100 : 1;
513 if (el
->x
> 1000 * factor
)
514 el
->x
= 500 * factor
;
515 if (el
->y
> 1000 * factor
)
516 el
->y
= 500 * factor
;
521 insert_element(FILE *f_out
, gchar
*element_file
,
522 gchar
*footprint
, gchar
*refdes
, gchar
*value
)
526 gchar
*fmt
, *s
, buf
[1024];
527 gboolean retval
= FALSE
;
529 if ((f_in
= fopen(element_file
, "r")) == NULL
)
531 s
= g_strdup_printf("insert_element() can't open %s", element_file
);
536 /* Copy the file element lines. Substitute new parameters into the
537 | Element() or Element[] line and strip comments.
539 while ((fgets(buf
, sizeof(buf
), f_in
)) != NULL
)
541 for (s
= buf
; *s
== ' ' || *s
== '\t'; ++s
)
543 if ((el
= pcb_element_line_parse(s
)) != NULL
)
545 simple_translate(el
);
546 fmt
= el
->quoted_flags
?
547 "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %d %d%s\n" :
548 "Element%c%s \"%s\" \"%s\" \"%s\" %d %d%s\n";
551 el
->res_char
, el
->flags
, footprint
, refdes
, value
,
552 el
->x
, el
->y
, el
->tail
);
557 pcb_element_free(el
);
565 find_element(gchar
*dir_path
, gchar
*element
)
568 gchar
*path
, *name
, *s
, *found
= NULL
;
570 if ((dir
= g_dir_open(dir_path
, 0, NULL
)) == NULL
)
572 s
= g_strdup_printf("find_element can't open dir \"%s\"", dir_path
);
578 printf("\t Searching: \"%s\" for \"%s\"\n", dir_path
, element
);
579 while ((name
= (gchar
*) g_dir_read_name(dir
)) != NULL
)
581 path
= g_strconcat(dir_path
, "/", name
, NULL
);
584 /* if we got a directory name, then recurse down into it */
585 if (g_file_test(path
, G_FILE_TEST_IS_DIR
))
586 found
= find_element(path
, element
);
588 /* otherwise assume it is a file and see if it is the one we want */
592 printf("\t : %s\t", name
);
593 if (!strcmp(name
, element
))
594 found
= g_strdup(path
);
598 tmps
= g_strconcat (element
, ".fp", NULL
);
599 if (!strcmp(name
, tmps
))
600 found
= g_strdup(path
);
604 printf("%s\n", found
? "Yes" : "No");
615 search_element_directories( PcbElement
*el
)
618 gchar
*s
, *elname
= NULL
, *dir_path
, *path
= NULL
;
621 /* See comment before pkg_to_element() */
622 if (el
->pkg_name_fix
)
624 if (strchr(el
->description
, '-'))
626 n1
= strlen(el
->description
);
627 n2
= strlen(el
->pkg_name_fix
);
628 s
= el
->description
+ n1
- n2
- 1;
630 // printf("n1=%d n2=%d desc:%s fix:%s s:%s\n",
631 // n1, n2, el->description, el->pkg_name_fix, s);
633 if ( n1
> 0 && n2
< n1
635 && *(s
+ 1) == *el
->pkg_name_fix
638 s
= g_strndup(el
->description
, n1
- n2
- 1);
639 elname
= g_strconcat(s
, " ", el
->pkg_name_fix
, NULL
);
645 printf("Warning: argument passing may have been confused by\n");
646 printf(" a comma in a component value:\n");
647 printf(" Check %s %s %s\n",
648 el
->refdes
, el
->description
, el
->value
);
649 printf(" Maybe just use a space instead of a comma?\n");
653 elname
= g_strdup(el
->description
);
655 if (!strcmp(elname
, "unknown"))
661 printf("\tSearching directories looking for file element: %s\n",
663 for (list
= element_directory_list
; list
; list
= g_list_next(list
))
665 dir_path
= (gchar
*) list
->data
;
667 printf("\tLooking in directory: \"%s\"\n", dir_path
);
668 path
= find_element(dir_path
, elname
);
672 printf("\tFound: %s\n", path
);
680 /* The gnetlist backend gnet-gsch2pcb.scm generates PKG_ lines:
682 | PKG_footprint(footprint{-fp0-fp1},refdes,value{,fp0,fp1})
684 | where fp1 and fp2 (if they exist) are the extra footprint components when
685 | specifying footprints like "DIL 14 300". This is needed for m4 macros.
686 | A complication is if the footprint references a file element with spaces
687 | embedded in the name. The gnetlist backend will interpret these as
688 | fp0, fp1, ... args and the footprint will in this case incorrectly have
689 | '-' inserted where the spaces should be. So, if there are additional
690 | args, reconstruct the portion of the name given by the args with spaces
691 | for later use. Eg. if the footprint is "100 Pin jack", we will have
692 | PKG_100-Pin-jack(100-Pin-jack,refdes,value,Pin,jack)
693 | So put "Pin jack" into pkg_name_fix so if this element is searched
694 | as a file element we can munge the description to what it should be, eg:
695 | 100-Pin-jack -> 100 Pin jack
698 pkg_to_element(FILE *f
, gchar
*pkg_line
)
702 gint n
, n_extra_args
, n_dashes
;
704 if ( strncmp(pkg_line
, "PKG_", 4)
705 || (s
= strchr(pkg_line
, (gint
) '(')) == NULL
709 args
= g_strsplit(s
+ 1, ",", 12);
710 if (!args
[0] || !args
[1] || !args
[2])
712 fprintf(stderr
, "Bad package line: %s\n", pkg_line
);
719 el
= g_new0(PcbElement
, 1);
720 el
->description
= g_strdup(args
[0]);
721 el
->refdes
= g_strdup(args
[1]);
722 el
->value
= g_strdup(args
[2]);
723 if ((s
= strchr(el
->value
, (gint
) ')')) != NULL
)
726 /* If the component value has a comma, eg "1k, 1%", the gnetlist generated
727 | PKG line will be PKG_XXX(`R0w8',`R100',`1k, 1%'), but after processed
728 | by m4, the input to gsch2pcb will be PKG_XXX(R0w8,R100,1k, 1%). So the
729 | quoting info has been lost when processing for file elements. So here
730 | try to detect and fix this. But I can't handle the situation where
731 | the description has a '-' and the value has a comma because
732 | gnet-gsch2pcb.scm munges the description with '-' when there are
735 for (n_extra_args
= 0; args
[3 + n_extra_args
] != NULL
; ++n_extra_args
)
738 for (n_dashes
= 0; (s
= strchr(s
+ 1, '-')) != NULL
; ++n_dashes
)
742 if (n_extra_args
== n_dashes
+ 1)
743 { /* Assume there was a comma in the value, eg "1K, 1%" */
745 el
->value
= g_strconcat(s
, ",", fix_spaces(args
[n
]), NULL
);
747 if ((s
= strchr(el
->value
, (gint
) ')')) != NULL
)
753 el
->pkg_name_fix
= g_strdup(args
[n
]);
754 for (n
+= 1; args
[n
] != NULL
; ++n
)
756 s
= el
->pkg_name_fix
;
757 el
->pkg_name_fix
= g_strconcat(s
, " ", args
[n
], NULL
);
760 if ((s
= strchr(el
->pkg_name_fix
, (gint
) ')')) != NULL
)
765 if (empty_footprint_name
&& !strcmp(el
->description
, empty_footprint_name
))
769 "%s: has the empty footprint attribute \"%s\" so won't be in the layout.\n",
770 el
->refdes
, el
->description
);
774 else if (!strcmp(el
->description
, "none"))
777 "WARNING: %s has a footprint attribute \"%s\" so won't be in the layout.\n",
778 el
->refdes
, el
->description
);
782 else if (!strcmp(el
->description
, "unknown"))
785 "WARNING: %s has no footprint attribute so won't be in the layout.\n",
793 /* Process the newly created pcb file which is the output from
794 | gnetlist -g gsch2pcb ...
795 | It will have elements found via the m4 interface and PKG_ lines for
796 | elements not found.
797 | Insert pcb file elements for PKG_ lines if file elements can be found.
798 | If there was an existing pcb file, strip out any elements if they are
799 | already present so that the new pcb file will only have new elements.
802 add_elements(gchar
*pcb_file
)
805 PcbElement
*el
= NULL
;
806 gchar
*command
, *p
, *tmp_file
, *s
, buf
[1024];
807 gint total
, paren_level
= 0;
808 gboolean is_m4
, skipping
= FALSE
;
810 if ((f_in
= fopen(pcb_file
, "r")) == NULL
)
812 tmp_file
= g_strconcat(pcb_file
, ".tmp", NULL
);
813 if ((f_out
= fopen(tmp_file
, "wb")) == NULL
)
819 while ((fgets(buf
, sizeof(buf
), f_in
)) != NULL
)
821 for (s
= buf
; *s
== ' ' || *s
== '\t'; ++s
)
827 else if (*s
== ')' && --paren_level
<= 0)
832 if ((el
= pcb_element_line_parse(s
)) != NULL
)
835 el
= pkg_to_element(f_out
, s
);
836 if (el
&& pcb_element_exists(el
, TRUE
))
839 pcb_element_free(el
);
842 if (!el
|| el
->omit_PKG
)
852 if (!is_m4
|| (is_m4
&& force_element_files
))
854 if (verbose
&& !is_m4
)
856 "%s: need new file element for footprint %s (value=%s)\n",
857 el
->refdes
, el
->description
, el
->value
);
858 if (verbose
&& is_m4
&& force_element_files
)
860 "%s: have m4 element %s, but trying to replace with a file element.\n",
861 el
->refdes
, el
->description
);
862 p
= search_element_directories(el
);
863 if (!p
&& verbose
&& is_m4
&& force_element_files
)
864 printf("\tNo file element found.\n");
866 if (p
&& insert_element(f_out
, p
,
867 el
->description
, el
->refdes
, el
->value
))
874 "%s: added new file element for footprint %s (value=%s)\n",
875 el
->refdes
, el
->description
, el
->value
);
880 "%s: can't find PCB element for footprint %s (value=%s)\n",
881 el
->refdes
, el
->description
, el
->value
);
882 if (remove_unfound_elements
&& !fix_elements
)
885 "So device %s will not be in the layout.\n",
892 fputs(buf
, f_out
); /* Copy PKG_ line */
903 "%s: added new m4 element for footprint %s (value=%s)\n",
904 el
->refdes
, el
->description
, el
->value
);
906 pcb_element_free(el
);
913 total
= n_added_ef
+ n_added_m4
+ n_not_found
;
915 command
= g_strconcat("rm ", tmp_file
, NULL
);
917 command
= g_strconcat("mv ", tmp_file
, " ", pcb_file
, NULL
);
918 g_spawn_command_line_sync(command
, NULL
, NULL
, NULL
, NULL
);
925 update_element_descriptions(gchar
*pcb_file
, gchar
*bak
)
929 PcbElement
*el
, *el_exists
;
930 gchar
*fmt
, *command
, *tmp
, *s
, buf
[1024];
932 for (list
= pcb_element_list
; list
; list
= g_list_next(list
))
934 el
= (PcbElement
*) list
->data
;
935 if (el
->changed_description
)
938 if (!pcb_element_list
|| n_fixed
== 0)
940 fprintf(stderr
, "Could not find any elements to fix.\n");
943 if ((f_in
= fopen(pcb_file
, "r")) == NULL
)
945 tmp
= g_strconcat(pcb_file
, ".tmp", NULL
);
946 if ((f_out
= fopen(tmp
, "wb")) == NULL
)
951 while ((fgets(buf
, sizeof(buf
), f_in
)) != NULL
)
953 for (s
= buf
; *s
== ' ' || *s
== '\t'; ++s
)
955 if ( (el
= pcb_element_line_parse(s
)) != NULL
956 && (el_exists
= pcb_element_exists(el
, FALSE
)) != NULL
957 && el_exists
->changed_description
960 fmt
= el
->quoted_flags
?
961 "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %d %d%s\n" :
962 "Element%c%s \"%s\" \"%s\" \"%s\" %d %d%s\n";
965 el
->flags
, el_exists
->changed_description
,
966 el
->refdes
, el
->value
, el
->x
, el
->y
, el
->tail
);
967 printf("%s: updating element Description: %s -> %s\n",
968 el
->refdes
, el
->description
,
969 el_exists
->changed_description
);
970 el_exists
->still_exists
= TRUE
;
974 pcb_element_free(el
);
981 command
= g_strconcat("mv ", pcb_file
, " ", bak
, NULL
);
982 g_spawn_command_line_sync(command
, NULL
, NULL
, NULL
, NULL
);
987 command
= g_strconcat("mv ", tmp
, " ", pcb_file
, NULL
);
988 g_spawn_command_line_sync(command
, NULL
, NULL
, NULL
, NULL
);
994 prune_elements(gchar
*pcb_file
, gchar
*bak
)
998 PcbElement
*el
, *el_exists
;
999 gchar
*fmt
, *command
, *tmp
, *s
, buf
[1024];
1000 gint paren_level
= 0;
1001 gboolean skipping
= FALSE
;
1003 for (list
= pcb_element_list
; list
; list
= g_list_next(list
))
1005 el
= (PcbElement
*) list
->data
;
1006 if (!el
->still_exists
)
1012 "Preserving PCB element not in the schematic: %s (element %s)\n",
1013 el
->refdes
, el
->description
);
1018 else if (el
->changed_value
)
1021 if ( !pcb_element_list
1022 || (n_deleted
== 0 && !need_PKG_purge
&& n_changed_value
== 0)
1025 if ((f_in
= fopen(pcb_file
, "r")) == NULL
)
1027 tmp
= g_strconcat(pcb_file
, ".tmp", NULL
);
1028 if ((f_out
= fopen(tmp
, "wb")) == NULL
)
1033 while ((fgets(buf
, sizeof(buf
), f_in
)) != NULL
)
1035 for (s
= buf
; *s
== ' ' || *s
== '\t'; ++s
)
1041 else if (*s
== ')' && --paren_level
<= 0)
1046 if ( (el
= pcb_element_line_parse(s
)) != NULL
1047 && (el_exists
= pcb_element_exists(el
, FALSE
)) != NULL
1048 && !el_exists
->still_exists
1055 "%s: deleted element %s (value=%s)\n",
1056 el
->refdes
, el
->description
, el
->value
);
1057 pcb_element_free(el
);
1060 if (el_exists
&& el_exists
->changed_value
)
1062 fmt
= el
->quoted_flags
?
1063 "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %d %d%s\n" :
1064 "Element%c%s \"%s\" \"%s\" \"%s\" %d %d%s\n";
1066 el
->res_char
, el
->flags
, el
->description
, el
->refdes
,
1067 el_exists
->changed_value
, el
->x
, el
->y
, el
->tail
);
1070 "%s: changed element %s value: %s -> %s\n",
1071 el
->refdes
, el
->description
,
1072 el
->value
, el_exists
->changed_value
);
1074 else if (!strncmp(s
, "PKG_", 4))
1075 ++n_PKG_removed_old
;
1078 pcb_element_free(el
);
1085 command
= g_strconcat("mv ", pcb_file
, " ", bak
, NULL
);
1086 g_spawn_command_line_sync(command
, NULL
, NULL
, NULL
, NULL
);
1091 command
= g_strconcat("mv ", tmp
, " ", pcb_file
, NULL
);
1092 g_spawn_command_line_sync(command
, NULL
, NULL
, NULL
, NULL
);
1098 add_m4_file(gchar
*arg
)
1103 m4_files
= g_strdup(arg
);
1107 m4_files
= g_strconcat(m4_files
, " ", arg
, NULL
);
1113 expand_dir(gchar
*dir
)
1118 s
= g_build_filename((gchar
*) g_get_home_dir(), dir
+ 1, NULL
);
1125 add_default_m4_files(void)
1129 path
= g_build_filename((gchar
*) g_get_home_dir(),
1130 ".pcb", DEFAULT_PCB_INC
, NULL
);
1131 if (g_file_test(path
, G_FILE_TEST_IS_REGULAR
))
1135 if (g_file_test(DEFAULT_PCB_INC
, G_FILE_TEST_IS_REGULAR
))
1136 add_m4_file(DEFAULT_PCB_INC
);
1141 add_schematic(gchar
*sch
)
1147 schematics
= g_strconcat(s
, " ", sch
, NULL
);
1149 schematics
= g_strdup(sch
);
1151 if (!basename
&& (s
= strstr(sch
, ".sch")) != NULL
)
1152 basename
= g_strndup(sch
, s
- sch
);
1156 parse_config(gchar
*config
, gchar
*arg
)
1160 /* remove trailing white space otherwise strange things can happen */
1161 if ( (arg
!= NULL
) && (strlen(arg
) >= 1) )
1163 s
= arg
+ strlen(arg
) - 1;
1164 while( (*s
== ' ' || *s
== '\t' ) && (s
!= arg
) )
1170 printf(" %s \"%s\"\n", config
, arg
? arg
: "");
1172 if (!strcmp(config
, "remove-unfound") || !strcmp(config
, "r"))
1174 /* This is default behavior set in header section */
1175 remove_unfound_elements
= TRUE
;
1178 if (!strcmp(config
, "keep-unfound") || !strcmp(config
, "k"))
1180 remove_unfound_elements
= FALSE
;
1183 if (!strcmp(config
, "quiet") || !strcmp(config
, "q"))
1188 if (!strcmp(config
, "preserve") || !strcmp(config
, "p"))
1193 if (!strcmp(config
, "use-files") || !strcmp(config
, "f"))
1195 force_element_files
= TRUE
;
1198 if (!strcmp(config
, "skip-m4") || !strcmp(config
, "s"))
1203 if (!strcmp(config
, "elements-dir") || !strcmp(config
, "d"))
1206 printf("\tAdding directory to file element directory list: %s\n",
1208 element_directory_list
=
1209 g_list_prepend(element_directory_list
, expand_dir(arg
));
1211 else if (!strcmp(config
, "output-name") || !strcmp(config
, "o"))
1212 basename
= g_strdup(arg
);
1213 else if (!strcmp(config
, "schematics"))
1215 else if (!strcmp(config
, "m4-command"))
1216 m4_command
= g_strdup(arg
);
1217 else if (!strcmp(config
, "m4-pcbdir"))
1218 m4_pcbdir
= g_strdup(arg
);
1219 else if (!strcmp(config
, "m4-file"))
1221 else if (!strcmp(config
, "gnetlist"))
1222 extra_gnetlist_list
=
1223 g_list_append(extra_gnetlist_list
, g_strdup(arg
));
1224 else if (!strcmp(config
, "empty-footprint"))
1225 empty_footprint_name
= g_strdup(arg
);
1233 load_project(gchar
*path
)
1236 gchar
*s
, buf
[1024], config
[32], arg
[768];
1238 f
= fopen(path
, "r");
1242 printf("Reading project file: %s\n", path
);
1243 while (fgets(buf
, sizeof(buf
), f
))
1245 for (s
= buf
; *s
== ' ' || *s
== '\t' || *s
== '\n'; ++s
)
1247 if (!*s
|| *s
== '#' || *s
== '/' || *s
== ';')
1250 sscanf(s
, "%31s %767[^\n]", config
, arg
);
1251 parse_config(config
, arg
);
1257 load_extra_project_files(void)
1260 static gboolean done
= FALSE
;
1265 load_project("/etc/gsch2pcb");
1266 load_project("/usr/local/etc/gsch2pcb");
1268 path
= g_build_filename((gchar
*) g_get_home_dir(), ".gsch2pcb", NULL
);
1275 static gchar
*usage_string0
=
1276 "usage: gsch2pcb [options] {project | foo.sch [foo1.sch ...]}\n"
1278 "Generate a PCB layout file from a set of gschem schematics.\n"
1279 " gnetlist -g PCB is run to generate foo.net from the schematics.\n"
1281 " gnetlist -g gsch2pcb is run to get PCB m4 derived elements which\n"
1282 " match schematic footprints. For schematic footprints which don't match\n"
1283 " any PCB m4 layout elements, search a set of file element directories in\n"
1284 " an attempt to find matching PCB file elements.\n"
1285 " Output to foo.pcb if it doesn't exist. If there is a current foo.pcb,\n"
1286 " output only new elements to foo.new.pcb.\n"
1287 " If any elements with a non-empty element name in the current foo.pcb\n"
1288 " have no matching schematic component, then remove those elements from\n"
1289 " foo.pcb and rename foo.pcb to a foo.pcb.bak sequence.\n"
1291 " gnetlist -g pcbpins is run to get a PCB actions file which will rename all\n"
1292 " of the pins in a .pcb file to match pin names from the schematic.\n"
1294 " \"project\" is a file (not ending in .sch) containing a list of\n"
1295 " schematics to process and some options. A schematics line is like:\n"
1296 " schematics foo1.sch foo2.sch ...\n"
1297 " Options in a project file are like command line args without the \"-\":\n"
1298 " output-name myproject\n"
1300 "options (may be included in a project file):\n"
1301 " -d, --elements-dir D Search D for PCB file elements. These defaults\n"
1302 " are searched if they exist: ./packages,\n"
1303 " /usr/local/share/pcb/newlib, /usr/share/pcb/newlib,\n"
1304 " (old pcb) /usr/local/lib/pcb_lib, /usr/lib/pcb_lib,\n"
1305 " (old pcb) /usr/local/pcb_lib\n"
1306 " -o, --output-name N Use output file names N.net, N.pcb, and N.new.pcb\n"
1307 " instead of foo.net, ... where foo is the basename\n"
1308 " of the first command line .sch file.\n"
1309 " -f, --use-files Force using file elements over m4 PCB elements\n"
1310 " for new footprints even though m4 elements are\n"
1311 " searched for first and may have been found.\n"
1312 " -r, --remove-unfound Don't include references to unfound elements in\n"
1313 " the generated .pcb files. Use if you want PCB to\n"
1314 " be able to load the (incomplete) .pcb file.\n"
1315 " This is the default behavior.\n"
1316 " -k, --keep-unfound Keep include references to unfound elements in\n"
1317 " the generated .pcb files. Use if you want to hand\n"
1318 " edit or otherwise preprocess the generated .pcb file\n"
1319 " before running pcb.\n"
1320 " -p, --preserve Preserve elements in PCB files which are not found\n"
1321 " in the schematics. Note that elements with an empty\n"
1322 " element name (schematic refdes) are never deleted,\n"
1323 " so you really shouldn't need this option.\n"
1324 " -q, --quiet Don't tell the user what to do next after running gsch2pcb.\n"
1326 " -s, --skip-m4 Skip m4 when looking for footprints. The default is to use\n"
1327 " m4 (which is what previous versions did).\n"
1328 " --m4-file F.inc Use m4 file F.inc in addition to the default m4\n"
1329 " files ./pcb.inc and ~/.pcb/pcb.inc.\n"
1330 " --m4-pcbdir D Use D as the PCB m4 files install directory\n"
1331 " instead of the default:\n";
1333 static gchar
*usage_string1
=
1334 " --gnetlist backend A convenience run of extra gnetlist -g commands.\n"
1335 " Example: gnetlist partslist3\n"
1336 " Creates: myproject.partslist3\n"
1337 " --empty-footprint name See the project.sample file.\n"
1339 "options (not recognized in a project file):\n"
1340 " --fix-elements If a schematic component footprint is not equal\n"
1341 " to its PCB element Description, update the\n"
1342 " Description instead of replacing the element.\n"
1343 " Do this the first time gsch2pcb is used with\n"
1344 " PCB files originally created with gschem2pcb.\n"
1345 " -v, --verbose Use -v -v for additional file element debugging.\n"
1346 " -V, --version\n\n"
1347 "environment variables:\n"
1348 " GNETLIST If set, this specifies the name of the gnetlist program\n"
1351 "Additional Resources:\n"
1353 " gEDA homepage: http://www.geda.seul.org\n"
1354 " PCB homepage: http://pcb.sf.net\n"
1355 " gEDA Wiki: http://geda.seul.org/dokuwiki/doku.php?id=geda\n"
1362 printf(usage_string0
);
1363 printf(" %s\n", default_m4_pcbdir
);
1364 printf(usage_string1
);
1369 get_args(gint argc
, gchar
**argv
)
1371 gchar
*opt
, *arg
, *s
;
1374 for (i
= 1; i
< argc
; ++i
)
1383 if (!strcmp(opt
, "version") || !strcmp(opt
, "V"))
1385 printf("gsch2pcb %s\n", GSC2PCB_VERSION
);
1388 else if (!strcmp(opt
, "verbose") || !strcmp(opt
, "v"))
1393 else if (!strcmp(opt
, "fix-elements"))
1395 fix_elements
= TRUE
;
1398 else if (!strcmp(opt
, "help") || !strcmp(opt
, "h"))
1401 && ((r
= parse_config(opt
, (i
< argc
- 1) ? arg
: NULL
))
1408 printf("gsch2pcb: bad or incomplete arg: %s\n", argv
[i
]);
1413 if ((s
= strstr(argv
[i
], ".sch")) == NULL
)
1415 load_extra_project_files();
1416 load_project(argv
[i
]);
1419 add_schematic(argv
[i
]);
1425 main(gint argc
, gchar
**argv
)
1427 gchar
*pcb_file_name
,
1434 gboolean initial_pcb
= TRUE
;
1435 gboolean created_pcb_file
= TRUE
;
1437 const char *pcbdata_path
;
1442 pcbdata_path
= g_getenv ("PCBDATA"); /* do not free return value */
1443 if (pcbdata_path
!= NULL
)
1445 /* If PCBDATA is set, use the value */
1446 m4_pcbdir
= g_strconcat( pcbdata_path
, "/pcb/m4", NULL
);
1448 /* Use the default value passed in from the configure script
1449 * instead of trying to hard code a value which is very
1452 m4_pcbdir
= g_strconcat( PCBDATADIR
, "/pcb/m4", NULL
);
1455 default_m4_pcbdir
= g_strdup(m4_pcbdir
);
1457 get_args(argc
, argv
);
1459 load_extra_project_files();
1460 add_default_m4_files();
1466 /* Defaults for the search path if not configured in the project file */
1467 if (g_file_test("packages", G_FILE_TEST_IS_DIR
))
1468 element_directory_list
= g_list_append(element_directory_list
,
1471 #define PCB_PATH_DELIMETER ":"
1473 printf ("Processing PCBLIBPATH=\"%s\"\n", PCBLIBPATH
);
1475 path
= g_strdup (PCBLIBPATH
);
1476 for (p
= strtok (path
, PCB_PATH_DELIMETER
); p
&& *p
;
1477 p
= strtok (NULL
, PCB_PATH_DELIMETER
))
1479 if (g_file_test(p
, G_FILE_TEST_IS_DIR
))
1482 printf ("Adding %s to the newlib search path\n", p
);
1483 element_directory_list
= g_list_append(element_directory_list
,
1489 pins_file_name
= g_strconcat(basename
, ".cmd", NULL
);
1490 net_file_name
= g_strconcat(basename
, ".net", NULL
);
1491 pcb_file_name
= g_strconcat(basename
, ".pcb", NULL
);
1492 bak_file_name
= g_strconcat(basename
, ".pcb.bak", NULL
);
1493 tmp
= g_strdup(bak_file_name
);
1495 for (i
= 0; g_file_test(bak_file_name
, G_FILE_TEST_EXISTS
); ++i
)
1497 g_free(bak_file_name
);
1498 bak_file_name
= g_strdup_printf("%s%d", tmp
, i
);
1502 if (g_file_test(pcb_file_name
, G_FILE_TEST_EXISTS
))
1504 initial_pcb
= FALSE
;
1505 pcb_new_file_name
= g_strconcat(basename
, ".new.pcb", NULL
);
1506 get_pcb_element_list(pcb_file_name
);
1509 pcb_new_file_name
= g_strdup(pcb_file_name
);
1511 run_gnetlist(pins_file_name
, net_file_name
, pcb_new_file_name
, basename
, schematics
);
1513 if (add_elements(pcb_new_file_name
) == 0)
1515 tmp
= g_strconcat("rm ", pcb_new_file_name
, NULL
);
1516 g_spawn_command_line_sync(tmp
, NULL
, NULL
, NULL
, NULL
);
1520 printf("No elements found, so nothing to do.\n");
1526 update_element_descriptions(pcb_file_name
, bak_file_name
);
1527 prune_elements(pcb_file_name
, bak_file_name
);
1529 /* Report work done during processing */
1532 printf("\n----------------------------------\n");
1533 printf("Done processing. Work performed:\n");
1534 if (n_deleted
> 0 || n_fixed
> 0 || need_PKG_purge
|| n_changed_value
> 0)
1535 printf("%s is backed up as %s.\n",
1536 pcb_file_name
, bak_file_name
);
1537 if (pcb_element_list
&& n_deleted
> 0)
1538 printf("%d elements deleted from %s.\n", n_deleted
, pcb_file_name
);
1540 if (n_added_ef
+ n_added_m4
> 0)
1541 printf("%d file elements and %d m4 elements added to %s.\n",
1542 n_added_ef
, n_added_m4
, pcb_new_file_name
);
1543 else if (n_not_found
== 0)
1545 printf("No elements to add so not creating %s\n", pcb_new_file_name
);
1546 created_pcb_file
= FALSE
;
1549 if (n_not_found
> 0)
1551 printf("%d not found elements added to %s.\n",
1552 n_not_found
, pcb_new_file_name
);
1555 printf("%d components had no footprint attribute and are omitted.\n",
1558 printf("%d components with footprint \"none\" omitted from %s.\n",
1559 n_none
, pcb_new_file_name
);
1561 printf("%d components with empty footprint \"%s\" omitted from %s.\n",
1562 n_empty
, empty_footprint_name
, pcb_new_file_name
);
1563 if (n_changed_value
> 0)
1564 printf("%d elements had a value change in %s.\n",
1565 n_changed_value
, pcb_file_name
);
1567 printf("%d elements fixed in %s.\n", n_fixed
, pcb_file_name
);
1568 if (n_PKG_removed_old
> 0)
1570 printf("%d elements could not be found.", n_PKG_removed_old
);
1571 if (created_pcb_file
)
1572 printf(" So %s is incomplete.\n", pcb_file_name
);
1576 if (n_PKG_removed_new
> 0)
1578 printf("%d elements could not be found.", n_PKG_removed_new
);
1579 if (created_pcb_file
)
1580 printf(" So %s is incomplete.\n", pcb_new_file_name
);
1584 if (n_preserved
> 0)
1585 printf("%d elements not in the schematic preserved in %s.\n",
1586 n_preserved
, pcb_file_name
);
1588 /* Tell user what to do next */
1592 if (n_added_ef
+ n_added_m4
> 0)
1596 printf("\nNext step:\n");
1597 printf("1. Run pcb on your file %s.\n", pcb_file_name
);
1598 printf(" You will find all your footprints in a bundle ready for you to place\n");
1599 printf(" or disperse with \"Select -> Disperse all elements\" in PCB.\n\n");
1600 printf("2. From within PCB, select \"File -> Load netlist file\" and select \n");
1601 printf(" %s to load the netlist.\n\n", net_file_name
);
1602 printf("3. From within PCB, enter\n\n");
1603 printf(" :ExecuteFile(%s)\n\n", pins_file_name
);
1604 printf(" to propagate the pin names of all footprints to the layout.\n\n");
1606 else if (quiet_mode
== FALSE
)
1608 printf("\nNext steps:\n");
1609 printf("1. Run pcb on your file %s.\n", pcb_file_name
);
1610 printf("2. From within PCB, select \"File -> Load layout data to paste buffer\"\n");
1611 printf(" and select %s to load the new footprints into your existing layout.\n",
1613 printf("3. From within PCB, select \"File -> Load netlist file\" and select \n");
1614 printf(" %s to load the updated netlist.\n\n", net_file_name
);
1615 printf("4. From within PCB, enter\n\n");
1616 printf(" :ExecuteFile(%s)\n\n", pins_file_name
);
1617 printf(" to update the pin names of all footprints.\n\n");
1621 g_free(net_file_name
);
1622 g_free(pins_file_name
);
1623 g_free(pcb_file_name
);
1624 g_free(bak_file_name
);