utils
|
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 }