utils

gsch2pcb.c

Go to the documentation of this file.
00001 /* gsch2pcb
00002  *
00003  *  Bill Wilson    billw@wt.net
00004  *
00005  *  This program is free software which I release under the GNU General Public
00006  *  License. You may redistribute and/or modify this program under the terms
00007  *  of that license as published by the Free Software Foundation; either
00008  *  version 2 of the License, or (at your option) any later version.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.  Version 2 is in the
00014  *  COPYRIGHT file in the top level directory of this distribution.
00015  *
00016  *  To get a copy of the GNU General Puplic License, write to the Free Software
00017  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00018  */
00019 
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #include "config.h"
00023 #endif
00024 
00025 #include <glib.h>
00026 
00027 #include <stdio.h>
00028 #include <stdlib.h>
00029 #include <string.h>
00030 #include <ctype.h>
00031 #include <unistd.h>
00032 #include <sys/stat.h>
00033 
00034 #ifdef HAVE_LIBDMALLOC
00035 #include <dmalloc.h>
00036 #endif
00037 
00038 #define GSC2PCB_VERSION "1.6"
00039 
00040 #define DEFAULT_PCB_INC "pcb.inc"
00041 
00042 #define SEP_STRING "--------\n"
00043 
00044 typedef struct
00045 {
00046   gchar *refdes, *value, *description, *changed_description, *changed_value;
00047   gchar *flags, *tail;
00048   gchar *x, *y;
00049   gchar *pkg_name_fix;
00050   gchar res_char;
00051 
00052   gboolean still_exists, new_format, hi_res_format, quoted_flags, omit_PKG;
00053 }
00054 PcbElement;
00055 
00056 
00057 typedef struct
00058 {
00059   gchar *part_number, *element_name;
00060 }
00061 ElementMap;
00062 
00063 static GList *pcb_element_list,
00064   *element_directory_list, *extra_gnetlist_list, *extra_gnetlist_arg_list;
00065 
00066 static gchar *sch_basename;
00067 
00068 static GList *schematics;
00069 
00070 static gchar *m4_pcbdir, *default_m4_pcbdir, *m4_files, *m4_override_file;
00071 
00072 static gboolean use_m4 = TRUE;
00073 
00074 static gchar *empty_footprint_name;
00075 
00076 static gint verbose,
00077   n_deleted,
00078   n_added_m4,
00079   n_added_ef,
00080   n_fixed,
00081   n_PKG_removed_new,
00082   n_PKG_removed_old,
00083   n_preserved, n_changed_value, n_not_found, n_unknown, n_none, n_empty;
00084 
00085 static gboolean remove_unfound_elements = TRUE,
00086   quiet_mode = FALSE,
00087   force_element_files, preserve, fix_elements, bak_done, need_PKG_purge;
00088 
00089 
00090 static void
00091 create_m4_override_file ()
00092 {
00093   FILE *f;
00094 
00095   m4_override_file = "gnet-gsch2pcb-tmp.scm";
00096   f = fopen (m4_override_file, "wb");
00097   if (!f) {
00098     m4_override_file = NULL;
00099     return;
00100   }
00101   if (m4_pcbdir)
00102     fprintf (f, "(define gsch2pcb:pcb-m4-dir \"%s\")\n", m4_pcbdir);
00103   if (m4_files)
00104     fprintf (f, "(define gsch2pcb:m4-files \"%s\")\n", m4_files);
00105   fprintf (f, "(define gsch2pcb:use-m4 %s)\n", use_m4 == TRUE ? "#t" : "#f");
00106 
00107   fclose (f);
00108   if (verbose) {
00109     printf ("Default m4-pcbdir: %s\n", default_m4_pcbdir);
00110     printf ("--------\ngnet-gsch2pcb-tmp.scm override file:\n");
00111     if (m4_pcbdir)
00112       printf ("    (define gsch2pcb:pcb-m4-dir \"%s\")\n", m4_pcbdir);
00113     if (m4_files)
00114       printf ("    (define gsch2pcb:m4-files \"%s\")\n", m4_files);
00115     printf ("    (define gsch2pcb:use-m4 %s)\n", use_m4 == TRUE ? "#t" : "#f");
00116   }
00117 }
00118 
00130 static gboolean
00131 build_and_run_command (const gchar *format, ...)
00132 {
00133   va_list vargs;
00134   gchar ** split;
00135   GList *tmp = NULL;
00136   gint num_split;
00137   gint i;
00138   gint status;
00139   gboolean result = FALSE;
00140   gboolean spawn_result;
00141   gchar *standard_output = NULL;
00142   gchar *standard_error = NULL;
00143   GError * error = NULL;
00144 
00145   va_start (vargs, format);
00146   split = g_strsplit_set (format, " \t\n\v", 0);
00147   num_split = g_strv_length (split);
00148   for (i = 0; i < num_split; ++i) {
00149     gchar *chunk = split[i];
00150     if (strcmp (chunk, "%l") == 0) {
00151       /* append contents of list into command args - shared data */
00152       tmp = g_list_concat (tmp, g_list_copy (va_arg (vargs, GList*)));
00153     } else if (strcmp (chunk, "%s") == 0) {
00154       /* insert contents of string into output */
00155       tmp = g_list_append (tmp, va_arg (vargs, gchar*));
00156     } else {
00157       /* bare string, use as is */
00158       tmp = g_list_append (tmp, chunk);
00159     }
00160   }
00161   va_end (vargs);
00162 
00163   if (tmp) {
00164     /* we have something in the list, build & call command */
00165     GList *p;
00166     gint i = 0;
00167     gchar ** args = g_new0 (gchar*, g_list_length (tmp) + 1/* NULL terminate the list */);
00168 
00169     if (verbose)
00170       printf ("Running command:\n\t");
00171 
00172     for (p = tmp; p; p = g_list_next (p)) {
00173       args[i++] = (gchar*) p->data;
00174       if (verbose)
00175         printf ("%s ", (char*)p->data);
00176     }
00177 
00178     if (verbose)
00179       printf ("\n%s", SEP_STRING);
00180     
00181     if (g_spawn_sync (".",                  /* Working directory */
00182                       args,                 /* argv */
00183                       NULL,                 /* envp */
00184                       G_SPAWN_SEARCH_PATH,  /* flags */
00185                       NULL,                 /* child_setup */
00186                       NULL,                 /* user data */
00187                       &standard_output,     /* standard output */
00188                       &standard_error,      /* standard error */
00189                       &status,              /* exit status return */
00190                       &error)) {            /* GError return */
00191       if (verbose)
00192         fputs(standard_output, stdout);
00193       if (status == 0)
00194         result = TRUE;
00195       else {
00196         if (standard_error)
00197           fputs(standard_error, stderr);
00198       }
00199     }
00200     else {
00201       fprintf(stderr, "Failed to execute external program: %s\n", error->message);
00202       g_error_free(error);
00203     }
00204 
00205     if (verbose)
00206       printf ("\n%s", SEP_STRING);
00207 
00208     g_free(standard_error);
00209     g_free (standard_output);
00210     
00211     g_free (args);
00212     /* free the list, but leave data untouched */
00213     g_list_free (tmp);
00214   }
00215 
00216   g_strfreev (split);
00217 
00218   return result;
00219 }
00220 
00221 /* Run gnetlist to generate a netlist and a PCB board file.  gnetlist
00222  * has exit status of 0 even if it's given an invalid arg, so do some
00223  * stat() hoops to decide if gnetlist successfully generated the PCB
00224  * board file (only gnetlist >= 20030901 recognizes -m).
00225  */
00226 static gboolean
00227 run_gnetlist (gchar * pins_file, gchar * net_file, gchar * pcb_file,
00228               gchar * basename, GList * largs)
00229 {
00230   struct stat st;
00231   time_t mtime;
00232   static const gchar *gnetlist = NULL;
00233   GList *list = NULL;
00234   GList *verboseList = NULL;
00235   GList *args1 = NULL;
00236 
00237   /* Allow the user to specify a full path or a different name for
00238    * the gnetlist command.  Especially useful if multiple copies
00239    * are installed at once.
00240    */
00241   if (gnetlist == NULL)
00242     gnetlist = g_getenv ("GNETLIST");
00243   if (gnetlist == NULL)
00244     gnetlist = "gnetlist";
00245 
00246   if (!verbose)
00247     verboseList = g_list_append (verboseList, "-q");
00248 
00249   if (!build_and_run_command ("%s %l -g pcbpins -o %s %l %l",
00250                   gnetlist,
00251                   verboseList,
00252                   pins_file,
00253                   extra_gnetlist_arg_list,
00254                   largs))
00255     return FALSE;
00256 
00257   if (!build_and_run_command ("%s %l -g PCB -o %s %l %l",
00258                   gnetlist,
00259                   verboseList,
00260                   net_file,
00261                   extra_gnetlist_arg_list,
00262                   largs))
00263     return FALSE;
00264   create_m4_override_file ();
00265 
00266   if (m4_override_file) {
00267     args1 = g_list_append (args1, "-m");
00268     args1 = g_list_append (args1, m4_override_file);
00269   }
00270 
00271   mtime = (stat (pcb_file, &st) == 0) ? st.st_mtime : 0;
00272 
00273   if (!build_and_run_command ("%s %l -g gsch2pcb -o %s %l %l %l",
00274                   gnetlist,
00275                   verboseList,
00276                   pcb_file,
00277                   args1,
00278                   extra_gnetlist_arg_list,
00279                   largs)) {
00280       if (stat (pcb_file, &st) != 0 || mtime == st.st_mtime) {
00281           fprintf (stderr,
00282                    "gsch2pcb: gnetlist command failed, `%s' not updated\n",
00283                    pcb_file
00284                    );
00285           if (m4_override_file)
00286               fprintf (stderr,
00287                        "    At least gnetlist 20030901 is required for m4-xxx options.\n");
00288           return FALSE;
00289       }
00290       return FALSE;
00291   }
00292 
00293   if (m4_override_file)
00294     unlink (m4_override_file);
00295 
00296   for (list = extra_gnetlist_list; list; list = g_list_next (list)) {
00297     const gchar *s = (gchar *) list->data;
00298     const gchar *s2 = strstr (s, " -o ");
00299     gchar *out_file;
00300     gchar *backend;
00301     if (!s2) {
00302       out_file = g_strconcat (basename, ".", s, NULL);
00303       backend = g_strdup (s);
00304     } else {
00305       out_file = g_strdup (s2 + 4);
00306       backend = g_strndup (s, s2 - s);
00307     }
00308 
00309     if (!build_and_run_command ("%s %l -g %s -o %s %l %l",
00310                 gnetlist,
00311                 verboseList,
00312                 backend,
00313                 out_file,
00314                 extra_gnetlist_arg_list,
00315                 largs))
00316       return FALSE;
00317     g_free (out_file);
00318     g_free (backend);
00319   }
00320 
00321   g_list_free (args1);
00322   g_list_free (verboseList);
00323 
00324   return TRUE;
00325 }
00326 
00327 static gchar *
00328 token (gchar * string, gchar ** next, gboolean * quoted_ret)
00329 {
00330   static gchar *str;
00331   gchar *s, *ret;
00332   gboolean quoted = FALSE;
00333 
00334   if (string)
00335     str = string;
00336   if (!str || !*str) {
00337     if (next)
00338       *next = str;
00339     return g_strdup ("");
00340   }
00341   while (*str == ' ' || *str == '\t' || *str == ',' || *str == '\n')
00342     ++str;
00343   if (*str == '"') {
00344     quoted = TRUE;
00345     if (quoted_ret)
00346       *quoted_ret = TRUE;
00347     ++str;
00348     for (s = str; *s && *s != '"' && *s != '\n'; ++s);
00349   } else {
00350     if (quoted_ret)
00351       *quoted_ret = FALSE;
00352     for (s = str;
00353          *s && (*s != ' ' && *s != '\t' && *s != ',' && *s != '\n'); ++s);
00354   }
00355   ret = g_strndup (str, s - str);
00356   str = (quoted && *s) ? s + 1 : s;
00357   if (next)
00358     *next = str;
00359   return ret;
00360 }
00361 
00362 static gchar *
00363 fix_spaces (gchar * str)
00364 {
00365   gchar *s;
00366 
00367   if (!str)
00368     return NULL;
00369   for (s = str; *s; ++s)
00370     if (*s == ' ' || *s == '\t')
00371       *s = '_';
00372   return str;
00373 }
00374 
00375   /* As of 1/9/2004 CVS hi_res Element[] line format:
00376    *   Element[element_flags, description, pcb-name, value, mark_x, mark_y,
00377    *       text_x, text_y, text_direction, text_scale, text_flags]
00378    *   New PCB 1.7 / 1.99 Element() line format:
00379    *   Element(element_flags, description, pcb-name, value, mark_x, mark_y,
00380    *       text_x, text_y, text_direction, text_scale, text_flags)
00381    *   Old PCB 1.6 Element() line format:
00382    *   Element(element_flags, description, pcb-name, value,
00383    *       text_x, text_y, text_direction, text_scale, text_flags)
00384    *
00385    *   (mark_x, mark_y) is the element position (mark) and (text_x,text_y)
00386    *   is the description text position which is absolute in pre 1.7 and
00387    *   is now relative.  The hi_res mark_x,mark_y and text_x,text_y resolutions
00388    *   are 100x the other formats.
00389    */
00390 PcbElement *
00391 pcb_element_line_parse (gchar * line)
00392 {
00393   PcbElement *el = NULL;
00394   gchar *s, *t, close_char;
00395   gint state = 0, elcount = 0;
00396 
00397   if (strncmp (line, "Element", 7))
00398     return NULL;
00399 
00400   el = g_new0 (PcbElement, 1);
00401 
00402   s = line + 7;
00403   while (*s == ' ' || *s == '\t')
00404     ++s;
00405 
00406   if (*s == '[')
00407     el->hi_res_format = TRUE;
00408   else if (*s != '(') {
00409     g_free (el);
00410     return NULL;
00411   }
00412 
00413   el->res_char = el->hi_res_format ? '[' : '(';
00414   close_char = el->hi_res_format ? ']' : ')';
00415 
00416   el->flags = token (s + 1, NULL, &el->quoted_flags);
00417   el->description = token (NULL, NULL, NULL);
00418   el->refdes = token (NULL, NULL, NULL);
00419   el->value = token (NULL, NULL, NULL);
00420 
00421   el->x = token (NULL, NULL, NULL);
00422   el->y = token (NULL, &t, NULL);
00423 
00424   el->tail = g_strdup (t ? t : "");
00425   if ((s = strrchr (el->tail, (gint) '\n')) != NULL)
00426     *s = '\0';
00427 
00428   /* Count the tokens in tail to decide if it's new or old format.
00429    * Old format will have 3 tokens, new format will have 5 tokens.
00430    */
00431   for (s = el->tail; *s && *s != close_char; ++s) {
00432     if (*s != ' ') {
00433       if (state == 0)
00434         ++elcount;
00435       state = 1;
00436     } else
00437       state = 0;
00438   }
00439   if (elcount > 4)
00440     el->new_format = TRUE;
00441 
00442   fix_spaces (el->description);
00443   fix_spaces (el->refdes);
00444   fix_spaces (el->value);
00445 
00446   /* Don't allow elements with no refdes to ever be deleted because
00447    * they may be desired pc board elements not in schematics.  So
00448    * initialize still_exists to TRUE if empty or non-alphanumeric
00449    * refdes.
00450    */
00451   if (!*el->refdes || !isalnum ((gint) (*el->refdes)))
00452     el->still_exists = TRUE;
00453 
00454   return el;
00455 }
00456 
00457 static void
00458 pcb_element_free (PcbElement * el)
00459 {
00460   if (!el)
00461     return;
00462   g_free (el->flags);
00463   g_free (el->description);
00464   g_free (el->changed_description);
00465   g_free (el->changed_value);
00466   g_free (el->refdes);
00467   g_free (el->value);
00468   g_free (el->x);
00469   g_free (el->y);
00470   g_free (el->tail);
00471   g_free (el->pkg_name_fix);
00472   g_free (el);
00473 }
00474 
00475 static void
00476 get_pcb_element_list (gchar * pcb_file)
00477 {
00478   FILE *f;
00479   PcbElement *el;
00480   gchar *s, buf[1024];
00481 
00482   if ((f = fopen (pcb_file, "r")) == NULL)
00483     return;
00484   while ((fgets (buf, sizeof (buf), f)) != NULL) {
00485     for (s = buf; *s == ' ' || *s == '\t'; ++s);
00486     if (!strncmp (s, "PKG_", 4)) {
00487       need_PKG_purge = TRUE;
00488       continue;
00489     }
00490     if ((el = pcb_element_line_parse (s)) == NULL)
00491       continue;
00492     pcb_element_list = g_list_append (pcb_element_list, el);
00493   }
00494   fclose (f);
00495 }
00496 
00497 static PcbElement *
00498 pcb_element_exists (PcbElement * el_test, gboolean record)
00499 {
00500   GList *list;
00501   PcbElement *el;
00502 
00503   for (list = pcb_element_list; list; list = g_list_next (list)) {
00504     el = (PcbElement *) list->data;
00505 
00506     if (strcmp (el_test->refdes, el->refdes))
00507       continue;
00508     if (strcmp (el_test->description, el->description)) { /* footprint */
00509       if (record)
00510         el->changed_description = g_strdup (el_test->description);
00511     } else {
00512       if (record) {
00513         if (strcmp (el_test->value, el->value))
00514           el->changed_value = g_strdup (el_test->value);
00515         el->still_exists = TRUE;
00516       }
00517       return el;
00518     }
00519   }
00520   return NULL;
00521 }
00522 
00523 /* A problem is that new PCB 1.7 file elements have the
00524  * (mark_x,mark_y) value set to wherever the element was created and
00525  * no equivalent of a gschem translate symbol was done.
00526  *
00527  * So, file elements inserted can be scattered over a big area and
00528  * this is bad when loading a file.new.pcb into an existing PC
00529  * board.  So, do a simple translate if (mark_x,mark_y) is
00530  * (arbitrarily) over 1000.  I'll assume that for values < 1000 the
00531  * element creator was concerned with a sane initial element
00532  * placement.  Unless someone has a better idea?  Don't bother with
00533  * pre PCB 1.7 formats as that would require parsing the mark().
00534  * Current m4 elements use the old format but they seem to have a
00535  * reasonable initial mark().
00536  */
00537 static void
00538 simple_translate (PcbElement * el)
00539 {
00540 
00541   el->x=strdup("0");
00542   el->y=strdup("0");
00543 }
00544 
00545 static gboolean
00546 insert_element (FILE * f_out, gchar * element_file,
00547                 gchar * footprint, gchar * refdes, gchar * value)
00548 {
00549   FILE *f_in;
00550   PcbElement *el;
00551   gchar *fmt, *s, buf[1024];
00552   gboolean retval = FALSE;
00553 
00554   if ((f_in = fopen (element_file, "r")) == NULL) {
00555     s = g_strdup_printf ("insert_element() can't open %s", element_file);
00556     perror (s);
00557     g_free (s);
00558     return FALSE;
00559   }
00560   /* Scan the file to detect whether it's actually a PCB
00561    * layout. Assumes that a PCB layout will have a "PCB" line. */
00562   while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
00563     for (s = buf; *s == ' ' || *s == '\t'; ++s);
00564     s[3] = 0;                   /* Truncate line */
00565     if (strncmp ("PCB", s, sizeof (buf)) == 0) {
00566       printf ("Warning: %s appears to be a PCB layout file. Skipping.\n",
00567               element_file);
00568       fclose (f_in);
00569       return FALSE;
00570     }
00571   }
00572   rewind (f_in);
00573 
00574   /* Copy the file element lines.  Substitute new parameters into the
00575    * Element() or Element[] line and strip comments.
00576    */
00577   while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
00578     for (s = buf; *s == ' ' || *s == '\t'; ++s);
00579     if ((el = pcb_element_line_parse (s)) != NULL) {
00580       simple_translate (el);
00581       fmt = el->quoted_flags ?
00582         "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" :
00583         "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n";
00584 
00585       fprintf (f_out, fmt,
00586                el->res_char, el->flags, footprint, refdes, value,
00587                el->x, el->y, el->tail);
00588       retval = TRUE;
00589     } else if (*s != '#')
00590       fputs (buf, f_out);
00591     pcb_element_free (el);
00592   }
00593   fclose (f_in);
00594   return retval;
00595 }
00596 
00597 
00598 gchar *
00599 find_element (gchar * dir_path, gchar * element)
00600 {
00601   GDir *dir;
00602   gchar *path, *name, *s, *found = NULL;
00603 
00604   if ((dir = g_dir_open (dir_path, 0, NULL)) == NULL) {
00605     s = g_strdup_printf ("find_element can't open dir \"%s\"", dir_path);
00606     perror (s);
00607     g_free (s);
00608     return NULL;
00609   }
00610   if (verbose > 1)
00611     printf ("\t  Searching: \"%s\" for \"%s\"\n", dir_path, element);
00612   while ((name = (gchar *) g_dir_read_name (dir)) != NULL) {
00613     path = g_strconcat (dir_path, "/", name, NULL);
00614     found = NULL;
00615 
00616     /* if we got a directory name, then recurse down into it */
00617     if (g_file_test (path, G_FILE_TEST_IS_DIR))
00618       found = find_element (path, element);
00619 
00620     /* otherwise assume it is a file and see if it is the one we want */
00621     else {
00622       if (verbose > 1)
00623         printf ("\t           : %s\t", name);
00624       if (!strcmp (name, element))
00625         found = g_strdup (path);
00626       else {
00627         gchar *tmps;
00628         tmps = g_strconcat (element, ".fp", NULL);
00629         if (!strcmp (name, tmps))
00630           found = g_strdup (path);
00631         g_free (tmps);
00632       }
00633       if (verbose > 1)
00634         printf ("%s\n", found ? "Yes" : "No");
00635     }
00636     g_free (path);
00637     if (found)
00638       break;
00639   }
00640   g_dir_close (dir);
00641   return found;
00642 }
00643 
00644 gchar *
00645 search_element_directories (PcbElement * el)
00646 {
00647   GList *list;
00648   gchar *s, *elname = NULL, *dir_path, *path = NULL;
00649   gint n1, n2;
00650 
00651   /* See comment before pkg_to_element() */
00652   if (el->pkg_name_fix) {
00653     if (strchr (el->description, '-')) {
00654       n1 = strlen (el->description);
00655       n2 = strlen (el->pkg_name_fix);
00656       s = el->description + n1 - n2 - 1;
00657 
00658 // printf("n1=%d n2=%d desc:%s fix:%s s:%s\n",
00659 //  n1, n2, el->description, el->pkg_name_fix, s);
00660 
00661       if (n1 > 0 && n2 < n1 && *s == '-' && *(s + 1) == *el->pkg_name_fix) {
00662         s = g_strndup (el->description, n1 - n2 - 1);
00663         elname = g_strconcat (s, " ", el->pkg_name_fix, NULL);
00664         g_free (s);
00665       }
00666     }
00667     if (!elname) {
00668       printf ("Warning: argument passing may have been confused by\n");
00669       printf ("         a comma in a component value:\n");
00670       printf ("         Check %s %s %s\n",
00671               el->refdes, el->description, el->value);
00672       printf ("         Maybe just use a space instead of a comma?\n");
00673     }
00674   }
00675   if (!elname)
00676     elname = g_strdup (el->description);
00677 
00678   if (!strcmp (elname, "unknown")) {
00679     g_free (elname);
00680     return NULL;
00681   }
00682   if (verbose > 1)
00683     printf ("\tSearching directories looking for file element: %s\n", elname);
00684   for (list = element_directory_list; list; list = g_list_next (list)) {
00685     dir_path = (gchar *) list->data;
00686     if (verbose > 1)
00687       printf ("\tLooking in directory: \"%s\"\n", dir_path);
00688     path = find_element (dir_path, elname);
00689     if (path) {
00690       if (verbose)
00691         printf ("\tFound: %s\n", path);
00692       break;
00693     }
00694   }
00695   g_free (elname);
00696   return path;
00697 }
00698 
00699 /* The gnetlist backend gnet-gsch2pcb.scm generates PKG_ lines:
00700  *
00701  *        PKG_footprint(footprint{-fp0-fp1},refdes,value{,fp0,fp1})
00702  *
00703  * where fp1 and fp2 (if they exist) are the extra footprint
00704  * components when specifying footprints like "DIL 14 300".  This is
00705  * needed for m4 macros.
00706  *
00707  * A complication is if the footprint references a file element with
00708  * spaces embedded in the name.  The gnetlist backend will interpret
00709  * these as fp0, fp1, ... args and the footprint will in this case
00710  * incorrectly have '-' inserted where the spaces should be.  So, if
00711  * there are additional args, reconstruct the portion of the name
00712  * given by the args with spaces for later use.  Eg. if the footprint
00713  * is "100 Pin jack", we will have
00714  *
00715  *      PKG_100-Pin-jack(100-Pin-jack,refdes,value,Pin,jack)
00716  *
00717  *  So put "Pin jack" into pkg_name_fix so if this element is searched
00718  *  as a file element we can munge the description to what it should
00719  *  be, eg:
00720  *
00721  *      100-Pin-jack -> 100 Pin jack
00722  */
00723 static PcbElement *
00724 pkg_to_element (FILE * f, gchar * pkg_line)
00725 {
00726   PcbElement *el;
00727   gchar **args, *s;
00728   gint n, n_extra_args, n_dashes;
00729 
00730   if (strncmp (pkg_line, "PKG_", 4)
00731       || (s = strchr (pkg_line, (gint) '(')) == NULL)
00732     return NULL;
00733 
00734   args = g_strsplit (s + 1, ",", 12);
00735   if (!args[0] || !args[1] || !args[2]) {
00736     fprintf (stderr, "Bad package line: %s\n", pkg_line);
00737     return NULL;
00738   }
00739   fix_spaces (args[0]);
00740   fix_spaces (args[1]);
00741   fix_spaces (args[2]);
00742 
00743   el = g_new0 (PcbElement, 1);
00744   el->description = g_strdup (args[0]);
00745   el->refdes = g_strdup (args[1]);
00746   el->value = g_strdup (args[2]);
00747   if ((s = strchr (el->value, (gint) ')')) != NULL)
00748     *s = '\0';
00749 
00750   /* If the component value has a comma, eg "1k, 1%", the gnetlist generated
00751    * PKG line will be
00752    *
00753    *   PKG_XXX(`R0w8',`R100',`1k, 1%'),
00754    *
00755    * but after processed by m4, the input to gsch2pcb will be
00756    *
00757    *   PKG_XXX(R0w8,R100,1k, 1%).
00758    *
00759    * So the quoting info has been lost when processing for file
00760    * elements.  So here try to detect and fix this.  But I can't
00761    * handle the situation where the description has a '-' and the
00762    * value has a comma because gnet-gsch2pcb.scm munges the
00763    * description with '-' when there are extra args.
00764    */
00765   for (n_extra_args = 0; args[3 + n_extra_args] != NULL; ++n_extra_args);
00766   s = el->description;
00767   for (n_dashes = 0; (s = strchr (s + 1, '-')) != NULL; ++n_dashes);
00768 
00769   n = 3;
00770   if (n_extra_args == n_dashes + 1) { /* Assume there was a comma in the value, eg "1K, 1%" */
00771     s = el->value;
00772     el->value = g_strconcat (s, ",", fix_spaces (args[n]), NULL);
00773     g_free (s);
00774     if ((s = strchr (el->value, (gint) ')')) != NULL)
00775       *s = '\0';
00776     n = 4;
00777   }
00778   if (args[n]) {
00779     el->pkg_name_fix = g_strdup (args[n]);
00780     for (n += 1; args[n] != NULL; ++n) {
00781       s = el->pkg_name_fix;
00782       el->pkg_name_fix = g_strconcat (s, " ", args[n], NULL);
00783       g_free (s);
00784     }
00785     if ((s = strchr (el->pkg_name_fix, (gint) ')')) != NULL)
00786       *s = '\0';
00787   }
00788   g_strfreev (args);
00789 
00790   if (empty_footprint_name && !strcmp (el->description, empty_footprint_name)) {
00791     if (verbose)
00792       printf
00793         ("%s: has the empty footprint attribute \"%s\" so won't be in the layout.\n",
00794          el->refdes, el->description);
00795     n_empty += 1;
00796     el->omit_PKG = TRUE;
00797   } else if (!strcmp (el->description, "none")) {
00798     fprintf (stderr,
00799              "WARNING: %s has a footprint attribute \"%s\" so won't be in the layout.\n",
00800              el->refdes, el->description);
00801     n_none += 1;
00802     el->omit_PKG = TRUE;
00803   } else if (!strcmp (el->description, "unknown")) {
00804     fprintf (stderr,
00805              "WARNING: %s has no footprint attribute so won't be in the layout.\n",
00806              el->refdes);
00807     n_unknown += 1;
00808     el->omit_PKG = TRUE;
00809   }
00810   return el;
00811 }
00812 
00813 /* Process the newly created pcb file which is the output from
00814  *     gnetlist -g gsch2pcb ...
00815  *
00816  * It will have elements found via the m4 interface and PKG_ lines for
00817  * elements not found.  Insert pcb file elements for PKG_ lines if
00818  * file elements can be found.  If there was an existing pcb file,
00819  * strip out any elements if they are already present so that the new
00820  * pcb file will only have new elements.
00821  */
00822 static gint
00823 add_elements (gchar * pcb_file)
00824 {
00825   FILE *f_in, *f_out;
00826   PcbElement *el = NULL;
00827   gchar *command, *p, *tmp_file, *s, buf[1024];
00828   gint total, paren_level = 0;
00829   gboolean is_m4, skipping = FALSE;
00830 
00831   if ((f_in = fopen (pcb_file, "r")) == NULL)
00832     return 0;
00833   tmp_file = g_strconcat (pcb_file, ".tmp", NULL);
00834   if ((f_out = fopen (tmp_file, "wb")) == NULL) {
00835     fclose (f_in);
00836     g_free (tmp_file);
00837     return 0;
00838   }
00839   while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
00840     for (s = buf; *s == ' ' || *s == '\t'; ++s);
00841     if (skipping) {
00842       if (*s == '(')
00843         ++paren_level;
00844       else if (*s == ')' && --paren_level <= 0)
00845         skipping = FALSE;
00846       continue;
00847     }
00848     is_m4 = FALSE;
00849     if ((el = pcb_element_line_parse (s)) != NULL)
00850       is_m4 = TRUE;
00851     else
00852       el = pkg_to_element (f_out, s);
00853     if (el && pcb_element_exists (el, TRUE)) {
00854       skipping = is_m4;
00855       pcb_element_free (el);
00856       continue;
00857     }
00858     if (!el || el->omit_PKG) {
00859       if (el) {
00860 
00861       } else
00862         fputs (buf, f_out);
00863       continue;
00864     }
00865     if (!is_m4 || (is_m4 && force_element_files)) {
00866       if (verbose && !is_m4)
00867         printf ("%s: need new file element for footprint  %s (value=%s)\n",
00868                 el->refdes, el->description, el->value);
00869       if (verbose && is_m4 && force_element_files)
00870         printf
00871           ("%s: have m4 element %s, but trying to replace with a file element.\n",
00872            el->refdes, el->description);
00873       p = search_element_directories (el);
00874       if (!p && verbose && is_m4 && force_element_files)
00875         printf ("\tNo file element found.\n");
00876 
00877       if (p && insert_element (f_out, p,
00878                                el->description, el->refdes, el->value)) {
00879         skipping = is_m4;
00880         is_m4 = FALSE;
00881         ++n_added_ef;
00882         if (verbose)
00883           printf ("%s: added new file element for footprint %s (value=%s)\n",
00884                   el->refdes, el->description, el->value);
00885       } else if (!is_m4) {
00886         fprintf (stderr,
00887                  "%s: can't find PCB element for footprint %s (value=%s)\n",
00888                  el->refdes, el->description, el->value);
00889         if (remove_unfound_elements && !fix_elements) {
00890           fprintf (stderr,
00891                    "So device %s will not be in the layout.\n", el->refdes);
00892           ++n_PKG_removed_new;
00893         } else {
00894           ++n_not_found;
00895           fputs (buf, f_out);   /* Copy PKG_ line */
00896         }
00897       }
00898       g_free (p);
00899     }
00900     if (is_m4) {
00901       fputs (buf, f_out);
00902       ++n_added_m4;
00903       if (verbose)
00904         printf ("%s: added new m4 element for footprint   %s (value=%s)\n",
00905                 el->refdes, el->description, el->value);
00906     }
00907     pcb_element_free (el);
00908     if (verbose)
00909       printf ("----\n");
00910   }
00911   fclose (f_in);
00912   fclose (f_out);
00913 
00914   total = n_added_ef + n_added_m4 + n_not_found;
00915   if (total == 0)
00916     build_and_run_command ("rm %s", tmp_file);
00917   else
00918     build_and_run_command ("mv %s %s", tmp_file, pcb_file);
00919   g_free (tmp_file);
00920   return total;
00921 }
00922 
00923 static void
00924 update_element_descriptions (gchar * pcb_file, gchar * bak)
00925 {
00926   FILE *f_in, *f_out;
00927   GList *list;
00928   PcbElement *el, *el_exists;
00929   gchar *fmt, *command, *tmp, *s, buf[1024];
00930 
00931   for (list = pcb_element_list; list; list = g_list_next (list)) {
00932     el = (PcbElement *) list->data;
00933     if (el->changed_description)
00934       ++n_fixed;
00935   }
00936   if (!pcb_element_list || n_fixed == 0) {
00937     fprintf (stderr, "Could not find any elements to fix.\n");
00938     return;
00939   }
00940   if ((f_in = fopen (pcb_file, "r")) == NULL)
00941     return;
00942   tmp = g_strconcat (pcb_file, ".tmp", NULL);
00943   if ((f_out = fopen (tmp, "wb")) == NULL) {
00944     fclose (f_in);
00945     return;
00946   }
00947   while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
00948     for (s = buf; *s == ' ' || *s == '\t'; ++s);
00949     if ((el = pcb_element_line_parse (s)) != NULL
00950         && (el_exists = pcb_element_exists (el, FALSE)) != NULL
00951         && el_exists->changed_description) {
00952       fmt = el->quoted_flags ?
00953         "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" :
00954         "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n";
00955       fprintf (f_out, fmt,
00956                el->res_char,
00957                el->flags, el_exists->changed_description,
00958                el->refdes, el->value, el->x, el->y, el->tail);
00959       printf ("%s: updating element Description: %s -> %s\n",
00960               el->refdes, el->description, el_exists->changed_description);
00961       el_exists->still_exists = TRUE;
00962     } else
00963       fputs (buf, f_out);
00964     pcb_element_free (el);
00965   }
00966   fclose (f_in);
00967   fclose (f_out);
00968 
00969   if (!bak_done) {
00970     build_and_run_command ("mv %s %s", pcb_file, bak);
00971     bak_done = TRUE;
00972   }
00973 
00974   build_and_run_command ("mv %s %s", tmp, pcb_file);
00975   g_free (tmp);
00976 }
00977 
00978 static void
00979 prune_elements (gchar * pcb_file, gchar * bak)
00980 {
00981   FILE *f_in, *f_out;
00982   GList *list;
00983   PcbElement *el, *el_exists;
00984   gchar *fmt, *command, *tmp, *s, buf[1024];
00985   gint paren_level = 0;
00986   gboolean skipping = FALSE;
00987 
00988   for (list = pcb_element_list; list; list = g_list_next (list)) {
00989     el = (PcbElement *) list->data;
00990     if (!el->still_exists) {
00991       if (preserve) {
00992         ++n_preserved;
00993         fprintf (stderr,
00994                  "Preserving PCB element not in the schematic:    %s (element   %s)\n",
00995                  el->refdes, el->description);
00996       } else
00997         ++n_deleted;
00998     } else if (el->changed_value)
00999       ++n_changed_value;
01000   }
01001   if (!pcb_element_list
01002       || (n_deleted == 0 && !need_PKG_purge && n_changed_value == 0)
01003     )
01004     return;
01005   if ((f_in = fopen (pcb_file, "r")) == NULL)
01006     return;
01007   tmp = g_strconcat (pcb_file, ".tmp", NULL);
01008   if ((f_out = fopen (tmp, "wb")) == NULL) {
01009     fclose (f_in);
01010     return;
01011   }
01012   while ((fgets (buf, sizeof (buf), f_in)) != NULL) {
01013     for (s = buf; *s == ' ' || *s == '\t'; ++s);
01014     if (skipping) {
01015       if (*s == '(')
01016         ++paren_level;
01017       else if (*s == ')' && --paren_level <= 0)
01018         skipping = FALSE;
01019       continue;
01020     }
01021     el_exists = NULL;
01022     if ((el = pcb_element_line_parse (s)) != NULL
01023         && (el_exists = pcb_element_exists (el, FALSE)) != NULL
01024         && !el_exists->still_exists && !preserve) {
01025       skipping = TRUE;
01026       if (verbose)
01027         printf ("%s: deleted element %s (value=%s)\n",
01028                 el->refdes, el->description, el->value);
01029       pcb_element_free (el);
01030       continue;
01031     }
01032     if (el_exists && el_exists->changed_value) {
01033       fmt = el->quoted_flags ?
01034         "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" :
01035         "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n";
01036       fprintf (f_out, fmt,
01037                el->res_char, el->flags, el->description, el->refdes,
01038                el_exists->changed_value, el->x, el->y, el->tail);
01039       if (verbose)
01040         printf ("%s: changed element %s value: %s -> %s\n",
01041                 el->refdes, el->description,
01042                 el->value, el_exists->changed_value);
01043     } else if (!strncmp (s, "PKG_", 4))
01044       ++n_PKG_removed_old;
01045     else
01046       fputs (buf, f_out);
01047     pcb_element_free (el);
01048   }
01049   fclose (f_in);
01050   fclose (f_out);
01051 
01052   if (!bak_done) {
01053     build_and_run_command ("mv %s %s", pcb_file, bak);
01054     bak_done = TRUE;
01055   }
01056 
01057   build_and_run_command ("mv %s %s", tmp, pcb_file);
01058   g_free (tmp);
01059 }
01060 
01061 static void
01062 add_m4_file (gchar * arg)
01063 {
01064   gchar *s;
01065 
01066   if (!m4_files)
01067     m4_files = g_strdup (arg);
01068   else {
01069     s = m4_files;
01070     m4_files = g_strconcat (m4_files, " ", arg, NULL);
01071     g_free (s);
01072   }
01073 }
01074 
01075 static gchar *
01076 expand_dir (gchar * dir)
01077 {
01078   gchar *s;
01079 
01080   if (*dir == '~')
01081     s = g_build_filename ((gchar *) g_get_home_dir (), dir + 1, NULL);
01082   else
01083     s = g_strdup (dir);
01084   return s;
01085 }
01086 
01087 static void
01088 add_default_m4_files (void)
01089 {
01090   gchar *path;
01091 
01092   path = g_build_filename ((gchar *) g_get_home_dir (),
01093                            ".pcb", DEFAULT_PCB_INC, NULL);
01094   if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
01095     add_m4_file (path);
01096   g_free (path);
01097 
01098   if (g_file_test (DEFAULT_PCB_INC, G_FILE_TEST_IS_REGULAR))
01099     add_m4_file (DEFAULT_PCB_INC);
01100 
01101 }
01102 
01103 static void
01104 add_schematic (gchar * sch)
01105 {
01106   const gchar* s;
01107   schematics = g_list_append (schematics, g_strdup (sch));
01108   if (!sch_basename && (s = g_strrstr (sch, ".sch")) != NULL && strlen(s) == 4)
01109     sch_basename = g_strndup (sch, s - sch);
01110 }
01111 
01112 static void
01113 add_multiple_schematics (gchar * sch)
01114 {
01115   /* parse the string using shell semantics */
01116   gint count;
01117   gchar** args = NULL;
01118   GError* error = NULL;
01119 
01120   if (g_shell_parse_argv (sch, &count, &args, &error)) {
01121     int i;
01122     for (i = 0; i < count; ++i)
01123     {
01124       add_schematic (args[i]);
01125     }
01126     g_strfreev (args);
01127   } else {
01128     fprintf (stderr,
01129              "invalid `schematics' option: %s\n",
01130              error->message);
01131     g_error_free (error);
01132   }
01133 }
01134 
01135 static gint
01136 parse_config (gchar * config, gchar * arg)
01137 {
01138   gchar *s;
01139 
01140   /* remove trailing white space otherwise strange things can happen */
01141   if ((arg != NULL) && (strlen (arg) >= 1)) {
01142     s = arg + strlen (arg) - 1;
01143     while ((*s == ' ' || *s == '\t') && (s != arg))
01144       s--;
01145     s++;
01146     *s = '\0';
01147   }
01148   if (verbose)
01149     printf ("    %s \"%s\"\n", config, arg ? arg : "");
01150 
01151   if (!strcmp (config, "remove-unfound") || !strcmp (config, "r")) {
01152     /* This is default behavior set in header section */
01153     remove_unfound_elements = TRUE;
01154     return 0;
01155   }
01156   if (!strcmp (config, "keep-unfound") || !strcmp (config, "k")) {
01157     remove_unfound_elements = FALSE;
01158     return 0;
01159   }
01160   if (!strcmp (config, "quiet") || !strcmp (config, "q")) {
01161     quiet_mode = TRUE;
01162     return 0;
01163   }
01164   if (!strcmp (config, "preserve") || !strcmp (config, "p")) {
01165     preserve = TRUE;
01166     return 0;
01167   }
01168   if (!strcmp (config, "use-files") || !strcmp (config, "f")) {
01169     force_element_files = TRUE;
01170     return 0;
01171   }
01172   if (!strcmp (config, "skip-m4") || !strcmp (config, "s")) {
01173     use_m4 = FALSE;
01174     return 0;
01175   }
01176   if (!strcmp (config, "elements-dir") || !strcmp (config, "d")) {
01177     if (verbose > 1)
01178       printf ("\tAdding directory to file element directory list: %s\n",
01179               expand_dir (arg));
01180     element_directory_list =
01181       g_list_prepend (element_directory_list, expand_dir (arg));
01182   } else if (!strcmp (config, "output-name") || !strcmp (config, "o"))
01183     sch_basename = g_strdup (arg);
01184   else if (!strcmp (config, "schematics"))
01185     add_multiple_schematics (arg);
01186   else if (!strcmp (config, "m4-pcbdir")) {
01187     g_free (m4_pcbdir);
01188     m4_pcbdir = g_strdup (arg);
01189   } else if (!strcmp (config, "m4-file"))
01190     add_m4_file (arg);
01191   else if (!strcmp (config, "gnetlist"))
01192     extra_gnetlist_list = g_list_append (extra_gnetlist_list, g_strdup (arg));
01193   else if (!strcmp (config, "empty-footprint"))
01194     empty_footprint_name = g_strdup (arg);
01195   else
01196     return -1;
01197 
01198   return 1;
01199 }
01200 
01201 static void
01202 load_project (gchar * path)
01203 {
01204   FILE *f;
01205   gchar *s, buf[1024], config[32], arg[768];
01206 
01207   f = fopen (path, "r");
01208   if (!f)
01209     return;
01210   if (verbose)
01211     printf ("Reading project file: %s\n", path);
01212   while (fgets (buf, sizeof (buf), f)) {
01213     for (s = buf; *s == ' ' || *s == '\t' || *s == '\n'; ++s);
01214     if (!*s || *s == '#' || *s == '/' || *s == ';')
01215       continue;
01216     arg[0] = '\0';
01217     sscanf (s, "%31s %767[^\n]", config, arg);
01218     parse_config (config, arg);
01219   }
01220   fclose (f);
01221 }
01222 
01223 static void
01224 load_extra_project_files (void)
01225 {
01226   gchar *path;
01227   static gboolean done = FALSE;
01228 
01229   if (done)
01230     return;
01231 
01232   load_project ("/etc/gsch2pcb");
01233   load_project ("/usr/local/etc/gsch2pcb");
01234 
01235   path = g_build_filename ((gchar *) g_get_home_dir (), ".gEDA",
01236                            "gsch2pcb", NULL);
01237   load_project (path);
01238   g_free (path);
01239 
01240   done = TRUE;
01241 }
01242 
01243 static gchar *usage_string0 =
01244   "usage: gsch2pcb [options] {project | foo.sch [foo1.sch ...]}\n"
01245   "\n"
01246   "Generate a PCB layout file from a set of gschem schematics.\n"
01247   "   gnetlist -g PCB is run to generate foo.net from the schematics.\n"
01248   "\n"
01249   "   gnetlist -g gsch2pcb is run to get PCB m4 derived elements which\n"
01250   "   match schematic footprints.  For schematic footprints which don't match\n"
01251   "   any PCB m4 layout elements, search a set of file element directories in\n"
01252   "   an attempt to find matching PCB file elements.\n"
01253   "   Output to foo.pcb if it doesn't exist.  If there is a current foo.pcb,\n"
01254   "   output only new elements to foo.new.pcb.\n"
01255   "   If any elements with a non-empty element name in the current foo.pcb\n"
01256   "   have no matching schematic component, then remove those elements from\n"
01257   "   foo.pcb and rename foo.pcb to a foo.pcb.bak sequence.\n"
01258   "\n"
01259   "   gnetlist -g pcbpins is run to get a PCB actions file which will rename all\n"
01260   "   of the pins in a .pcb file to match pin names from the schematic.\n"
01261   "\n"
01262   "   \"project\" is a file (not ending in .sch) containing a list of\n"
01263   "   schematics to process and some options.  A schematics line is like:\n"
01264   "       schematics foo1.sch foo2.sch ...\n"
01265   "   Options in a project file are like command line args without the \"-\":\n"
01266   "       output-name myproject\n"
01267   "\n"
01268   "options (may be included in a project file):\n"
01269   "   -d, --elements-dir D  Search D for PCB file elements.  These defaults\n"
01270   "                         are searched if they exist: ./packages,\n"
01271   "                         /usr/local/share/pcb/newlib, /usr/share/pcb/newlib,\n"
01272   "                         (old pcb) /usr/local/lib/pcb_lib, /usr/lib/pcb_lib,\n"
01273   "                         (old pcb) /usr/local/pcb_lib\n"
01274   "   -o, --output-name N   Use output file names N.net, N.pcb, and N.new.pcb\n"
01275   "                         instead of foo.net, ... where foo is the basename\n"
01276   "                         of the first command line .sch file.\n"
01277   "   -f, --use-files       Force using file elements over m4 PCB elements\n"
01278   "                         for new footprints even though m4 elements are\n"
01279   "                         searched for first and may have been found.\n"
01280   "   -r, --remove-unfound  Don't include references to unfound elements in\n"
01281   "                         the generated .pcb files.  Use if you want PCB to\n"
01282   "                         be able to load the (incomplete) .pcb file.\n"
01283   "                         This is the default behavior.\n"
01284   "   -k, --keep-unfound    Keep include references to unfound elements in\n"
01285   "                         the generated .pcb files.  Use if you want to hand\n"
01286   "                         edit or otherwise preprocess the generated .pcb file\n"
01287   "                         before running pcb.\n"
01288   "   -p, --preserve        Preserve elements in PCB files which are not found\n"
01289   "                         in the schematics.  Note that elements with an empty\n"
01290   "                         element name (schematic refdes) are never deleted,\n"
01291   "                         so you really shouldn't need this option.\n"
01292   "   -q, --quiet           Don't tell the user what to do next after running gsch2pcb.\n"
01293   "\n"
01294   "   -s, --skip-m4         Skip m4 when looking for footprints.  The default is to use\n"
01295   "                         m4 (which is what previous versions did).\n"
01296   "       --m4-file F.inc   Use m4 file F.inc in addition to the default m4\n"
01297   "                         files ./pcb.inc and ~/.pcb/pcb.inc.\n"
01298   "       --m4-pcbdir D     Use D as the PCB m4 files install directory\n"
01299   "                         instead of the default:\n";
01300 
01301 static gchar *usage_string1 =
01302   "   --gnetlist backend    A convenience run of extra gnetlist -g commands.\n"
01303   "                         Example:  gnetlist partslist3\n"
01304   "                         Creates:  myproject.partslist3\n"
01305   " --empty-footprint name  See the project.sample file.\n"
01306   "\n"
01307   "options (not recognized in a project file):\n"
01308   "   --gnetlist-arg arg    Allows additional arguments to be passed to gnetlist.\n"
01309   "       --fix-elements    If a schematic component footprint is not equal\n"
01310   "                         to its PCB element Description, update the\n"
01311   "                         Description instead of replacing the element.\n"
01312   "                         Do this the first time gsch2pcb is used with\n"
01313   "                         PCB files originally created with gschem2pcb.\n"
01314   "   -v, --verbose         Use -v -v for additional file element debugging.\n"
01315   "   -V, --version\n\n"
01316   "environment variables:\n"
01317   "   GNETLIST              If set, this specifies the name of the gnetlist program\n"
01318   "                         to execute.\n"
01319   "\n"
01320   "Additional Resources:\n"
01321   "\n"
01322   "  gnetlist user guide:  http://geda.seul.org/wiki/geda:gnetlist_ug\n"
01323   "  gEDA homepage:        http://www.gpleda.org\n"
01324   "  PCB homepage:         http://pcb.gpleda.org\n"  "\n";
01325 
01326 static void
01327 usage ()
01328 {
01329   puts (usage_string0);
01330   printf ("                         %s\n", default_m4_pcbdir);
01331   puts (usage_string1);
01332   exit (0);
01333 }
01334 
01335 static void
01336 get_args (gint argc, gchar ** argv)
01337 {
01338   gchar *opt, *arg, *s;
01339   gint i, r;
01340 
01341   for (i = 1; i < argc; ++i) {
01342     opt = argv[i];
01343     arg = argv[i + 1];
01344     if (*opt == '-') {
01345       ++opt;
01346       if (*opt == '-')
01347         ++opt;
01348       if (!strcmp (opt, "version") || !strcmp (opt, "V")) {
01349         printf ("gsch2pcb %s\n", GSC2PCB_VERSION);
01350         exit (0);
01351       } else if (!strcmp (opt, "verbose") || !strcmp (opt, "v")) {
01352         verbose += 1;
01353         continue;
01354       } else if (!strcmp (opt, "fix-elements")) {
01355         fix_elements = TRUE;
01356         continue;
01357       } else if (!strcmp (opt, "gnetlist-arg")) {
01358         extra_gnetlist_arg_list =
01359           g_list_append (extra_gnetlist_arg_list, g_strdup (arg));
01360         i++;
01361         continue;
01362       } else if (!strcmp (opt, "help") || !strcmp (opt, "h"))
01363         usage ();
01364       else if (i < argc
01365                && ((r = parse_config (opt, (i < argc - 1) ? arg : NULL))
01366                    >= 0)
01367         ) {
01368         i += r;
01369         continue;
01370       }
01371       printf ("gsch2pcb: bad or incomplete arg: %s\n", argv[i]);
01372       usage ();
01373     } else {
01374       if (!g_str_has_suffix (argv[i], ".sch")) {
01375         load_extra_project_files ();
01376         load_project (argv[i]);
01377       } else
01378         add_schematic (argv[i]);
01379     }
01380   }
01381 }
01382 
01383 gint
01384 main (gint argc, gchar ** argv)
01385 {
01386   gchar *pcb_file_name,
01387     *pcb_new_file_name, *bak_file_name, *pins_file_name, *net_file_name, *tmp;
01388   gint i;
01389   gboolean initial_pcb = TRUE;
01390   gboolean created_pcb_file = TRUE;
01391   char *path, *p;
01392   const char *pcbdata_path;
01393 
01394   if (argc < 2)
01395     usage ();
01396 
01397   pcbdata_path = g_getenv ("PCBDATA");  /* do not free return value */
01398   if (pcbdata_path != NULL) {
01399     /* If PCBDATA is set, use the value */
01400     m4_pcbdir = g_strconcat (pcbdata_path, "/pcb/m4", NULL);
01401   } else {
01402     /* Use the default value passed in from the configure script
01403      * instead of trying to hard code a value which is very
01404      * likely wrong
01405      */
01406     m4_pcbdir = g_strconcat (PCBDATADIR, "/pcb/m4", NULL);
01407   }
01408 
01409   default_m4_pcbdir = g_strdup (m4_pcbdir);
01410 
01411   get_args (argc, argv);
01412 
01413   load_extra_project_files ();
01414   add_default_m4_files ();
01415 
01416   if (!schematics)
01417     usage ();
01418 
01419 
01420   /* Defaults for the search path if not configured in the project file */
01421   if (g_file_test ("packages", G_FILE_TEST_IS_DIR))
01422     element_directory_list = g_list_append (element_directory_list, "packages");
01423 
01424 #define PCB_PATH_DELIMETER ":"
01425   if (verbose)
01426     printf ("Processing PCBLIBPATH=\"%s\"\n", PCBLIBPATH);
01427 
01428   path = g_strdup (PCBLIBPATH);
01429   for (p = strtok (path, PCB_PATH_DELIMETER); p && *p;
01430        p = strtok (NULL, PCB_PATH_DELIMETER)) {
01431     if (g_file_test (p, G_FILE_TEST_IS_DIR)) {
01432       if (verbose)
01433         printf ("Adding %s to the newlib search path\n", p);
01434       element_directory_list = g_list_append (element_directory_list,
01435                                               g_strdup (p));
01436     }
01437   }
01438   g_free (path);
01439 
01440   pins_file_name = g_strconcat (sch_basename, ".cmd", NULL);
01441   net_file_name = g_strconcat (sch_basename, ".net", NULL);
01442   pcb_file_name = g_strconcat (sch_basename, ".pcb", NULL);
01443   bak_file_name = g_strconcat (sch_basename, ".pcb.bak", NULL);
01444   tmp = g_strdup (bak_file_name);
01445 
01446   for (i = 0; g_file_test (bak_file_name, G_FILE_TEST_EXISTS); ++i) {
01447     g_free (bak_file_name);
01448     bak_file_name = g_strdup_printf ("%s%d", tmp, i);
01449   }
01450   g_free (tmp);
01451 
01452   if (g_file_test (pcb_file_name, G_FILE_TEST_EXISTS)) {
01453     initial_pcb = FALSE;
01454     pcb_new_file_name = g_strconcat (sch_basename, ".new.pcb", NULL);
01455     get_pcb_element_list (pcb_file_name);
01456   } else
01457     pcb_new_file_name = g_strdup (pcb_file_name);
01458 
01459   if (!run_gnetlist (pins_file_name, net_file_name, pcb_new_file_name,
01460              sch_basename, schematics)) {
01461     fprintf(stderr, "Failed to run gnetlist\n");
01462     exit (1);
01463   }
01464 
01465   if (add_elements (pcb_new_file_name) == 0) {
01466     build_and_run_command ("rm %s", pcb_new_file_name);
01467     if (initial_pcb) {
01468       printf ("No elements found, so nothing to do.\n");
01469       exit (0);
01470     }
01471   }
01472 
01473   if (fix_elements)
01474     update_element_descriptions (pcb_file_name, bak_file_name);
01475   prune_elements (pcb_file_name, bak_file_name);
01476 
01477   /* Report work done during processing */
01478   if (verbose)
01479     printf ("\n");
01480   printf ("\n----------------------------------\n");
01481   printf ("Done processing.  Work performed:\n");
01482   if (n_deleted > 0 || n_fixed > 0 || need_PKG_purge || n_changed_value > 0)
01483     printf ("%s is backed up as %s.\n", pcb_file_name, bak_file_name);
01484   if (pcb_element_list && n_deleted > 0)
01485     printf ("%d elements deleted from %s.\n", n_deleted, pcb_file_name);
01486 
01487   if (n_added_ef + n_added_m4 > 0)
01488     printf ("%d file elements and %d m4 elements added to %s.\n",
01489             n_added_ef, n_added_m4, pcb_new_file_name);
01490   else if (n_not_found == 0) {
01491     printf ("No elements to add so not creating %s\n", pcb_new_file_name);
01492     created_pcb_file = FALSE;
01493   }
01494 
01495   if (n_not_found > 0) {
01496     printf ("%d not found elements added to %s.\n",
01497             n_not_found, pcb_new_file_name);
01498   }
01499   if (n_unknown > 0)
01500     printf ("%d components had no footprint attribute and are omitted.\n",
01501             n_unknown);
01502   if (n_none > 0)
01503     printf ("%d components with footprint \"none\" omitted from %s.\n",
01504             n_none, pcb_new_file_name);
01505   if (n_empty > 0)
01506     printf ("%d components with empty footprint \"%s\" omitted from %s.\n",
01507             n_empty, empty_footprint_name, pcb_new_file_name);
01508   if (n_changed_value > 0)
01509     printf ("%d elements had a value change in %s.\n",
01510             n_changed_value, pcb_file_name);
01511   if (n_fixed > 0)
01512     printf ("%d elements fixed in %s.\n", n_fixed, pcb_file_name);
01513   if (n_PKG_removed_old > 0) {
01514     printf ("%d elements could not be found.", n_PKG_removed_old);
01515     if (created_pcb_file)
01516       printf ("  So %s is incomplete.\n", pcb_file_name);
01517     else
01518       printf ("\n");
01519   }
01520   if (n_PKG_removed_new > 0) {
01521     printf ("%d elements could not be found.", n_PKG_removed_new);
01522     if (created_pcb_file)
01523       printf ("  So %s is incomplete.\n", pcb_new_file_name);
01524     else
01525       printf ("\n");
01526   }
01527   if (n_preserved > 0)
01528     printf ("%d elements not in the schematic preserved in %s.\n",
01529             n_preserved, pcb_file_name);
01530 
01531   /* Tell user what to do next */
01532   if (verbose)
01533     printf ("\n");
01534 
01535   if (n_added_ef + n_added_m4 > 0) {
01536     if (initial_pcb) {
01537       printf ("\nNext step:\n");
01538       printf ("1.  Run pcb on your file %s.\n", pcb_file_name);
01539       printf
01540         ("    You will find all your footprints in a bundle ready for you to place\n");
01541       printf
01542         ("    or disperse with \"Select -> Disperse all elements\" in PCB.\n\n");
01543       printf
01544         ("2.  From within PCB, select \"File -> Load netlist file\" and select \n");
01545       printf ("    %s to load the netlist.\n\n", net_file_name);
01546       printf ("3.  From within PCB, enter\n\n");
01547       printf ("           :ExecuteFile(%s)\n\n", pins_file_name);
01548       printf
01549         ("    to propagate the pin names of all footprints to the layout.\n\n");
01550     } else if (quiet_mode == FALSE) {
01551       printf ("\nNext steps:\n");
01552       printf ("1.  Run pcb on your file %s.\n", pcb_file_name);
01553       printf
01554         ("2.  From within PCB, select \"File -> Load layout data to paste buffer\"\n");
01555       printf
01556         ("    and select %s to load the new footprints into your existing layout.\n",
01557          pcb_new_file_name);
01558       printf
01559         ("3.  From within PCB, select \"File -> Load netlist file\" and select \n");
01560       printf ("    %s to load the updated netlist.\n\n", net_file_name);
01561       printf ("4.  From within PCB, enter\n\n");
01562       printf ("           :ExecuteFile(%s)\n\n", pins_file_name);
01563       printf ("    to update the pin names of all footprints.\n\n");
01564     }
01565   }
01566 
01567   g_free (net_file_name);
01568   g_free (pins_file_name);
01569   g_free (pcb_file_name);
01570   g_free (bak_file_name);
01571 
01572   return 0;
01573 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines