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