Index: 1.1.4/util/gnet-pcbrndfwd_elem.scm =================================================================== --- 1.1.4/util/gnet-pcbrndfwd_elem.scm (nonexistent) +++ 1.1.4/util/gnet-pcbrndfwd_elem.scm (revision 5760) @@ -0,0 +1,107 @@ +;;; gEDA - GPL Electronic Design Automation +;;; gnetlist - gEDA Netlist +;;; Copyright (C) 1998-2008 Ales Hvezda +;;; Copyright (C) 1998-2008 gEDA Contributors (see ChangeLog for details) +;;; +;;; This program is free software; you can redistribute it and/or modify +;;; it under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 2 of the License, or +;;; (at your option) any later version. +;;; +;;; This program is distributed in the hope that it will be useful, +;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with this program; if not, write to the Free Software +;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +;; PCB forward annotation script; +;; modified version of pcbrndfwd: do not do the net list, only elements, +;; this way nets can be imported in the traditional netlist-way + +(use-modules (ice-9 regex)) +(use-modules (ice-9 format)) + +;; This is a list of attributes which are propogated to the pcb +;; elements. Note that refdes, value, and footprint need not be +;; listed here. +(define pcbrndfwd:element-attrs + '("device" + "manufacturer" + "manufacturer_part_number" + "vendor" + "vendor_part_number" + )) + +(define (pcbrndfwd:quote-string s) + (string-append "\"" + (regexp-substitute/global #f "\"" s 'pre "\\\"" 'post) + "\"") + ) + + +(define (pcbrndfwd:each-attr refdes attrs port) + (if (not (null? attrs)) + (let ((attr (car attrs))) + (format port "ElementSetAttr(~a,~a,~a)~%" + (pcbrndfwd:quote-string refdes) + (pcbrndfwd:quote-string attr) + (pcbrndfwd:quote-string (gnetlist:get-package-attribute refdes attr))) + (pcbrndfwd:each-attr refdes (cdr attrs) port)))) + +;; write out the pins for a particular component +(define pcbrndfwd:component_pins + (lambda (port package pins) + (if (and (not (null? package)) (not (null? pins))) + (begin + (let ( + (pin (car pins)) + (label #f) + (pinnum #f) + ) + (display "ChangePinName(" port) + (display (pcbrndfwd:quote-string package) port) + (display ", " port) + + (set! pinnum (gnetlist:get-attribute-by-pinnumber package pin "pinnumber")) + + (display pinnum port) + (display ", " port) + + (set! label (gnetlist:get-attribute-by-pinnumber package pin "pinlabel")) + (if (string=? label "unknown") + (set! label pinnum) + ) + (display (pcbrndfwd:quote-string label) port) + (display ")\n" port) + ) + (pcbrndfwd:component_pins port package (cdr pins)) + ) + ) + ) + ) + +(define (pcbrndfwd:each-element elements port) + (if (not (null? elements)) + (let* ((refdes (car elements)) + (value (gnetlist:get-package-attribute refdes "value")) + (footprint (gnetlist:get-package-attribute refdes "footprint")) + ) + + (format port "ElementList(Need,~a,~a,~a)~%" + (pcbrndfwd:quote-string refdes) + (pcbrndfwd:quote-string footprint) + (pcbrndfwd:quote-string value)) + (pcbrndfwd:each-attr refdes pcbrndfwd:element-attrs port) + (pcbrndfwd:component_pins port refdes (gnetlist:get-pins refdes)) + + (pcbrndfwd:each-element (cdr elements) port)))) + +(define (pcbrndfwd_elem output-filename) + (let ((port (open-output-file output-filename))) + (format port "ElementList(Start)\n") + (pcbrndfwd:each-element packages port) + (format port "ElementList(Done)\n") + (close-output-port port))) Index: 1.1.4/util/gsch2pcb-rnd/Makefile.in =================================================================== --- 1.1.4/util/gsch2pcb-rnd/Makefile.in (revision 5759) +++ 1.1.4/util/gsch2pcb-rnd/Makefile.in (revision 5760) @@ -47,7 +47,14 @@ CONF_OBJS = ../../src/vtlibrary.o ../../src/compat_fs.o ../../src/paths.o ../../src/conf.o ../../src/conf_core.o ../../src/hid_cfg.o ../../src/misc_util.o ../../src/unit.o ../../src/conf_internal.o ../../src/list_conf.o ../../src/conf_hid.o ../../src/pcb-printf.o ../../src/compat_misc.o FP_LDFLAGS = @/local/pcb/LDFLAGS@ FP_CFLAGS = @/local/pcb/CFLAGS@ -OBJS=gsch2pcb.o help.o +OBJS = \ + gsch2pcb.o \ + help.o \ + netlister.o \ + run.o \ + glue.o \ + method_pcb.o \ + method_import.o all: make revcheck Index: 1.1.4/util/gsch2pcb-rnd/glue.c =================================================================== --- 1.1.4/util/gsch2pcb-rnd/glue.c (nonexistent) +++ 1.1.4/util/gsch2pcb-rnd/glue.c (revision 5760) @@ -0,0 +1,61 @@ +/* gsch2pcb-rnd + * (C) 2015..2016, Tibor 'Igor2' Palinkas + * + * This program is free software which I release under the GNU General Public + * License. You may redistribute and/or modify this program under the terms + * of that license as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. Version 2 is in the + * COPYRIGHT file in the top level directory of this distribution. + * + * To get a copy of the GNU General Puplic License, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include "../src/error.h" + +/* glue for pcb-rnd core */ + +void ChdirErrorMessage(const char *DirName) +{ + fprintf(stderr, "gsch2pcb-rnd: warning: can't cd to %s\n", DirName); +} + +void OpendirErrorMessage(const char *DirName) +{ + fprintf(stderr, "gsch2pcb-rnd: warning: can't opendir %s\n", DirName); +} + +void PopenErrorMessage(const char *cmd) +{ + fprintf(stderr, "gsch2pcb-rnd: warning: can't popen %s\n", cmd); +} + +void Message(enum pcb_message_level level, const char *fmt, ...) +{ + va_list args; + fprintf(stderr, "gsch2pcb-rnd: "); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); +} + +void pcb_trace(const char *Format, ...) +{ +#ifndef NDEBUG + va_list args; + va_start(args, Format); + vfprintf(stderr, Format, args); + va_end(args); +#endif +} + +const char *pcb_board_get_filename(void) { return NULL; } +const char *pcb_board_get_name(void) { return NULL; } Index: 1.1.4/util/gsch2pcb-rnd/gsch2pcb.c =================================================================== --- 1.1.4/util/gsch2pcb-rnd/gsch2pcb.c (revision 5759) +++ 1.1.4/util/gsch2pcb-rnd/gsch2pcb.c (revision 5760) @@ -25,97 +25,53 @@ - use pcb-rnd's conf system - use popen() instead of glib's spawn (stderr is always printed to stderr) */ -/* for popen() */ -#define _DEFAULT_SOURCE -#define _BSD_SOURCE - #include "config.h" -#include #include #include -#include -#include -#include -#include -#include #include "../src/plug_footprint.h" -#include "../src/paths.h" #include "../src/conf.h" #include "../src/conf_core.h" -#include "../src_3rd/genvector/vts0.h" -#include "../src_3rd/genlist/gendlist.h" -#include "../src_3rd/genlist/genadlist.h" #include "../src_3rd/qparse/qparse.h" #include "../config.h" #include "../src/error.h" #include "../src/plugins.h" -#include "../src/plug_footprint.h" #include "../src/compat_misc.h" +#include "method.h" #include "help.h" #include "gsch2pcb_rnd_conf.h" +#include "gsch2pcb.h" +#include "method_pcb.h" +#include "method_import.h" -#define TRUE 1 -#define FALSE 0 +static const char *want_method_default = "pcb"; -#define GSCH2PCB_RND_VERSION "1.0.1" - -#define DEFAULT_PCB_INC "pcb.inc" - -#define SEP_STRING "--------\n" - -/* from scconfig str lib: */ -char *str_concat(const char *sep, ...); - -typedef struct { - char *refdes, *value, *description, *changed_description, *changed_value; - char *flags, *tail; - char *x, *y; - char *pkg_name_fix; - char res_char; - - gdl_elem_t all_elems; - - unsigned still_exists:1; - unsigned new_format:1; - unsigned hi_res_format:1; - unsigned quoted_flags:1; - unsigned omit_PKG:1; - unsigned nonetlist:1; - -} PcbElement; - -typedef struct { - char *part_number, *element_name; -} ElementMap; - gdl_list_t pcb_element_list; /* initialized to 0 */ gadl_list_t schematics, extra_gnetlist_arg_list, extra_gnetlist_list; -static int n_deleted, n_added_ef, n_fixed, n_PKG_removed_new, - n_PKG_removed_old, n_preserved, n_changed_value, n_not_found, - n_unknown, n_none, n_empty; +int n_deleted, n_added_ef, n_fixed, n_PKG_removed_new, + n_PKG_removed_old, n_preserved, n_changed_value, n_not_found, + n_unknown, n_none, n_empty; -static int bak_done, need_PKG_purge; +int bak_done, need_PKG_purge; conf_gsch2pcb_rnd_t conf_g2pr; -static const char *element_search_path = NULL; /* queried once from the config, when the config is already stable */ +method_t *methods = NULL, *current_method; -static char *loc_strndup(const char *str, size_t len) +void method_register(method_t *method) { - char *s; - int l; + method->next = methods; + methods = method; +} - if (str == NULL) - return NULL; - l = strlen(str); - if (l < len) - len = l; - s = malloc(len+1); - memcpy(s, str, len); - s[len] = '\0'; - return s; +method_t *method_find(const char *name) +{ + method_t *m; + for(m = methods; m != NULL; m = m->next) + if (strcmp(m->name, name) == 0) + return m; + return NULL; } /* Return a pointer to the suffix if inp ends in that suffix */ @@ -127,284 +83,8 @@ return NULL; } -/* Checks if a file exists and is readable */ -static int file_exists(const char *fn) +char *fix_spaces(char * str) { - FILE *f; - f = fopen(fn, "r"); - if (f == NULL) - return 0; - fclose(f); - return 1; -} - -void ChdirErrorMessage(const char *DirName) -{ - fprintf(stderr, "gsch2pcb-rnd: warning: can't cd to %s\n", DirName); -} - -void OpendirErrorMessage(const char *DirName) -{ - fprintf(stderr, "gsch2pcb-rnd: warning: can't opendir %s\n", DirName); -} - -void PopenErrorMessage(const char *cmd) -{ - fprintf(stderr, "gsch2pcb-rnd: warning: can't popen %s\n", cmd); -} - -void Message(enum pcb_message_level level, const char *fmt, ...) -{ - va_list args; - fprintf(stderr, "gsch2pcb-rnd: "); - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - fprintf(stderr, "\n"); -} - -void pcb_trace(const char *Format, ...) -{ -#ifndef NDEBUG - va_list args; - va_start(args, Format); - vfprintf(stderr, Format, args); - va_end(args); -#endif -} - - -/** - * Build and run a command. No redirection or error handling is - * done. Format string is split on whitespace. Specifiers %l and %s - * are replaced with contents of positional args. To be recognized, - * specifiers must be separated from other arguments in the format by - * whitespace. - * - %L expects a gadl_list_t vith char * payload, contents used as separate arguments - * - %s expects a char*, contents used as a single argument (omitted if NULL) - * @param[in] format used to specify command to be executed - * @param[in] ... positional parameters - */ -static int build_and_run_command(const char * format_, ...) -{ - va_list vargs; - int result = FALSE; - vts0_t args; - char *format, *s, *start; - - /* Translate the format string; args elements point to const char *'s - within a copy of the format string. The format string is copied so - that these parts can be terminated by overwriting whitepsace with \0 */ - va_start(vargs, format_); - format = pcb_strdup(format_); - vts0_init(&args); - for(s = start = format; *s != '\0'; s++) { - /* if word separator is reached, save the previous word */ - if (isspace(s[0])) { - if (start == s) { /* empty word - skip */ - start++; - continue; - } - *s = '\0'; - vts0_append(&args, start); - start = s+1; - continue; - } - - /* check if current word is a format */ - if ((s == start) && (s[0] == '%') && (s[1] != '\0') && ((s[2] == '\0') || isspace(s[2]))) { - switch(s[1]) { - case 'L': /* append contents of char * gadl_list_t */ - { - gadl_list_t *list = va_arg(vargs, gadl_list_t *); - gadl_iterator_t it; - char **s; - gadl_foreach(list, &it, s) { - vts0_append(&args, *s); - } - } - start = s+2; - s++; - continue; - case 's': - { - char *arg = va_arg(vargs, char *); - if (arg != NULL) - vts0_append(&args, arg); - start = s+2; - s++; - } - continue; - } - } - } - va_end(vargs); - - if (args.used > 0) { - int i, l; - char *cmd, *end, line[1024]; - FILE *f; - - l = 0; - for (i = 0; i < args.used; i++) - l += strlen(args.array[i]) + 3; - - end = cmd = malloc(l+1); - for (i = 0; i < args.used; i++) { - l = strlen(args.array[i]); - *end = '"'; end++; - memcpy(end, args.array[i], l); - end += l; - *end = '"'; end++; - *end = ' '; end++; - } - end--; - *end = '\0'; - - /* we have something in the list, build & call command */ - if (conf_g2pr.utils.gsch2pcb_rnd.verbose) { - printf("Running command:\n\t%s\n", cmd); - printf("%s", SEP_STRING); - } - - f = popen(cmd, "r"); - while(fgets(line, sizeof(line), f) != NULL) { - if (conf_g2pr.utils.gsch2pcb_rnd.verbose) - fputs(line, stdout); - } - - if (pclose(f) == 0) - result = TRUE; - else - fprintf(stderr, "Failed to execute external program\n"); - free(cmd); - - if (conf_g2pr.utils.gsch2pcb_rnd.verbose) - printf("\n%s", SEP_STRING); - } - - free(format); - vts0_uninit(&args); - return result; -} - -/* Run gnetlist to generate a netlist and a PCB board file. gnetlist - * has exit status of 0 even if it's given an invalid arg, so do some - * stat() hoops to decide if gnetlist successfully generated the PCB - * board file (only gnetlist >= 20030901 recognizes -m). - */ -static int run_gnetlist(const char * pins_file, const char * net_file, const char * pcb_file, const char * basename, const gadl_list_t *largs) -{ - struct stat st; - time_t mtime; - static const char *gnetlist = NULL; - gadl_iterator_t it; - char **sp; - char *verbose_str = NULL; - - /* Allow the user to specify a full path or a different name for - * the gnetlist command. Especially useful if multiple copies - * are installed at once. - */ - if (gnetlist == NULL) - gnetlist = getenv("GNETLIST"); - if (gnetlist == NULL) - gnetlist = "gnetlist"; - - if (!conf_g2pr.utils.gsch2pcb_rnd.verbose) - verbose_str = "-q"; - - if (!build_and_run_command("%s %s -g pcbpins -o %s %L %L", gnetlist, verbose_str, pins_file, &extra_gnetlist_arg_list, largs)) - return FALSE; - - if (!build_and_run_command("%s %s -g PCB -o %s %L %L", gnetlist, verbose_str, net_file, &extra_gnetlist_arg_list, largs)) - return FALSE; - - mtime = (stat(pcb_file, &st) == 0) ? st.st_mtime : 0; - - if (!build_and_run_command("%s %s -L " SCMDIR " -g gsch2pcb-rnd -o %s %L %L", - gnetlist, verbose_str, pcb_file, &extra_gnetlist_arg_list, largs)) { - if (stat(pcb_file, &st) != 0 || mtime == st.st_mtime) { - fprintf(stderr, "gsch2pcb: gnetlist command failed, `%s' not updated\n", pcb_file); - return FALSE; - } - return FALSE; - } - - gadl_foreach(&extra_gnetlist_list, &it, sp) { - const char *s = *sp; - const char *s2 = strstr(s, " -o "); - char *out_file; - char *backend; - if (!s2) { - out_file = str_concat(NULL, basename, ".", s, NULL); - backend = pcb_strdup(s); - } - else { - out_file = pcb_strdup(s2 + 4); - backend = loc_strndup(s, s2 - s); - } - - if (!build_and_run_command("%s %s -g %s -o %s %L %L", - gnetlist, verbose_str, backend, out_file, &extra_gnetlist_arg_list, largs)) - return FALSE; - free(out_file); - free(backend); - } - - return TRUE; -} - -static char *token(char * string, char ** next, int * quoted_ret, int parenth) -{ - static char *str; - char *s, *ret; - int quoted = FALSE; - - if (string) - str = string; - if (!str || !*str) { - if (next) - *next = str; - return pcb_strdup(""); - } - while (*str == ' ' || *str == '\t' || *str == ',' || *str == '\n') - ++str; - - if (*str == '"') { - quoted = TRUE; - if (quoted_ret) - *quoted_ret = TRUE; - ++str; - for (s = str; *s && *s != '"' && *s != '\n'; ++s); - } - else { - if (quoted_ret) - *quoted_ret = FALSE; - for (s = str; *s != '\0'; ++s) { - if ((parenth) && (*s == '(')) { - quoted = TRUE; - if (quoted_ret) - *quoted_ret = TRUE; - for (; *s && *s != ')' && *s != '\n'; ++s); - /* preserve closing ')' */ - if (*s == ')') - s++; - break; - } - if (*s == ' ' || *s == '\t' || *s == ',' || *s == '\n') - break; - } - } - ret = loc_strndup(str, s - str); - str = (quoted && *s) ? s + 1 : s; - if (next) - *next = str; - return ret; -} - -static char *fix_spaces(char * str) -{ char *s; if (!str) @@ -415,556 +95,6 @@ return str; } - /* As of 1/9/2004 CVS hi_res Element[] line format: - * Element[element_flags, description, pcb-name, value, mark_x, mark_y, - * text_x, text_y, text_direction, text_scale, text_flags] - * New PCB 1.7 / 1.99 Element() line format: - * Element(element_flags, description, pcb-name, value, mark_x, mark_y, - * text_x, text_y, text_direction, text_scale, text_flags) - * Old PCB 1.6 Element() line format: - * Element(element_flags, description, pcb-name, value, - * text_x, text_y, text_direction, text_scale, text_flags) - * - * (mark_x, mark_y) is the element position (mark) and (text_x,text_y) - * is the description text position which is absolute in pre 1.7 and - * is now relative. The hi_res mark_x,mark_y and text_x,text_y resolutions - * are 100x the other formats. - */ -PcbElement *pcb_element_line_parse(char * line) -{ - PcbElement *el = NULL; - char *s, *t, close_char; - int state = 0, elcount = 0, tmp; - - if (strncmp(line, "Element", 7)) - return NULL; - - el = calloc(sizeof(PcbElement), 1); - - s = line + 7; - while (*s == ' ' || *s == '\t') - ++s; - - if (*s == '[') - el->hi_res_format = TRUE; - else if (*s != '(') { - free(el); - return NULL; - } - - el->res_char = el->hi_res_format ? '[' : '('; - close_char = el->hi_res_format ? ']' : ')'; - - el->flags = token(s + 1, NULL, &tmp, 0); el->quoted_flags = tmp; - el->description = token(NULL, NULL, NULL, 0); - el->refdes = token(NULL, NULL, NULL, 0); - el->value = token(NULL, NULL, NULL, 0); - - el->x = token(NULL, NULL, NULL, 0); - el->y = token(NULL, &t, NULL, 0); - - el->tail = pcb_strdup(t ? t : ""); - if ((s = strrchr(el->tail, (int) '\n')) != NULL) - *s = '\0'; - - /* Count the tokens in tail to decide if it's new or old format. - * Old format will have 3 tokens, new format will have 5 tokens. - */ - for (s = el->tail; *s && *s != close_char; ++s) { - if (*s != ' ') { - if (state == 0) - ++elcount; - state = 1; - } - else - state = 0; - } - el->nonetlist = 0; - if (elcount > 4) { - el->new_format = TRUE; - if (strstr(el->tail, "nonetlist") != NULL) - el->nonetlist = 1; - } - - fix_spaces(el->description); - fix_spaces(el->refdes); - fix_spaces(el->value); - - /* Don't allow elements with no refdes to ever be deleted because - * they may be desired pc board elements not in schematics. So - * initialize still_exists to TRUE if empty or non-alphanumeric - * refdes. - */ - if (!*el->refdes || !isalnum((int) (*el->refdes))) - el->still_exists = TRUE; - - return el; -} - -static void pcb_element_free(PcbElement * el) -{ - if (!el) - return; - free(el->flags); - free(el->description); - free(el->changed_description); - free(el->changed_value); - free(el->refdes); - free(el->value); - free(el->x); - free(el->y); - free(el->tail); - free(el->pkg_name_fix); - free(el); -} - -static void get_pcb_element_list(char * pcb_file) -{ - FILE *f; - PcbElement *el; - char *s, buf[1024]; - - if ((f = fopen(pcb_file, "r")) == NULL) - return; - while ((fgets(buf, sizeof(buf), f)) != NULL) { - for (s = buf; *s == ' ' || *s == '\t'; ++s); - if (!strncmp(s, "PKG_", 4)) { - need_PKG_purge = TRUE; - continue; - } - if ((el = pcb_element_line_parse(s)) == NULL) - continue; - gdl_append(&pcb_element_list, el, all_elems); - } - fclose(f); -} - -static PcbElement *pcb_element_exists(PcbElement * el_test, int record) -{ - PcbElement *el; - gdl_iterator_t it; - - gdl_foreach(&pcb_element_list, &it, el) { - if (strcmp(el_test->refdes, el->refdes)) - continue; - if (strcmp(el_test->description, el->description)) { /* footprint */ - if (record) - el->changed_description = pcb_strdup(el_test->description); - } - else { - if (record) { - if (strcmp(el_test->value, el->value)) - el->changed_value = pcb_strdup(el_test->value); - el->still_exists = TRUE; - } - return el; - } - } - return NULL; -} - -/* A problem is that new PCB 1.7 file elements have the - * (mark_x,mark_y) value set to wherever the element was created and - * no equivalent of a gschem translate symbol was done. - * - * So, file elements inserted can be scattered over a big area and - * this is bad when loading a file.new.pcb into an existing PC - * board. So, do a simple translate if (mark_x,mark_y) is - * (arbitrarily) over 1000. I'll assume that for values < 1000 the - * element creator was concerned with a sane initial element - * placement. Unless someone has a better idea? Don't bother with - * pre PCB 1.7 formats as that would require parsing the mark(). - */ -static void simple_translate(PcbElement * el) -{ - if (el->x != NULL) - free(el->x); - if (el->y != NULL) - free(el->y); - el->x = pcb_strdup("0"); - el->y = pcb_strdup("0"); -} - -static int insert_element(FILE * f_out, FILE * f_elem, char * footprint, char * refdes, char * value) -{ - PcbElement *el; - char *fmt, *s, buf[1024]; - int retval = FALSE; - - /* Copy the file element lines. Substitute new parameters into the - * Element() or Element[] line and strip comments. - */ - while ((fgets(buf, sizeof(buf), f_elem)) != NULL) { - for (s = buf; *s == ' ' || *s == '\t'; ++s); - if ((el = pcb_element_line_parse(s)) != NULL) { - simple_translate(el); - fmt = el->quoted_flags ? "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" : "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n"; - - fprintf(f_out, fmt, el->res_char, el->flags, footprint, refdes, value, el->x, el->y, el->tail); - retval = TRUE; - } - else if (*s != '#') - fputs(buf, f_out); - pcb_element_free(el); - } - return retval; -} - - -char *search_element(PcbElement * el) -{ - char *elname = NULL, *path = NULL; - - if (!elname) - elname = pcb_strdup(el->description); - - if (!strcmp(elname, "unknown")) { - free(elname); - return NULL; - } - if (conf_g2pr.utils.gsch2pcb_rnd.verbose > 1) - printf("\tSearching directories looking for file element: %s\n", elname); - free(elname); - return path; -} - -/* The gnetlist backend gnet-gsch2pcb-rnd.scm generates PKG lines: - * - * PKG(footprint,refdes,value) - * - */ -static PcbElement *pkg_to_element(FILE * f, char * pkg_line) -{ - PcbElement *el; - char *s, *end, *refdes, *fp, *value; - -/*fprintf(stderr, "--- %s\n", pkg_line);*/ - - if (strncmp(pkg_line, "PKG", 3) - || (s = strchr(pkg_line, (int) '(')) == NULL) - return NULL; - -/* remove trailing ")" */ - end = s + strlen(s) - 2; - if (*end == ')') - *end = '\0'; - -/* tokenize the line keeping () */ - fp = token(s + 1, NULL, NULL, 1); - refdes = token(NULL, NULL, NULL, 1); - value = token(NULL, NULL, NULL, 1); - - -/*fprintf(stderr, "refdes: %s\n", refdes); -fprintf(stderr, " fp: %s\n", fp); -fprintf(stderr, " val: %s\n", value);*/ - - - if (!refdes || !fp || !value) { - if (refdes != NULL) - free(refdes); - if (fp != NULL) - free(fp); - if (value != NULL) - free(value); - fprintf(stderr, "Bad package line: %s\n", pkg_line); - return NULL; - } - - fix_spaces(refdes); - fix_spaces(value); - - el = calloc(sizeof(PcbElement), 1); - el->description = fp; - el->refdes = refdes; - el->value = value; - -/* -// wtf? -// if ((s = strchr (el->value, (int) ')')) != NULL) -// *s = '\0'; -*/ - - if (conf_g2pr.utils.gsch2pcb_rnd.empty_footprint_name && !strcmp(el->description, conf_g2pr.utils.gsch2pcb_rnd.empty_footprint_name)) { - if (conf_g2pr.utils.gsch2pcb_rnd.verbose) - printf("%s: has the empty footprint attribute \"%s\" so won't be in the layout.\n", el->refdes, el->description); - n_empty += 1; - el->omit_PKG = TRUE; - } - else if (!strcmp(el->description, "none")) { - fprintf(stderr, "WARNING: %s has a footprint attribute \"%s\" so won't be in the layout.\n", el->refdes, el->description); - n_none += 1; - el->omit_PKG = TRUE; - } - else if (!strcmp(el->description, "unknown")) { - fprintf(stderr, "WARNING: %s has no footprint attribute so won't be in the layout.\n", el->refdes); - n_unknown += 1; - el->omit_PKG = TRUE; - } - return el; -} - -/* Copies the content of fn to fout and returns 0 on success. */ -static int CatPCB(FILE * fout, const char *fn) -{ - FILE *fin; - fin = fopen(fn, "r"); - if (fin == NULL) - return -1; - - for (;;) { - char buff[1024]; - int len; - - len = fread(buff, 1, sizeof(buff), fin); - if (len <= 0) - break; - - fwrite(buff, len, 1, fout); - } - - fclose(fin); - return 0; -} - -/* Process the newly created pcb file which is the output from - * gnetlist -g gsch2pcb-rnd ... - * - * Insert elements for PKG_ lines if they be found by external element query. - * If there was an existing pcb file, strip out any elements if they are - * already present so that the new pcb file will only have new elements. - */ -static int add_elements(char * pcb_file) -{ - FILE *f_in, *f_out, *fp; - PcbElement *el = NULL; - char *tmp_file, *s, buf[1024]; - int total, paren_level = 0; - int skipping = FALSE; - int dpcb; - fp_fopen_ctx_t fctx; - - if ((f_in = fopen(pcb_file, "r")) == NULL) - return 0; - tmp_file = str_concat(NULL, pcb_file, ".tmp", NULL); - if ((f_out = fopen(tmp_file, "wb")) == NULL) { - fclose(f_in); - free(tmp_file); - return 0; - } - - if (conf_g2pr.utils.gsch2pcb_rnd.default_pcb == NULL) { - dpcb = -1; - conf_list_foreach_path_first(dpcb, &conf_core.rc.default_pcb_file, CatPCB(f_out, __path__)); - if (dpcb != 0) { - fprintf(stderr, "ERROR: can't load default pcb (using the configured search paths)\n"); - exit(1); - } - } - else { - if (CatPCB(f_out, conf_g2pr.utils.gsch2pcb_rnd.default_pcb) != 0) { - fprintf(stderr, "ERROR: can't load default pcb (using user defined %s)\n", conf_g2pr.utils.gsch2pcb_rnd.default_pcb); - exit(1); - } - } - - while ((fgets(buf, sizeof(buf), f_in)) != NULL) { - for (s = buf; *s == ' ' || *s == '\t'; ++s); - if (skipping) { - if (*s == '(') - ++paren_level; - else if (*s == ')' && --paren_level <= 0) - skipping = FALSE; - continue; - } - el = pkg_to_element(f_out, s); - if (el && pcb_element_exists(el, TRUE)) { - pcb_element_free(el); - continue; - } - if (!el || el->omit_PKG) { - if (el) { - - } - else - fputs(buf, f_out); - continue; - } - - { - if (conf_g2pr.utils.gsch2pcb_rnd.verbose) - printf("%s: need new element for footprint %s (value=%s)\n", el->refdes, el->description, el->value); - - fp = fp_fopen(element_search_path, el->description, &fctx); - - if (fp == NULL && conf_g2pr.utils.gsch2pcb_rnd.verbose) - printf("\tNo file element found.\n"); - - if ((fp != NULL) && insert_element(f_out, fp, el->description, el->refdes, el->value)) { - ++n_added_ef; - if (conf_g2pr.utils.gsch2pcb_rnd.verbose) - printf("%s: added new element for footprint %s (value=%s)\n", el->refdes, el->description, el->value); - } - else { - fprintf(stderr, "%s: can't find PCB element for footprint %s (value=%s)\n", el->refdes, el->description, el->value); - if (conf_g2pr.utils.gsch2pcb_rnd.remove_unfound_elements && !conf_g2pr.utils.gsch2pcb_rnd.fix_elements) { - fprintf(stderr, "So device %s will not be in the layout.\n", el->refdes); - ++n_PKG_removed_new; - } - else { - ++n_not_found; - fputs(buf, f_out); /* Copy PKG_ line */ - } - } - if (fp != NULL) - fp_fclose(fp, &fctx); - } - - pcb_element_free(el); - if (conf_g2pr.utils.gsch2pcb_rnd.verbose) - printf("----\n"); - } - fclose(f_in); - fclose(f_out); - - total = n_added_ef + n_not_found; - if (total == 0) - build_and_run_command("rm %s", tmp_file); - else - build_and_run_command("mv %s %s", tmp_file, pcb_file); - free(tmp_file); - return total; -} - -static void update_element_descriptions(char * pcb_file, char * bak) -{ - FILE *f_in, *f_out; - PcbElement *el, *el_exists; - char *fmt, *tmp, *s, buf[1024]; - gdl_iterator_t it; - - gdl_foreach(&pcb_element_list, &it, el) { - if (el->changed_description) - ++n_fixed; - } - if (!pcb_element_list.length || n_fixed == 0) { - fprintf(stderr, "Could not find any elements to fix.\n"); - return; - } - if ((f_in = fopen(pcb_file, "r")) == NULL) - return; - tmp = str_concat(NULL, pcb_file, ".tmp", NULL); - if ((f_out = fopen(tmp, "wb")) == NULL) { - fclose(f_in); - return; - } - while ((fgets(buf, sizeof(buf), f_in)) != NULL) { - for (s = buf; *s == ' ' || *s == '\t'; ++s); - if ((el = pcb_element_line_parse(s)) != NULL - && (el_exists = pcb_element_exists(el, FALSE)) != NULL && el_exists->changed_description) { - fmt = el->quoted_flags ? "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" : "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n"; - fprintf(f_out, fmt, - el->res_char, el->flags, el_exists->changed_description, el->refdes, el->value, el->x, el->y, el->tail); - printf("%s: updating element Description: %s -> %s\n", el->refdes, el->description, el_exists->changed_description); - el_exists->still_exists = TRUE; - } - else - fputs(buf, f_out); - pcb_element_free(el); - } - fclose(f_in); - fclose(f_out); - - if (!bak_done) { - build_and_run_command("mv %s %s", pcb_file, bak); - bak_done = TRUE; - } - - build_and_run_command("mv %s %s", tmp, pcb_file); - free(tmp); -} - -static void prune_elements(char * pcb_file, char * bak) -{ - FILE *f_in, *f_out; - PcbElement *el, *el_exists; - char *fmt, *tmp, *s, buf[1024]; - int paren_level = 0; - int skipping = FALSE; - gdl_iterator_t it; - - gdl_foreach(&pcb_element_list, &it, el) { - if (!el->still_exists) { - if ((conf_g2pr.utils.gsch2pcb_rnd.preserve) || (el->nonetlist)) { - ++n_preserved; - if (conf_g2pr.utils.gsch2pcb_rnd.verbose > 1) - fprintf(stderr, - "Preserving PCB element not in the schematic: %s (element %s) %s\n", - el->refdes, el->description, el->nonetlist ? "nonetlist" : ""); - } - else - ++n_deleted; - } - else if (el->changed_value) - ++n_changed_value; - } - if ((pcb_element_list.length == 0) || (n_deleted == 0 && !need_PKG_purge && n_changed_value == 0)) { - return; - } - if ((f_in = fopen(pcb_file, "r")) == NULL) { - fprintf(stderr, "error: can not read %s\n", pcb_file); - return; - } - tmp = str_concat(NULL, pcb_file, ".tmp", NULL); - if ((f_out = fopen(tmp, "wb")) == NULL) { - fprintf(stderr, "error: can not write %s\n", tmp); - fclose(f_in); - return; - } - - while ((fgets(buf, sizeof(buf), f_in)) != NULL) { - for (s = buf; *s == ' ' || *s == '\t'; ++s); - if (skipping) { - if (*s == '(') - ++paren_level; - else if (*s == ')' && --paren_level <= 0) - skipping = FALSE; - continue; - } - el_exists = NULL; - if ((el = pcb_element_line_parse(s)) != NULL - && (el_exists = pcb_element_exists(el, FALSE)) != NULL && !el_exists->still_exists && !conf_g2pr.utils.gsch2pcb_rnd.preserve && !el->nonetlist) { - skipping = TRUE; - if (conf_g2pr.utils.gsch2pcb_rnd.verbose) - printf("%s: deleted element %s (value=%s)\n", el->refdes, el->description, el->value); - pcb_element_free(el); - continue; - } - if (el_exists && el_exists->changed_value) { - fmt = el->quoted_flags ? "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" : "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n"; - fprintf(f_out, fmt, - el->res_char, el->flags, el->description, el->refdes, el_exists->changed_value, el->x, el->y, el->tail); - if (conf_g2pr.utils.gsch2pcb_rnd.verbose) - printf("%s: changed element %s value: %s -> %s\n", el->refdes, el->description, el->value, el_exists->changed_value); - } - else if (!strncmp(s, "PKG_", 4)) - ++n_PKG_removed_old; - else - fputs(buf, f_out); - pcb_element_free(el); - } - fclose(f_in); - fclose(f_out); - - if (!bak_done) { - build_and_run_command("mv %s %s", pcb_file, bak); - bak_done = TRUE; - } - - build_and_run_command("mv %s %s", tmp, pcb_file); - free(tmp); -} - static void add_schematic(char * sch) { char **n; @@ -974,7 +104,7 @@ if (!conf_g2pr.utils.gsch2pcb_rnd.sch_basename) { char *suff = loc_str_has_suffix(sch, ".sch", 4); if (suff != NULL) { - char *tmp = loc_strndup(sch, suff - sch); + char *tmp = pcb_strndup(sch, suff - sch); conf_set(CFR_CLI, "utils/gsch2pcb_rnd/sch_basename", -1, tmp, POL_OVERWRITE); free(tmp); } @@ -1103,6 +233,7 @@ done = TRUE; } +int have_project_file = 0; static void get_args(int argc, char ** argv) { char *opt, *arg; @@ -1125,6 +256,15 @@ conf_set(CFR_CLI, "utils/gsch2pcb_rnd/verbose", -1, tmp, POL_OVERWRITE); continue; } + else if (!strcmp(opt, "m") || !strcmp(opt, "method")) { + if (method_find(arg) == NULL) { + Message(PCB_MSG_ERROR, "Error: can't use unknown method '%s'; try --help\n", arg); + exit(1); + } + conf_set(CFR_CLI, "utils/gsch2pcb_rnd/method", -1, arg, POL_OVERWRITE); + i++; + continue; + } else if (!strcmp(opt, "c") || !strcmp(opt, "conf")) { const char *stmp; if (conf_set_from_cli(NULL, arg, NULL, &stmp) != 0) { @@ -1161,6 +301,7 @@ if (loc_str_has_suffix(argv[i], ".sch", 4) == NULL) { load_extra_project_files(); load_project(argv[i]); + have_project_file = 1; } else add_schematic(argv[i]); @@ -1187,22 +328,22 @@ #include "fp_init.h" +const char *local_project_pcb_name = NULL; + /************************ main ***********************/ -char *pcb_file_name, *pcb_new_file_name, *bak_file_name, *pins_file_name, *net_file_name; int main(int argc, char ** argv) { - int i; - int initial_pcb = TRUE; - int created_pcb_file = TRUE; + const char *want_method; + method_pcb_register(); + method_import_register(); + if (argc < 2) usage(); - conf_init(); conf_core_init(); - gadl_list_init(&schematics, sizeof(char *), NULL, NULL); gadl_list_init(&extra_gnetlist_arg_list, sizeof(char *), NULL, NULL); gadl_list_init(&extra_gnetlist_list, sizeof(char *), NULL, NULL); @@ -1223,121 +364,46 @@ load_extra_project_files(); + conf_update(NULL); /* because of CLI changes */ - fp_init(); - - element_search_path = fp_default_search_path(); - - if (gadl_length(&schematics) == 0) - usage(); - - pins_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".cmd", NULL); - net_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".net", NULL); - pcb_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".pcb", NULL); - - conf_load_project(NULL, pcb_file_name); - conf_update(NULL); /* because of the project file */ - - { /* set bak_file_name, finding the first number that results in a non-existing bak */ - int len; - char *end; - - len = strlen(conf_g2pr.utils.gsch2pcb_rnd.sch_basename); - bak_file_name = malloc(len+8+64); /* make room for ".pcb.bak" and an integer */ - memcpy(bak_file_name, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, len); - end = bak_file_name + len; - strcpy(end, ".pcb.bak"); - end += 8; - - for (i = 0; file_exists(bak_file_name); ++i) - sprintf(end, "%d", i); + want_method = conf_g2pr.utils.gsch2pcb_rnd.method; + if (want_method == NULL) { + method_t *m; + for(m = methods; m != NULL; m = m->next) { + if (m->guess_out_name()) { + current_method = m; + break; + } + } + if (current_method == NULL) { + want_method = want_method_default; + Message(PCB_MSG_WARNING, "Warning: method not specified for a project without a board; defaulting to %s. This warning is harmless if you are running gsch2pcb-rnd for the first time on this project and you are fine with this default method.", want_method); + } } - if (file_exists(pcb_file_name)) { - initial_pcb = FALSE; - pcb_new_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".new.pcb", NULL); - get_pcb_element_list(pcb_file_name); - } - else - pcb_new_file_name = pcb_strdup(pcb_file_name); - - if (!run_gnetlist(pins_file_name, net_file_name, pcb_new_file_name, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, &schematics)) { - fprintf(stderr, "Failed to run gnetlist\n"); - exit(1); - } - - if (add_elements(pcb_new_file_name) == 0) { - build_and_run_command("rm %s", pcb_new_file_name); - if (initial_pcb) { - printf("No elements found, so nothing to do.\n"); - exit(0); + if (current_method == NULL) { + current_method = method_find(want_method); + if (current_method == NULL) { + Message(PCB_MSG_ERROR, "Error: can't find method %s\n", want_method); + exit(1); } } - if (conf_g2pr.utils.gsch2pcb_rnd.fix_elements) - update_element_descriptions(pcb_file_name, bak_file_name); - prune_elements(pcb_file_name, bak_file_name); + current_method->init(); + conf_update(NULL); - /* Report work done during processing */ - if (conf_g2pr.utils.gsch2pcb_rnd.verbose) - printf("\n"); - printf("\n----------------------------------\n"); - printf("Done processing. Work performed:\n"); - if (n_deleted > 0 || n_fixed > 0 || need_PKG_purge || n_changed_value > 0) - printf("%s is backed up as %s.\n", pcb_file_name, bak_file_name); - if (pcb_element_list.length && n_deleted > 0) - printf("%d elements deleted from %s.\n", n_deleted, pcb_file_name); + if (gadl_length(&schematics) == 0) + usage(); - if (n_added_ef > 0) - printf("%d file elements added to %s.\n", n_added_ef, pcb_new_file_name); - else if (n_not_found == 0) { - printf("No elements to add so not creating %s\n", pcb_new_file_name); - created_pcb_file = FALSE; - } + if ((local_project_pcb_name != NULL) && (!have_project_file)) + conf_load_project(NULL, local_project_pcb_name); + conf_update(NULL); /* because of the project file */ - if (n_not_found > 0) { - printf("%d not found elements added to %s.\n", n_not_found, pcb_new_file_name); - } - if (n_unknown > 0) - printf("%d components had no footprint attribute and are omitted.\n", n_unknown); - if (n_none > 0) - printf("%d components with footprint \"none\" omitted from %s.\n", n_none, pcb_new_file_name); - if (n_empty > 0) - printf("%d components with empty footprint \"%s\" omitted from %s.\n", n_empty, conf_g2pr.utils.gsch2pcb_rnd.empty_footprint_name, pcb_new_file_name); - if (n_changed_value > 0) - printf("%d elements had a value change in %s.\n", n_changed_value, pcb_file_name); - if (n_fixed > 0) - printf("%d elements fixed in %s.\n", n_fixed, pcb_file_name); - if (n_PKG_removed_old > 0) { - printf("%d elements could not be found.", n_PKG_removed_old); - if (created_pcb_file) - printf(" So %s is incomplete.\n", pcb_file_name); - else - printf("\n"); - } - if (n_PKG_removed_new > 0) { - printf("%d elements could not be found.", n_PKG_removed_new); - if (created_pcb_file) - printf(" So %s is incomplete.\n", pcb_new_file_name); - else - printf("\n"); - } - if (n_preserved > 0) - printf("%d elements not in the schematic preserved in %s.\n", n_preserved, pcb_file_name); + current_method->go(); /* the traditional, "parse element and edit the pcb file" approach */ - /* Tell user what to do next */ - if (conf_g2pr.utils.gsch2pcb_rnd.verbose) - printf("\n"); + current_method->uninit(); - if (n_added_ef > 0) - next_steps(initial_pcb, conf_g2pr.utils.gsch2pcb_rnd.quiet_mode); - - free(net_file_name); - free(pins_file_name); - free(pcb_file_name); - free(bak_file_name); - conf_uninit(); free_strlist(&schematics); @@ -1344,8 +410,5 @@ free_strlist(&extra_gnetlist_arg_list); free_strlist(&extra_gnetlist_list); - if (pcb_new_file_name != NULL) - free(pcb_new_file_name); - return 0; } Index: 1.1.4/util/gsch2pcb-rnd/gsch2pcb.h =================================================================== --- 1.1.4/util/gsch2pcb-rnd/gsch2pcb.h (nonexistent) +++ 1.1.4/util/gsch2pcb-rnd/gsch2pcb.h (revision 5760) @@ -0,0 +1,48 @@ +#include "../src_3rd/genlist/gendlist.h" +#include "../src_3rd/genlist/genadlist.h" + +#define TRUE 1 +#define FALSE 0 + +#define GSCH2PCB_RND_VERSION "2.0.0" + +#define DEFAULT_PCB_INC "pcb.inc" + +#define SEP_STRING "--------\n" + +/* from scconfig str lib: */ +char *str_concat(const char *sep, ...); + +typedef struct { + char *refdes, *value, *description, *changed_description, *changed_value; + char *flags, *tail; + char *x, *y; + char *pkg_name_fix; + char res_char; + + gdl_elem_t all_elems; + + unsigned still_exists:1; + unsigned new_format:1; + unsigned hi_res_format:1; + unsigned quoted_flags:1; + unsigned omit_PKG:1; + unsigned nonetlist:1; +} PcbElement; + +typedef struct { + char *part_number, *element_name; +} ElementMap; + +extern gdl_list_t pcb_element_list; +extern gadl_list_t schematics, extra_gnetlist_arg_list, extra_gnetlist_list; + +extern int n_deleted, n_added_ef, n_fixed, n_PKG_removed_new, + n_PKG_removed_old, n_preserved, n_changed_value, n_not_found, + n_unknown, n_none, n_empty; + +extern int bak_done, need_PKG_purge; + +const char *local_project_pcb_name; /* file name of the design from which the local project file name shall be derived */ + +char *fix_spaces(char * str); Index: 1.1.4/util/gsch2pcb-rnd/gsch2pcb_rnd_conf.h =================================================================== --- 1.1.4/util/gsch2pcb-rnd/gsch2pcb_rnd_conf.h (revision 5759) +++ 1.1.4/util/gsch2pcb-rnd/gsch2pcb_rnd_conf.h (revision 5760) @@ -14,6 +14,7 @@ CFT_STRING sch_basename; CFT_STRING default_pcb; /* override default pcb with a given file */ CFT_STRING empty_footprint_name; + CFT_STRING method; } gsch2pcb_rnd; } utils; } conf_gsch2pcb_rnd_t; Index: 1.1.4/util/gsch2pcb-rnd/help.c =================================================================== --- 1.1.4/util/gsch2pcb-rnd/help.c (revision 5759) +++ 1.1.4/util/gsch2pcb-rnd/help.c (revision 5760) @@ -25,7 +25,7 @@ - use popen() instead of glib's spawn (stderr is always printed to stderr) */ #include "config.h" - +#include "method.h" #include #include @@ -32,22 +32,24 @@ extern char *pcb_file_name, *pcb_new_file_name, *bak_file_name, *pins_file_name, *net_file_name; -static char *usage_string0 = +static char *usage_string0a = "usage: gsch2pcb [options] {project | foo.sch [foo1.sch ...]}\n" "\n" "Generate a PCB layout file from a set of gschem schematics.\n" - " gnetlist -g PCB is run to generate foo.net from the schematics.\n" "\n" + " gnetlist -g PCB is run to generate foo.net from the schematics.\n" "\n"; /* TODO */ /* " gnetlist -g gsch2pcb is run to get PCB elements which\n" " match schematic footprints. For schematic footprints which don't match\n" " any PCB layout elements, search a set of file element directories in\n" " an attempt to find matching PCB file elements.\n"*/ +static char *usage_string0b = " Output to foo.pcb if it doesn't exist. If there is a current foo.pcb,\n" " output only new elements to foo.new.pcb.\n" " If any elements with a non-empty element name in the current foo.pcb\n" " have no matching schematic component, then remove those elements from\n" " foo.pcb and rename foo.pcb to a foo.pcb.bak sequence.\n" - "\n" + "\n"; +static char *usage_string0c = " gnetlist -g pcbpins is run to get a PCB actions file which will rename all\n" " of the pins in a .pcb file to match pin names from the schematic.\n" "\n" @@ -57,12 +59,16 @@ " Options in a project file are like command line args without the \"-\":\n" " output-name myproject\n" "\n" - "options (may be included in a project file):\n" + "options (may be included in a project file):\n"; +static char *usage_string0d = " -d, --elements-dir D Search D for PCB file elements. These defaults\n" " are searched if they exist. See the default\n" " search paths at the end of this text." " -c, --elements-dir-clr Clear the elements dir. Useful before a series\n" " if -d's to flush defaults.\n" + " -m, --method M Use method M for the import. See available\n" + " methods below.\n"; +static char *usage_string0e = " -s, --elements-shell S Use S as a prefix for running parametric footrint\n" " generators. It is useful on systems where popen()\n" " doesn't do the right thing or the process should\n" @@ -69,7 +75,8 @@ " be wrapped. Example -s \"/bin/sh -c\"\n" " -P, --default-pcb Change the default PCB file's name; this file is\n" " inserted on top of the *.new.pcb generated, for\n" - " PCB default settings\n" + " PCB default settings\n"; +static char *usage_string0f = " -o, --output-name N Use output file names N.net, N.pcb, and N.new.pcb\n" " instead of foo.net, ... where foo is the basename\n" " of the first command line .sch file.\n" @@ -76,24 +83,28 @@ " -r, --remove-unfound Don't include references to unfound elements in\n" " the generated .pcb files. Use if you want PCB to\n" " be able to load the (incomplete) .pcb file.\n" - " This is the default behavior.\n" + " This is the default behavior.\n"; +static char *usage_string0g = " -k, --keep-unfound Keep include references to unfound elements in\n" " the generated .pcb files. Use if you want to hand\n" " edit or otherwise preprocess the generated .pcb file\n" - " before running pcb.\n" + " before running pcb.\n"; +static char *usage_string0h = " -p, --preserve Preserve elements in PCB files which are not found\n" " in the schematics. Note that elements with an empty\n" " element name (schematic refdes) are never deleted,\n" - " so you really shouldn't need this option.\n" + " so you really shouldn't need this option.\n"; +static char *usage_string0i = " -q, --quiet Don't tell the user what to do next after running\n" " gsch2pcb-rnd.\n" "\n"; -static char *usage_string1 = +static char *usage_string1a = " --gnetlist backend A convenience run of extra gnetlist -g commands.\n" " Example: gnetlist partslist3\n" " Creates: myproject.partslist3\n" " --empty-footprint name See the project.sample file.\n" - "\n" + "\n"; +static char *usage_string1b = "options (not recognized in a project file):\n" " --gnetlist-arg arg Allows additional arguments to be passed to gnetlist.\n" " --fix-elements If a schematic component footprint is not equal\n" @@ -100,13 +111,16 @@ " to its PCB element Description, update the\n" " Description instead of replacing the element.\n" " Do this the first time gsch2pcb is used with\n" - " PCB files originally created with gschem2pcb.\n" + " PCB files originally created with gschem2pcb.\n"; +static char *usage_string1c = " -v, --verbose Use -v -v for additional file element debugging.\n" " -V, --version\n\n" "environment variables:\n" " GNETLIST If set, this specifies the name of the gnetlist program\n" " to execute.\n" - "\n" + "\n"; + +static char *usage_string_foot = "Additional Resources:\n" "\n" " gnetlist user guide: http://wiki.geda-project.org/geda:gnetlist_ug\n" @@ -116,33 +130,24 @@ void usage(void) { - puts(usage_string0); - puts(usage_string1); + method_t *m; + printf("%s", usage_string0a); + printf("%s", usage_string0b); + printf("%s", usage_string0c); + printf("%s", usage_string0d); + printf("%s", usage_string0e); + printf("%s", usage_string0f); + printf("%s", usage_string0g); + printf("%s", usage_string0h); + printf("%s", usage_string0i); + printf("%s", usage_string1a); + printf("%s", usage_string1b); + printf("%s", usage_string1c); + + printf("\nMethods available:\n"); + for(m = methods; m != NULL; m = m->next) + printf(" %-12s %s\n", m->name, m->desc); + printf("\n"); + printf("%s", usage_string_foot); exit(0); } - -void next_steps(int initial_pcb, int quiet_mode) -{ - if (initial_pcb) { - printf("\nNext step:\n"); - printf("1. Run pcb on your file %s.\n", pcb_file_name); - printf(" You will find all your footprints in a bundle ready for you to place\n"); - printf(" or disperse with \"Select -> Disperse all elements\" in PCB.\n\n"); - printf("2. From within PCB, select \"File -> Load netlist file\" and select \n"); - printf(" %s to load the netlist.\n\n", net_file_name); - printf("3. From within PCB, enter\n\n"); - printf(" :ExecuteFile(%s)\n\n", pins_file_name); - printf(" to propagate the pin names of all footprints to the layout.\n\n"); - } - else if (!quiet_mode) { - printf("\nNext steps:\n"); - printf("1. Run pcb on your file %s.\n", pcb_file_name); - printf("2. From within PCB, select \"File -> Load layout data to paste buffer\"\n"); - printf(" and select %s to load the new footprints into your existing layout.\n", pcb_new_file_name); - printf("3. From within PCB, select \"File -> Load netlist file\" and select \n"); - printf(" %s to load the updated netlist.\n\n", net_file_name); - printf("4. From within PCB, enter\n\n"); - printf(" :ExecuteFile(%s)\n\n", pins_file_name); - printf(" to update the pin names of all footprints.\n\n"); - } -} Index: 1.1.4/util/gsch2pcb-rnd/help.h =================================================================== --- 1.1.4/util/gsch2pcb-rnd/help.h (revision 5759) +++ 1.1.4/util/gsch2pcb-rnd/help.h (revision 5760) @@ -1,2 +1 @@ void usage(void); -void next_steps(int initial_pcb, int quiet_mode); Index: 1.1.4/util/gsch2pcb-rnd/method.h =================================================================== --- 1.1.4/util/gsch2pcb-rnd/method.h (nonexistent) +++ 1.1.4/util/gsch2pcb-rnd/method.h (revision 5760) @@ -0,0 +1,15 @@ +typedef struct method_s method_t; + +struct method_s { + const char *name; + const char *desc; + void (*init)(void); + void (*go)(void); + void (*uninit)(void); + int (*guess_out_name)(void); /* returns 1 if the output file of the format exists for the current project */ + method_t *next; +}; + +extern method_t *methods; + +void method_register(method_t *fmt); Index: 1.1.4/util/gsch2pcb-rnd/method_import.c =================================================================== --- 1.1.4/util/gsch2pcb-rnd/method_import.c (nonexistent) +++ 1.1.4/util/gsch2pcb-rnd/method_import.c (revision 5760) @@ -0,0 +1,141 @@ +/* gsch2pcb-rnd + * + * Original version: Bill Wilson billw@wt.net + * rnd-version: (C) 2015..2016, Tibor 'Igor2' Palinkas + * + * This program is free software which I release under the GNU General Public + * License. You may redistribute and/or modify this program under the terms + * of that license as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. Version 2 is in the + * COPYRIGHT file in the top level directory of this distribution. + * + * To get a copy of the GNU General Puplic License, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include "gsch2pcb.h" +#include "gsch2pcb_rnd_conf.h" +#include "method_pcb.h" +#include "run.h" +#include "netlister.h" +#include "method.h" +#include "../src/conf.h" +#include "../src/conf_core.h" +#include "../src/compat_misc.h" +#include "../src/compat_fs.h" + +char *cmd_file_name; +char *pcb_file_name; +char *net_file_name; + +static void method_import_init(void) +{ + pcb_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".pcb", NULL); + cmd_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".cmd", NULL); + net_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".net", NULL); + local_project_pcb_name = pcb_file_name; +} + +static void import_go(int sep_net) +{ + char *verbose_str = NULL; + const char *gnetlist, *backend; + + gnetlist = gnetlist_name(); + if (!conf_g2pr.utils.gsch2pcb_rnd.verbose) + verbose_str = "-q"; + + backend = sep_net ? "pcbrndfwd_elem" : "pcbrndfwd"; + if (!build_and_run_command("%s %s -L %s -g %s -o %s %L %L", gnetlist, verbose_str, PCBLIBDIR, backend, cmd_file_name, &extra_gnetlist_arg_list, &schematics)) { + fprintf(stderr, "Failed to run gnetlist with backend %s to generate the elements\n", backend); + exit(1); + } + + if (sep_net) { + if (!build_and_run_command("%s %s -L %s -g PCB -o %s %L %L", gnetlist, verbose_str, PCBLIBDIR, net_file_name, &extra_gnetlist_arg_list, &schematics)) { + fprintf(stderr, "Failed to run gnetlist net file\n"); + exit(1); + } + } + + /* Tell user what to do next */ + printf("\nNext step:\n"); + printf("1. Run pcb-rnd on your board file (or on an empty board if it's the first time).\n"); + printf("2. From within pcb-rnd, enter\n\n"); + printf(" :ExecuteFile(%s)\n\n", cmd_file_name); + + if (sep_net) { + printf(" (this will update the elements)\n\n"); + printf("3. From within pcb-rnd, select \"File -> Load netlist file\" and select \n"); + printf(" %s to load the updated netlist.\n\n", net_file_name); + } + else + printf(" (this will update the elements and the netlist)\n\n"); +} + +static void method_import_go() +{ + import_go(0); +} + +static void method_import_sep_go() +{ + import_go(1); +} + + +static int method_import_guess_out_name(void) +{ + int res; + char *name; + + name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".lht", NULL); + res = pcb_file_readable(name); + free(name); + if (!res) { + name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".pcb.lht", NULL); + res = pcb_file_readable(name); + free(name); + } + return res; +} + +static void method_import_uninit(void) +{ + if (pcb_file_name != NULL) + free(pcb_file_name); + if (cmd_file_name != NULL) + free(cmd_file_name); + if (net_file_name != NULL) + free(net_file_name); +} + +static method_t method_import; +static method_t method_import_sep; + +void method_import_register(void) +{ + method_import.name = "import"; + method_import.desc = "import schematics (pure action script)"; + method_import.init = method_import_init; + method_import.go = method_import_go; + method_import.uninit = method_import_uninit; + method_import.guess_out_name = method_import_guess_out_name; + method_register(&method_import); + + method_import_sep.name = "importsep"; + method_import_sep.desc = "import schematics (separate action script and netlist)"; + method_import_sep.init = method_import_init; + method_import_sep.go = method_import_sep_go; + method_import_sep.uninit = method_import_uninit; + method_import_sep.guess_out_name = method_import_guess_out_name; + method_register(&method_import_sep); +} Index: 1.1.4/util/gsch2pcb-rnd/method_import.h =================================================================== --- 1.1.4/util/gsch2pcb-rnd/method_import.h (nonexistent) +++ 1.1.4/util/gsch2pcb-rnd/method_import.h (revision 5760) @@ -0,0 +1 @@ +void method_import_register(void); Index: 1.1.4/util/gsch2pcb-rnd/method_pcb.c =================================================================== --- 1.1.4/util/gsch2pcb-rnd/method_pcb.c (nonexistent) +++ 1.1.4/util/gsch2pcb-rnd/method_pcb.c (revision 5760) @@ -0,0 +1,813 @@ +/* gsch2pcb-rnd + * + * Original version: Bill Wilson billw@wt.net + * rnd-version: (C) 2015..2016, Tibor 'Igor2' Palinkas + * + * This program is free software which I release under the GNU General Public + * License. You may redistribute and/or modify this program under the terms + * of that license as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. Version 2 is in the + * COPYRIGHT file in the top level directory of this distribution. + * + * To get a copy of the GNU General Puplic License, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include "gsch2pcb.h" +#include "gsch2pcb_rnd_conf.h" +#include "method_pcb.h" +#include "run.h" +#include "netlister.h" +#include "method.h" +#include "../src/plug_footprint.h" +#include "../src/paths.h" +#include "../src/conf.h" +#include "../src/conf_core.h" +#include "../src/compat_misc.h" +#include "../src/compat_fs.h" +#include "../src/plugins.h" +#include "../src/plug_footprint.h" + +static const char *element_search_path = NULL; /* queried once from the config, when the config is already stable */ + +static int insert_element(FILE * f_out, FILE * f_elem, char * footprint, char * refdes, char * value); + +static char *token(char * string, char ** next, int * quoted_ret, int parenth) +{ + static char *str; + char *s, *ret; + int quoted = FALSE; + + if (string) + str = string; + if (!str || !*str) { + if (next) + *next = str; + return pcb_strdup(""); + } + while (*str == ' ' || *str == '\t' || *str == ',' || *str == '\n') + ++str; + + if (*str == '"') { + quoted = TRUE; + if (quoted_ret) + *quoted_ret = TRUE; + ++str; + for (s = str; *s && *s != '"' && *s != '\n'; ++s); + } + else { + if (quoted_ret) + *quoted_ret = FALSE; + for (s = str; *s != '\0'; ++s) { + if ((parenth) && (*s == '(')) { + quoted = TRUE; + if (quoted_ret) + *quoted_ret = TRUE; + for (; *s && *s != ')' && *s != '\n'; ++s); + /* preserve closing ')' */ + if (*s == ')') + s++; + break; + } + if (*s == ' ' || *s == '\t' || *s == ',' || *s == '\n') + break; + } + } + ret = pcb_strndup(str, s - str); + str = (quoted && *s) ? s + 1 : s; + if (next) + *next = str; + return ret; +} + +static PcbElement *pkg_to_element(FILE * f, char * pkg_line) +{ + PcbElement *el; + char *s, *end, *refdes, *fp, *value; + +/*fprintf(stderr, "--- %s\n", pkg_line);*/ + + if (strncmp(pkg_line, "PKG", 3) + || (s = strchr(pkg_line, (int) '(')) == NULL) + return NULL; + +/* remove trailing ")" */ + end = s + strlen(s) - 2; + if (*end == ')') + *end = '\0'; + +/* tokenize the line keeping () */ + fp = token(s + 1, NULL, NULL, 1); + refdes = token(NULL, NULL, NULL, 1); + value = token(NULL, NULL, NULL, 1); + + +/*fprintf(stderr, "refdes: %s\n", refdes); +fprintf(stderr, " fp: %s\n", fp); +fprintf(stderr, " val: %s\n", value);*/ + + + if (!refdes || !fp || !value) { + if (refdes != NULL) + free(refdes); + if (fp != NULL) + free(fp); + if (value != NULL) + free(value); + fprintf(stderr, "Bad package line: %s\n", pkg_line); + return NULL; + } + + fix_spaces(refdes); + fix_spaces(value); + + el = calloc(sizeof(PcbElement), 1); + el->description = fp; + el->refdes = refdes; + el->value = value; + +/* +// wtf? +// if ((s = strchr (el->value, (int) ')')) != NULL) +// *s = '\0'; +*/ + + if (conf_g2pr.utils.gsch2pcb_rnd.empty_footprint_name && !strcmp(el->description, conf_g2pr.utils.gsch2pcb_rnd.empty_footprint_name)) { + if (conf_g2pr.utils.gsch2pcb_rnd.verbose) + printf("%s: has the empty footprint attribute \"%s\" so won't be in the layout.\n", el->refdes, el->description); + n_empty += 1; + el->omit_PKG = TRUE; + } + else if (!strcmp(el->description, "none")) { + fprintf(stderr, "WARNING: %s has a footprint attribute \"%s\" so won't be in the layout.\n", el->refdes, el->description); + n_none += 1; + el->omit_PKG = TRUE; + } + else if (!strcmp(el->description, "unknown")) { + fprintf(stderr, "WARNING: %s has no footprint attribute so won't be in the layout.\n", el->refdes); + n_unknown += 1; + el->omit_PKG = TRUE; + } + return el; +} + + +/* Copies the content of fn to fout and returns 0 on success. */ +static int CatPCB(FILE * fout, const char *fn) +{ + FILE *fin; + fin = fopen(fn, "r"); + if (fin == NULL) + return -1; + + for (;;) { + char buff[1024]; + int len; + + len = fread(buff, 1, sizeof(buff), fin); + if (len <= 0) + break; + + fwrite(buff, len, 1, fout); + } + + fclose(fin); + return 0; +} + +static void pcb_element_free(PcbElement * el) +{ + if (!el) + return; + free(el->flags); + free(el->description); + free(el->changed_description); + free(el->changed_value); + free(el->refdes); + free(el->value); + free(el->x); + free(el->y); + free(el->tail); + free(el->pkg_name_fix); + free(el); +} + +static PcbElement *pcb_element_exists(PcbElement * el_test, int record) +{ + PcbElement *el; + gdl_iterator_t it; + + gdl_foreach(&pcb_element_list, &it, el) { + if (strcmp(el_test->refdes, el->refdes)) + continue; + if (strcmp(el_test->description, el->description)) { /* footprint */ + if (record) + el->changed_description = pcb_strdup(el_test->description); + } + else { + if (record) { + if (strcmp(el_test->value, el->value)) + el->changed_value = pcb_strdup(el_test->value); + el->still_exists = TRUE; + } + return el; + } + } + return NULL; +} + +/* +static char *search_element(PcbElement * el) +{ + char *elname = NULL, *path = NULL; + + if (!elname) + elname = pcb_strdup(el->description); + + if (!strcmp(elname, "unknown")) { + free(elname); + return NULL; + } + if (conf_g2pr.utils.gsch2pcb_rnd.verbose > 1) + printf("\tSearching directories looking for file element: %s\n", elname); + free(elname); + return path; +} +*/ + + /* As of 1/9/2004 CVS hi_res Element[] line format: + * Element[element_flags, description, pcb-name, value, mark_x, mark_y, + * text_x, text_y, text_direction, text_scale, text_flags] + * New PCB 1.7 / 1.99 Element() line format: + * Element(element_flags, description, pcb-name, value, mark_x, mark_y, + * text_x, text_y, text_direction, text_scale, text_flags) + * Old PCB 1.6 Element() line format: + * Element(element_flags, description, pcb-name, value, + * text_x, text_y, text_direction, text_scale, text_flags) + * + * (mark_x, mark_y) is the element position (mark) and (text_x,text_y) + * is the description text position which is absolute in pre 1.7 and + * is now relative. The hi_res mark_x,mark_y and text_x,text_y resolutions + * are 100x the other formats. + */ +static PcbElement *pcb_element_line_parse(char * line) +{ + PcbElement *el = NULL; + char *s, *t, close_char; + int state = 0, elcount = 0, tmp; + + if (strncmp(line, "Element", 7)) + return NULL; + + el = calloc(sizeof(PcbElement), 1); + + s = line + 7; + while (*s == ' ' || *s == '\t') + ++s; + + if (*s == '[') + el->hi_res_format = TRUE; + else if (*s != '(') { + free(el); + return NULL; + } + + el->res_char = el->hi_res_format ? '[' : '('; + close_char = el->hi_res_format ? ']' : ')'; + + el->flags = token(s + 1, NULL, &tmp, 0); el->quoted_flags = tmp; + el->description = token(NULL, NULL, NULL, 0); + el->refdes = token(NULL, NULL, NULL, 0); + el->value = token(NULL, NULL, NULL, 0); + + el->x = token(NULL, NULL, NULL, 0); + el->y = token(NULL, &t, NULL, 0); + + el->tail = pcb_strdup(t ? t : ""); + if ((s = strrchr(el->tail, (int) '\n')) != NULL) + *s = '\0'; + + /* Count the tokens in tail to decide if it's new or old format. + * Old format will have 3 tokens, new format will have 5 tokens. + */ + for (s = el->tail; *s && *s != close_char; ++s) { + if (*s != ' ') { + if (state == 0) + ++elcount; + state = 1; + } + else + state = 0; + } + el->nonetlist = 0; + if (elcount > 4) { + el->new_format = TRUE; + if (strstr(el->tail, "nonetlist") != NULL) + el->nonetlist = 1; + } + + fix_spaces(el->description); + fix_spaces(el->refdes); + fix_spaces(el->value); + + /* Don't allow elements with no refdes to ever be deleted because + * they may be desired pc board elements not in schematics. So + * initialize still_exists to TRUE if empty or non-alphanumeric + * refdes. + */ + if (!*el->refdes || !isalnum((int) (*el->refdes))) + el->still_exists = TRUE; + + return el; +} + + +static void get_pcb_element_list(char * pcb_file) +{ + FILE *f; + PcbElement *el; + char *s, buf[1024]; + + if ((f = fopen(pcb_file, "r")) == NULL) + return; + while ((fgets(buf, sizeof(buf), f)) != NULL) { + for (s = buf; *s == ' ' || *s == '\t'; ++s); + if (!strncmp(s, "PKG_", 4)) { + need_PKG_purge = TRUE; + continue; + } + if ((el = pcb_element_line_parse(s)) == NULL) + continue; + gdl_append(&pcb_element_list, el, all_elems); + } + fclose(f); +} + +static void prune_elements(char * pcb_file, char * bak) +{ + FILE *f_in, *f_out; + PcbElement *el, *el_exists; + char *fmt, *tmp, *s, buf[1024]; + int paren_level = 0; + int skipping = FALSE; + gdl_iterator_t it; + + gdl_foreach(&pcb_element_list, &it, el) { + if (!el->still_exists) { + if ((conf_g2pr.utils.gsch2pcb_rnd.preserve) || (el->nonetlist)) { + ++n_preserved; + if (conf_g2pr.utils.gsch2pcb_rnd.verbose > 1) + fprintf(stderr, + "Preserving PCB element not in the schematic: %s (element %s) %s\n", + el->refdes, el->description, el->nonetlist ? "nonetlist" : ""); + } + else + ++n_deleted; + } + else if (el->changed_value) + ++n_changed_value; + } + if ((pcb_element_list.length == 0) || (n_deleted == 0 && !need_PKG_purge && n_changed_value == 0)) { + return; + } + if ((f_in = fopen(pcb_file, "r")) == NULL) { + fprintf(stderr, "error: can not read %s\n", pcb_file); + return; + } + tmp = str_concat(NULL, pcb_file, ".tmp", NULL); + if ((f_out = fopen(tmp, "wb")) == NULL) { + fprintf(stderr, "error: can not write %s\n", tmp); + fclose(f_in); + return; + } + + while ((fgets(buf, sizeof(buf), f_in)) != NULL) { + for (s = buf; *s == ' ' || *s == '\t'; ++s); + if (skipping) { + if (*s == '(') + ++paren_level; + else if (*s == ')' && --paren_level <= 0) + skipping = FALSE; + continue; + } + el_exists = NULL; + if ((el = pcb_element_line_parse(s)) != NULL + && (el_exists = pcb_element_exists(el, FALSE)) != NULL && !el_exists->still_exists && !conf_g2pr.utils.gsch2pcb_rnd.preserve && !el->nonetlist) { + skipping = TRUE; + if (conf_g2pr.utils.gsch2pcb_rnd.verbose) + printf("%s: deleted element %s (value=%s)\n", el->refdes, el->description, el->value); + pcb_element_free(el); + continue; + } + if (el_exists && el_exists->changed_value) { + fmt = el->quoted_flags ? "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" : "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n"; + fprintf(f_out, fmt, + el->res_char, el->flags, el->description, el->refdes, el_exists->changed_value, el->x, el->y, el->tail); + if (conf_g2pr.utils.gsch2pcb_rnd.verbose) + printf("%s: changed element %s value: %s -> %s\n", el->refdes, el->description, el->value, el_exists->changed_value); + } + else if (!strncmp(s, "PKG_", 4)) + ++n_PKG_removed_old; + else + fputs(buf, f_out); + pcb_element_free(el); + } + fclose(f_in); + fclose(f_out); + + if (!bak_done) { + build_and_run_command("mv %s %s", pcb_file, bak); + bak_done = TRUE; + } + + build_and_run_command("mv %s %s", tmp, pcb_file); + free(tmp); +} + +/* Process the newly created pcb file which is the output from + * gnetlist -g gsch2pcb-rnd ... + * + * Insert elements for PKG_ lines if they be found by external element query. + * If there was an existing pcb file, strip out any elements if they are + * already present so that the new pcb file will only have new elements. + */ +static int add_elements(char * pcb_file) +{ + FILE *f_in, *f_out, *fp; + PcbElement *el = NULL; + char *tmp_file, *s, buf[1024]; + int total, paren_level = 0; + int skipping = FALSE; + int dpcb; + fp_fopen_ctx_t fctx; + + if ((f_in = fopen(pcb_file, "r")) == NULL) + return 0; + tmp_file = str_concat(NULL, pcb_file, ".tmp", NULL); + if ((f_out = fopen(tmp_file, "wb")) == NULL) { + fclose(f_in); + free(tmp_file); + return 0; + } + + if (conf_g2pr.utils.gsch2pcb_rnd.default_pcb == NULL) { + dpcb = -1; + conf_list_foreach_path_first(dpcb, &conf_core.rc.default_pcb_file, CatPCB(f_out, __path__)); + if (dpcb != 0) { + fprintf(stderr, "ERROR: can't load default pcb (using the configured search paths)\n"); + exit(1); + } + } + else { + if (CatPCB(f_out, conf_g2pr.utils.gsch2pcb_rnd.default_pcb) != 0) { + fprintf(stderr, "ERROR: can't load default pcb (using user defined %s)\n", conf_g2pr.utils.gsch2pcb_rnd.default_pcb); + exit(1); + } + } + + while ((fgets(buf, sizeof(buf), f_in)) != NULL) { + for (s = buf; *s == ' ' || *s == '\t'; ++s); + if (skipping) { + if (*s == '(') + ++paren_level; + else if (*s == ')' && --paren_level <= 0) + skipping = FALSE; + continue; + } + el = pkg_to_element(f_out, s); + if (el && pcb_element_exists(el, TRUE)) { + pcb_element_free(el); + continue; + } + if (!el || el->omit_PKG) { + if (el) { + + } + else + fputs(buf, f_out); + continue; + } + + { + if (conf_g2pr.utils.gsch2pcb_rnd.verbose) + printf("%s: need new element for footprint %s (value=%s)\n", el->refdes, el->description, el->value); + + fp = fp_fopen(element_search_path, el->description, &fctx); + + if (fp == NULL && conf_g2pr.utils.gsch2pcb_rnd.verbose) + printf("\tNo file element found.\n"); + + if ((fp != NULL) && insert_element(f_out, fp, el->description, el->refdes, el->value)) { + ++n_added_ef; + if (conf_g2pr.utils.gsch2pcb_rnd.verbose) + printf("%s: added new element for footprint %s (value=%s)\n", el->refdes, el->description, el->value); + } + else { + fprintf(stderr, "%s: can't find PCB element for footprint %s (value=%s)\n", el->refdes, el->description, el->value); + if (conf_g2pr.utils.gsch2pcb_rnd.remove_unfound_elements && !conf_g2pr.utils.gsch2pcb_rnd.fix_elements) { + fprintf(stderr, "So device %s will not be in the layout.\n", el->refdes); + ++n_PKG_removed_new; + } + else { + ++n_not_found; + fprintf(f_out, "# gsch2pcb-rnd: element not found: %s\n", buf); /* Copy PKG_ line as comment */ + } + } + if (fp != NULL) + fp_fclose(fp, &fctx); + } + + pcb_element_free(el); + if (conf_g2pr.utils.gsch2pcb_rnd.verbose) + printf("----\n"); + } + fclose(f_in); + fclose(f_out); + + total = n_added_ef + n_not_found; + if (total == 0) + build_and_run_command("rm %s", tmp_file); + else + build_and_run_command("mv %s %s", tmp_file, pcb_file); + free(tmp_file); + return total; +} + +static void update_element_descriptions(char * pcb_file, char * bak) +{ + FILE *f_in, *f_out; + PcbElement *el, *el_exists; + char *fmt, *tmp, *s, buf[1024]; + gdl_iterator_t it; + + gdl_foreach(&pcb_element_list, &it, el) { + if (el->changed_description) + ++n_fixed; + } + if (!pcb_element_list.length || n_fixed == 0) { + fprintf(stderr, "Could not find any elements to fix.\n"); + return; + } + if ((f_in = fopen(pcb_file, "r")) == NULL) + return; + tmp = str_concat(NULL, pcb_file, ".tmp", NULL); + if ((f_out = fopen(tmp, "wb")) == NULL) { + fclose(f_in); + return; + } + while ((fgets(buf, sizeof(buf), f_in)) != NULL) { + for (s = buf; *s == ' ' || *s == '\t'; ++s); + if ((el = pcb_element_line_parse(s)) != NULL + && (el_exists = pcb_element_exists(el, FALSE)) != NULL && el_exists->changed_description) { + fmt = el->quoted_flags ? "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" : "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n"; + fprintf(f_out, fmt, + el->res_char, el->flags, el_exists->changed_description, el->refdes, el->value, el->x, el->y, el->tail); + printf("%s: updating element Description: %s -> %s\n", el->refdes, el->description, el_exists->changed_description); + el_exists->still_exists = TRUE; + } + else + fputs(buf, f_out); + pcb_element_free(el); + } + fclose(f_in); + fclose(f_out); + + if (!bak_done) { + build_and_run_command("mv %s %s", pcb_file, bak); + bak_done = TRUE; + } + + build_and_run_command("mv %s %s", tmp, pcb_file); + free(tmp); +} + +/* A problem is that new PCB 1.7 file elements have the + * (mark_x,mark_y) value set to wherever the element was created and + * no equivalent of a gschem translate symbol was done. + * + * So, file elements inserted can be scattered over a big area and + * this is bad when loading a file.new.pcb into an existing PC + * board. So, do a simple translate if (mark_x,mark_y) is + * (arbitrarily) over 1000. I'll assume that for values < 1000 the + * element creator was concerned with a sane initial element + * placement. Unless someone has a better idea? Don't bother with + * pre PCB 1.7 formats as that would require parsing the mark(). + */ +static void simple_translate(PcbElement * el) +{ + if (el->x != NULL) + free(el->x); + if (el->y != NULL) + free(el->y); + el->x = pcb_strdup("0"); + el->y = pcb_strdup("0"); +} + +static int insert_element(FILE * f_out, FILE * f_elem, char * footprint, char * refdes, char * value) +{ + PcbElement *el; + char *fmt, *s, buf[1024]; + int retval = FALSE; + + /* Copy the file element lines. Substitute new parameters into the + * Element() or Element[] line and strip comments. + */ + while ((fgets(buf, sizeof(buf), f_elem)) != NULL) { + for (s = buf; *s == ' ' || *s == '\t'; ++s); + if ((el = pcb_element_line_parse(s)) != NULL) { + simple_translate(el); + fmt = el->quoted_flags ? "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" : "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n"; + + fprintf(f_out, fmt, el->res_char, el->flags, footprint, refdes, value, el->x, el->y, el->tail); + retval = TRUE; + } + else if (*s != '#') + fputs(buf, f_out); + pcb_element_free(el); + } + return retval; +} + +/********************** The actual actions we perform ************************/ + +static char *pcb_file_name, *pcb_new_file_name, *bak_file_name, *pins_file_name, *net_file_name; + +static void method_pcb_init(void) +{ + fp_init(); + pins_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".cmd", NULL); + net_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".net", NULL); + pcb_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".pcb", NULL); + local_project_pcb_name = pcb_file_name; +} + +static void next_steps(int initial_pcb, int quiet_mode) +{ + if (initial_pcb) { + printf("\nNext step:\n"); + printf("1. Run pcb on your file %s.\n", pcb_file_name); + printf(" You will find all your footprints in a bundle ready for you to place\n"); + printf(" or disperse with \"Select -> Disperse all elements\" in PCB.\n\n"); + printf("2. From within PCB, select \"File -> Load netlist file\" and select \n"); + printf(" %s to load the netlist.\n\n", net_file_name); + printf("3. From within PCB, enter\n\n"); + printf(" :ExecuteFile(%s)\n\n", pins_file_name); + printf(" to propagate the pin names of all footprints to the layout.\n\n"); + } + else if (!quiet_mode) { + printf("\nNext steps:\n"); + printf("1. Run pcb on your file %s.\n", pcb_file_name); + printf("2. From within PCB, select \"File -> Load layout data to paste buffer\"\n"); + printf(" and select %s to load the new footprints into your existing layout.\n", pcb_new_file_name); + printf("3. From within PCB, select \"File -> Load netlist file\" and select \n"); + printf(" %s to load the updated netlist.\n\n", net_file_name); + printf("4. From within PCB, enter\n\n"); + printf(" :ExecuteFile(%s)\n\n", pins_file_name); + printf(" to update the pin names of all footprints.\n\n"); + } +} + +static void method_pcb_go() +{ + int initial_pcb = TRUE; + int created_pcb_file = TRUE; + + element_search_path = fp_default_search_path(); + + { /* set bak_file_name, finding the first number that results in a non-existing bak */ + int len; + char *end; + int i; + + len = strlen(conf_g2pr.utils.gsch2pcb_rnd.sch_basename); + bak_file_name = malloc(len+8+64); /* make room for ".pcb.bak" and an integer */ + memcpy(bak_file_name, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, len); + end = bak_file_name + len; + strcpy(end, ".pcb.bak"); + end += 8; + + for (i = 0; pcb_file_readable(bak_file_name); ++i) + sprintf(end, "%d", i); + } + + if (pcb_file_readable(pcb_file_name)) { + initial_pcb = FALSE; + pcb_new_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".new.pcb", NULL); + get_pcb_element_list(pcb_file_name); + } + else + pcb_new_file_name = pcb_strdup(pcb_file_name); + + if (!run_gnetlist(pins_file_name, net_file_name, pcb_new_file_name, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, &schematics)) { + fprintf(stderr, "Failed to run gnetlist\n"); + exit(1); + } + + if (add_elements(pcb_new_file_name) == 0) { + build_and_run_command("rm %s", pcb_new_file_name); + if (initial_pcb) { + printf("No elements found, so nothing to do.\n"); + exit(0); + } + } + + if (conf_g2pr.utils.gsch2pcb_rnd.fix_elements) + update_element_descriptions(pcb_file_name, bak_file_name); + prune_elements(pcb_file_name, bak_file_name); + + /* Report work done during processing */ + if (conf_g2pr.utils.gsch2pcb_rnd.verbose) + printf("\n"); + printf("\n----------------------------------\n"); + printf("Done processing. Work performed:\n"); + if (n_deleted > 0 || n_fixed > 0 || need_PKG_purge || n_changed_value > 0) + printf("%s is backed up as %s.\n", pcb_file_name, bak_file_name); + if (pcb_element_list.length && n_deleted > 0) + printf("%d elements deleted from %s.\n", n_deleted, pcb_file_name); + + if (n_added_ef > 0) + printf("%d file elements added to %s.\n", n_added_ef, pcb_new_file_name); + else if (n_not_found == 0) { + printf("No elements to add so not creating %s\n", pcb_new_file_name); + created_pcb_file = FALSE; + } + + if (n_not_found > 0) { + printf("%d not found elements added to %s.\n", n_not_found, pcb_new_file_name); + } + if (n_unknown > 0) + printf("%d components had no footprint attribute and are omitted.\n", n_unknown); + if (n_none > 0) + printf("%d components with footprint \"none\" omitted from %s.\n", n_none, pcb_new_file_name); + if (n_empty > 0) + printf("%d components with empty footprint \"%s\" omitted from %s.\n", n_empty, conf_g2pr.utils.gsch2pcb_rnd.empty_footprint_name, pcb_new_file_name); + if (n_changed_value > 0) + printf("%d elements had a value change in %s.\n", n_changed_value, pcb_file_name); + if (n_fixed > 0) + printf("%d elements fixed in %s.\n", n_fixed, pcb_file_name); + if (n_PKG_removed_old > 0) { + printf("%d elements could not be found.", n_PKG_removed_old); + if (created_pcb_file) + printf(" So %s is incomplete.\n", pcb_file_name); + else + printf("\n"); + } + if (n_PKG_removed_new > 0) { + printf("%d elements could not be found.", n_PKG_removed_new); + if (created_pcb_file) + printf(" So %s is incomplete.\n", pcb_new_file_name); + else + printf("\n"); + } + if (n_preserved > 0) + printf("%d elements not in the schematic preserved in %s.\n", n_preserved, pcb_file_name); + + /* Tell user what to do next */ + if (conf_g2pr.utils.gsch2pcb_rnd.verbose) + printf("\n"); + + if (n_added_ef > 0) + next_steps(initial_pcb, conf_g2pr.utils.gsch2pcb_rnd.quiet_mode); + + free(net_file_name); + free(pins_file_name); + free(pcb_file_name); + free(bak_file_name); +} + +static int method_pcb_guess_out_name(void) +{ + int res; + char *name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".pcb", NULL); + res = pcb_file_readable(name); + free(name); + return res; +} + + +void method_pcb_uninit(void) +{ + if (pcb_new_file_name != NULL) + free(pcb_new_file_name); +} + +static method_t method_pcb; + +void method_pcb_register(void) +{ + method_pcb.name = "pcb"; + method_pcb.desc = "traditional: load footprints and edit the .pcb files"; + method_pcb.init = method_pcb_init; + method_pcb.go = method_pcb_go; + method_pcb.uninit = method_pcb_uninit; + method_pcb.guess_out_name = method_pcb_guess_out_name; + method_register(&method_pcb); +} Index: 1.1.4/util/gsch2pcb-rnd/method_pcb.h =================================================================== --- 1.1.4/util/gsch2pcb-rnd/method_pcb.h (nonexistent) +++ 1.1.4/util/gsch2pcb-rnd/method_pcb.h (revision 5760) @@ -0,0 +1 @@ +void method_pcb_register(void); Index: 1.1.4/util/gsch2pcb-rnd/netlister.c =================================================================== --- 1.1.4/util/gsch2pcb-rnd/netlister.c (nonexistent) +++ 1.1.4/util/gsch2pcb-rnd/netlister.c (revision 5760) @@ -0,0 +1,111 @@ +/* gsch2pcb-rnd + * + * Original version: Bill Wilson billw@wt.net + * rnd-version: (C) 2015..2016, Tibor 'Igor2' Palinkas + * + * This program is free software which I release under the GNU General Public + * License. You may redistribute and/or modify this program under the terms + * of that license as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. Version 2 is in the + * COPYRIGHT file in the top level directory of this distribution. + * + * To get a copy of the GNU General Puplic License, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "config.h" + +#include +#include +#include +#include +/*#include */ +#include +#include +#include +#include "../src/plug_footprint.h" +#include "../src/paths.h" +#include "../src/conf.h" +#include "../src/conf_core.h" +/*#include "../src_3rd/qparse/qparse.h"*/ +#include "../config.h" +/*#include "../src/error.h"*/ +/*#include "../src/plugins.h" +#include "../src/plug_footprint.h"*/ +#include "../src/compat_misc.h" +#include "gsch2pcb_rnd_conf.h" +#include "gsch2pcb.h" +#include "run.h" + +const char *gnetlist_name(void) +{ + const char *gnetlist; + /* Allow the user to specify a full path or a different name for + * the gnetlist command. Especially useful if multiple copies + * are installed at once. + */ + gnetlist = getenv("GNETLIST"); + if (gnetlist == NULL) + gnetlist = "gnetlist"; + return gnetlist; +} + +int run_gnetlist(const char *pins_file, const char *net_file, const char *pcb_file, const char * basename, const gadl_list_t *largs) +{ + struct stat st; + time_t mtime; + const char *gnetlist; + gadl_iterator_t it; + char **sp; + char *verbose_str = NULL; + + gnetlist = gnetlist_name(); + + if (!conf_g2pr.utils.gsch2pcb_rnd.verbose) + verbose_str = "-q"; + + if (!build_and_run_command("%s %s -g pcbpins -o %s %L %L", gnetlist, verbose_str, pins_file, &extra_gnetlist_arg_list, largs)) + return FALSE; + + if (!build_and_run_command("%s %s -g PCB -o %s %L %L", gnetlist, verbose_str, net_file, &extra_gnetlist_arg_list, largs)) + return FALSE; + + mtime = (stat(pcb_file, &st) == 0) ? st.st_mtime : 0; + + if (!build_and_run_command("%s %s -L " SCMDIR " -g gsch2pcb-rnd -o %s %L %L", + gnetlist, verbose_str, pcb_file, &extra_gnetlist_arg_list, largs)) { + if (stat(pcb_file, &st) != 0 || mtime == st.st_mtime) { + fprintf(stderr, "gsch2pcb: gnetlist command failed, `%s' not updated\n", pcb_file); + return FALSE; + } + return FALSE; + } + + gadl_foreach(&extra_gnetlist_list, &it, sp) { + const char *s = *sp; + const char *s2 = strstr(s, " -o "); + char *out_file; + char *backend; + if (!s2) { + out_file = str_concat(NULL, basename, ".", s, NULL); + backend = pcb_strdup(s); + } + else { + out_file = pcb_strdup(s2 + 4); + backend = pcb_strndup(s, s2 - s); + } + + if (!build_and_run_command("%s %s -g %s -o %s %L %L", + gnetlist, verbose_str, backend, out_file, &extra_gnetlist_arg_list, largs)) + return FALSE; + free(out_file); + free(backend); + } + + return TRUE; +} Index: 1.1.4/util/gsch2pcb-rnd/netlister.h =================================================================== --- 1.1.4/util/gsch2pcb-rnd/netlister.h (nonexistent) +++ 1.1.4/util/gsch2pcb-rnd/netlister.h (revision 5760) @@ -0,0 +1,11 @@ +/* Run gnetlist to generate a netlist and a PCB board file. gnetlist + * has exit status of 0 even if it's given an invalid arg, so do some + * stat() hoops to decide if gnetlist successfully generated the PCB + * board file (only gnetlist >= 20030901 recognizes -m). + */ +int run_gnetlist(const char *pins_file, const char *net_file, const char *pcb_file, const char * basename, const gadl_list_t *largs); + + +/* Return the name of gnetlist that should be executed */ +const char *gnetlist_name(void); + Index: 1.1.4/util/gsch2pcb-rnd/run.c =================================================================== --- 1.1.4/util/gsch2pcb-rnd/run.c (nonexistent) +++ 1.1.4/util/gsch2pcb-rnd/run.c (revision 5760) @@ -0,0 +1,137 @@ +/* gsch2pcb-rnd + * + * Original version: Bill Wilson billw@wt.net + * rnd-version: (C) 2015..2016, Tibor 'Igor2' Palinkas + * + * This program is free software which I release under the GNU General Public + * License. You may redistribute and/or modify this program under the terms + * of that license as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. Version 2 is in the + * COPYRIGHT file in the top level directory of this distribution. + * + * To get a copy of the GNU General Puplic License, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* for popen() */ +#define _DEFAULT_SOURCE +#define _BSD_SOURCE + +#include +#include +#include +#include +#include +#include "gsch2pcb.h" +#include "../src_3rd/genvector/vts0.h" +#include "../src/compat_misc.h" +#include "gsch2pcb_rnd_conf.h" + + +int build_and_run_command(const char * format_, ...) +{ + va_list vargs; + int result = FALSE; + vts0_t args; + char *format, *s, *start; + + /* Translate the format string; args elements point to const char *'s + within a copy of the format string. The format string is copied so + that these parts can be terminated by overwriting whitepsace with \0 */ + va_start(vargs, format_); + format = pcb_strdup(format_); + vts0_init(&args); + for(s = start = format; *s != '\0'; s++) { + /* if word separator is reached, save the previous word */ + if (isspace(s[0])) { + if (start == s) { /* empty word - skip */ + start++; + continue; + } + *s = '\0'; + vts0_append(&args, start); + start = s+1; + continue; + } + + /* check if current word is a format */ + if ((s == start) && (s[0] == '%') && (s[1] != '\0') && ((s[2] == '\0') || isspace(s[2]))) { + switch(s[1]) { + case 'L': /* append contents of char * gadl_list_t */ + { + gadl_list_t *list = va_arg(vargs, gadl_list_t *); + gadl_iterator_t it; + char **s; + gadl_foreach(list, &it, s) { + vts0_append(&args, *s); + } + } + start = s+2; + s++; + continue; + case 's': + { + char *arg = va_arg(vargs, char *); + if (arg != NULL) + vts0_append(&args, arg); + start = s+2; + s++; + } + continue; + } + } + } + va_end(vargs); + + if (args.used > 0) { + int i, l; + char *cmd, *end, line[1024]; + FILE *f; + + l = 0; + for (i = 0; i < args.used; i++) + l += strlen(args.array[i]) + 3; + + end = cmd = malloc(l+1); + for (i = 0; i < args.used; i++) { + l = strlen(args.array[i]); + *end = '"'; end++; + memcpy(end, args.array[i], l); + end += l; + *end = '"'; end++; + *end = ' '; end++; + } + end--; + *end = '\0'; + + /* we have something in the list, build & call command */ + if (conf_g2pr.utils.gsch2pcb_rnd.verbose) { + printf("Running command:\n\t%s\n", cmd); + printf("%s", SEP_STRING); + } + + f = popen(cmd, "r"); + while(fgets(line, sizeof(line), f) != NULL) { + if (conf_g2pr.utils.gsch2pcb_rnd.verbose) + fputs(line, stdout); + } + + if (pclose(f) == 0) + result = TRUE; + else + fprintf(stderr, "Failed to execute external program\n"); + free(cmd); + + if (conf_g2pr.utils.gsch2pcb_rnd.verbose) + printf("\n%s", SEP_STRING); + } + + free(format); + vts0_uninit(&args); + return result; +} Index: 1.1.4/util/gsch2pcb-rnd/run.h =================================================================== --- 1.1.4/util/gsch2pcb-rnd/run.h (nonexistent) +++ 1.1.4/util/gsch2pcb-rnd/run.h (revision 5760) @@ -0,0 +1,13 @@ +/** + * Build and run a command. No redirection or error handling is + * done. Format string is split on whitespace. Specifiers %l and %s + * are replaced with contents of positional args. To be recognized, + * specifiers must be separated from other arguments in the format by + * whitespace. + * - %L expects a gadl_list_t vith char * payload, contents used as separate arguments + * - %s expects a char*, contents used as a single argument (omitted if NULL) + * @param[in] format used to specify command to be executed + * @param[in] ... positional parameters + */ +int build_and_run_command(const char * format_, ...); +