/* File: editprop.c * * This file is part of XSCHEM, * a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit * simulation. * Copyright (C) 1998-2023 Stefan Frederik Schippers * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "xschem.h" double mylog10(double x) { if(x > 0) return log10(x); else return -35; } double mylog(double x) { if(x > 0) return log(x); else return -35; } int my_strcasecmp(const char *s1, const char *s2) { while(tolower(*s1) == tolower(*s2)) { if (*s1 == '\0') return 0; s1++; s2++; } return tolower(*s1) - tolower(*s2); } char *my_strcasestr(const char *haystack, const char *needle) { const char *h, *n; int found = 0; if(needle[0] == '\0') return (char *)haystack; for(h = haystack; *h; h++) { found = 1; for(n = needle; *n; n++) { const char *hh = h + (n - needle); dbg(1, "%c %c\n", *hh, *n); if(toupper(*hh) != toupper(*n)) { found = 0; break; } } if(found) return (char *)h; } return NULL; } int my_strncasecmp(const char *s1, const char *s2, size_t n) { if (n == 0) return 0; while(tolower(*s1) == tolower(*s2)) { if (--n == 0) return 0; if (*s1 == '\0') return 0; s1++; s2++; } return tolower(*s1) - tolower(*s2); } /* same as case insensitive strcmp(), but allow '1, true, on, yes' for true value * both in str and boolean */ int strboolcmp(const char *str, const char *boolean) { int retval, s = 0, b = 0; if(!my_strcasecmp(boolean, "true")) b = 1; else if(!my_strcasecmp(boolean, "false")) b = 0; else b = -1; if(!my_strcasecmp(str, "true") || !my_strcasecmp(str, "1") || !my_strcasecmp(str, "on") || !my_strcasecmp(str, "yes")) s = 1; else if(!my_strcasecmp(str, "false") || !my_strcasecmp(str, "0") || !my_strcasecmp(str, "off") || !my_strcasecmp(str, "no")) s = 0; else s = -1; if(s == -1 || b == -1) retval = strcmp(str, boolean); else retval = (s != b); dbg(1, "strboolcmp(): str=%s boolean=%s retval=%d\n", str, boolean, retval); return retval; } /* return lenght of line and skip */ size_t my_fgets_skip(FILE *fd) { enum { SIZE = 1024 }; char buf[SIZE]; size_t line_len = 0, len; while(fgets(buf, SIZE, fd)) { len = strlen(buf); line_len += len; if(buf[len - 1] == '\n') break; } return line_len; } /* caller should free allocated storage for s */ char *my_fgets(FILE *fd, size_t *line_len) { enum { SIZE = 1024 }; char buf[SIZE]; char *s = NULL; size_t len; if(line_len) *line_len = 0; while(fgets(buf, SIZE, fd)) { my_strcat(_ALLOC_ID_, &s, buf); len = strlen(buf); if(line_len) *line_len += len; if(buf[len - 1] == '\n') break; } return s; } /* split a string into tokens like standard strtok_r, * if quote string is not empty any character matching quote is considered a quoting * character, removed from input and all characters before next quote are considered * as part of the token. backslash can be used to enter literal quoting characters and * literal backslashes. * behavior described above can be changed if keep_quote is not zero: * keep_quote == 1: keep quotes and backslahes * keep_quote == 4: remove surrounding "...", keep everything in between * if quote is empty no backslash is removed from input and behavior is identical * to strtok_r */ char *my_strtok_r(char *str, const char *delim, const char *quote, int keep_quote, char **saveptr) { char *tok; int q = 0; /* quote */ int e = 0; /* escape */ int ne = 0; /* number of escapes / quoting chars to remove */ if(str) { /* 1st call */ *saveptr = str; } while(**saveptr && strchr(delim, **saveptr) ) { /* skip separators */ ++(*saveptr); } tok = *saveptr; /* start of token */ while(**saveptr && (e || q || !strchr(delim, **saveptr)) ) { /* look for sep. marking end of current token */ if(ne) *(*saveptr - ne) = **saveptr; /* shift back eating escapes / quotes */ if(!e && strchr(quote, **saveptr)) { q = !q; if(keep_quote != 1) ++ne; } if(!e && **saveptr == '\\') { /* do not skip backslashes either */ e = 1; if(keep_quote == 0) ++ne; } else e = 0; ++(*saveptr); } if(**saveptr) { **saveptr = '\0'; /* mark end of token */ if(ne) *(*saveptr - ne) = **saveptr; /* shift back eating escapes / quotes */ ++(*saveptr); /* if not at end of string advance one char for next iteration */ } else if(ne) *(*saveptr - ne ) = **saveptr; /* shift back eating escapes / quotes */ if(tok[0]) return tok; /* return token */ else return NULL; /* no more tokens */ } size_t my_strdup(int id, char **dest, const char *src) /* empty source string --> dest=NULL */ { size_t len; if(*dest == src && src!=NULL) dbg(0, "my_strdup(): WARNING: src == *dest == %p, id=%d\n", src, id); if(src!=NULL && src[0]!='\0') { len = strlen(src)+1; my_realloc(id, dest, len); memcpy(*dest, src, len); dbg(3,"my_strdup(%d,): duplicated string %s\n", id, src); return len-1; } else if(*dest) { my_free(_ALLOC_ID_, dest); dbg(3,"my_strdup(%d,): freed destination ptr\n", id); } return 0; } /* 20171004 copy at most n chars, adding a null char at end */ void my_strndup(int id, char **dest, const char *src, size_t n) /* empty source string --> dest=NULL */ { if(*dest!=NULL) { dbg(3," my_strndup: calling my_free\n"); my_free(_ALLOC_ID_, dest); } if(src!=NULL && src[0]!='\0') { /* 20180924 replace strndup() */ char *p = memchr(src, '\0', n); if(p) n = p - src; *dest = my_malloc(id, n+1); if(*dest) { memcpy(*dest, src, n); (*dest)[n] = '\0'; } /* *dest=strndup(src, n); */ dbg(3,"my_strndup(%d,): duplicated string %s\n", id, src); } } /* replace TABs with required number of spaces * User must free returned string */ char *my_expand(const char *s, int tabstop) { char pad[200]; size_t spos = 0; char *t = NULL; const char *sptr = s; if(!s) { return NULL; } my_strcat2(_ALLOC_ID_, &t, ""); while(*sptr) { if(*sptr == '\t') { int i; size_t npad = tabstop - spos % tabstop; for(i = 0; i < npad; i++) pad[i] = ' '; pad[i] = '\0'; spos += npad; } else { pad[0] = *sptr; pad[1] = '\0'; spos++; } my_strcat2(_ALLOC_ID_, &t, pad); if(*sptr == '\n') spos = 0; sptr++; } return t; } void dbg(int level, char *fmt, ...) { if(debug_var>=level) { va_list args; va_start(args, fmt); vfprintf(errfp, fmt, args); va_end(args); } } #ifdef HAS_SNPRINTF size_t my_snprintf(char *str, size_t size, const char *fmt, ...) { int size_of_print; char s[200]; va_list args; va_start(args, fmt); size_of_print = vsnprintf(str, size, fmt, args); if(has_x && size_of_print >=size) { /* output was truncated */ snprintf(s, S(s), "alert_ { Warning: overflow in my_snprintf print size=%d, buffer size=%d} {}", size_of_print, size); tcleval(s); } va_end(args); return size_of_print; } #else /* this is a replacement for snprintf(), **however** it implements only the bare minimum set of formatting used by XSCHEM */ size_t my_snprintf(char *string, size_t size, const char *format, ...) { va_list args; const char *f, *fmt = NULL, *prev; int overflow, format_spec; size_t l, n = 0; va_start(args, format); /* fprintf(errfp, "my_snprintf(): size=%d, format=%s\n", size, format); */ prev = format; format_spec = 0; overflow = 0; for(f = format; *f; f++) { if(*f == '%') { format_spec = 1; fmt = f; } if(*f == 's' && format_spec) { char *sptr; sptr = va_arg(args, char *); l = fmt - prev; if(n+l > size) { overflow = 1; break; } memcpy(string + n, prev, l); string[n+l] = '\0'; n += l; l = strlen(sptr); if(n+l+1 > size) { overflow = 1; break; } memcpy(string + n, sptr, l+1); n += l; format_spec = 0; prev = f + 1; } else if(format_spec && (*f == 'd' || *f == 'x' || *f == 'c' || *f == 'u') ) { char nfmt[50], nstr[50]; int i, nlen; i = va_arg(args, int); l = f - fmt+1; strncpy(nfmt, fmt, l); nfmt[l] = '\0'; l = fmt - prev; if(n+l > size) break; memcpy(string + n, prev, l); string[n+l] = '\0'; n += l; nlen = sprintf(nstr, nfmt, i); if(n + nlen + 1 > size) { overflow = 1; break; } memcpy(string +n, nstr, nlen+1); n += nlen; format_spec = 0; prev = f + 1; } else if(format_spec && (*f == 'p') ) { char nfmt[50], nstr[50]; void *i; int nlen; i = va_arg(args, void *); l = f - fmt+1; strncpy(nfmt, fmt, l); nfmt[l] = '\0'; l = fmt - prev; if(n+l > size) break; memcpy(string + n, prev, l); string[n+l] = '\0'; n += l; nlen = sprintf(nstr, nfmt, i); if(n + nlen + 1 > size) { overflow = 1; break; } memcpy(string +n, nstr, nlen+1); n += nlen; format_spec = 0; prev = f + 1; } else if(format_spec && (*f == 'g' || *f == 'e' || *f == 'f')) { char nfmt[50], nstr[50]; double i; int nlen; i = va_arg(args, double); l = f - fmt+1; strncpy(nfmt, fmt, l); nfmt[l] = '\0'; l = fmt - prev; if(n+l > size) { overflow = 1; break; } memcpy(string + n, prev, l); string[n+l] = '\0'; n += l; nlen = sprintf(nstr, nfmt, i); if(n + nlen + 1 > size) { overflow = 1; break; } memcpy(string +n, nstr, nlen+1); n += nlen; format_spec = 0; prev = f + 1; } } l = f - prev; if(!overflow && n+l+1 <= size) { memcpy(string + n, prev, l+1); n += l; } else { dbg(1, "my_snprintf(): overflow, target size=%d, format=%s\n", size, format); } va_end(args); /* fprintf(errfp, "my_snprintf(): returning: |%s|\n", string); */ return n; } #endif /* HAS_SNPRINTF */ size_t my_strdup2(int id, char **dest, const char *src) /* 20150409 duplicates also empty string */ { size_t len; if(*dest == src && src!=NULL) dbg(0, "my_strdup2(): WARNING: src == *dest == %p, id=%d\n", src, id); if(src!=NULL) { len = strlen(src)+1; my_realloc(id, dest, len); memcpy(*dest, src, len); dbg(3,"my_strdup2(%d,): duplicated string %s\n", id, src); return len-1; } else if(*dest) { my_free(_ALLOC_ID_, dest); dbg(3,"my_strdup2(%d,): freed destination ptr\n", id); } return 0; } /* recognizes spice suffixes: 12p --> 1.2e11, 3MEG --> 3e6 */ double atof_spice(const char *s) { int n; double a = 0.0, mul=1.0; char lower_s[100]; char suffix[100]={0}; const char *p; if(!s) return 0.0; my_strncpy(lower_s, s, S(lower_s)); strtolower(lower_s); n = sscanf(lower_s, "%lf%s", &a, suffix); if(n == 0) { return 0.0; } else if(n == 1) { mul = 1.0; } else { p = strpbrk(suffix, "tgmkunpfa"); if(p != suffix ) mul = 1.0; else if(*p == 't') mul=1e12; else if(*p == 'g') mul=1e9; else if(*p == 'm') { if(strstr(p, "meg") == p) mul=1e6; else if(strstr(p, "mil") == p) mul=25.4e-6; else mul=1e-3; } else if(*p == 'k') mul=1e3; else if(*p == 'u') mul=1e-6; else if(*p == 'n') mul=1e-9; else if(*p == 'p') mul=1e-12; else if(*p == 'f') mul=1e-15; else if(*p == 'a') mul=1e-18; else mul = 1.0; a *= mul; } return a; } /* same as atof_spice, but recognizes 'M' ae Mega, and 'm' as Milli */ double atof_eng(const char *s) { int n; double a = 0.0, mul=1.0; char suffix[100]={0}; const char *p; if(!s) return 0.0; n = sscanf(s, "%lf%s", &a, suffix); if(n == 0) { return 0.0; } else if(n == 1) { mul = 1.0; } else { p = strpbrk(suffix, "TGMKUNPFAtgmkunpfa"); if(p != suffix ) mul = 1.0; else if(tolower(*p) == 't') mul=1e12; else if(tolower(*p) == 'g') mul=1e9; else if(*p == 'M') mul=1e6; else if(*p == 'm') mul=1e-3; else if(tolower(*p) == 'k') mul=1e3; else if(tolower(*p) == 'u') mul=1e-6; else if(tolower(*p) == 'n') mul=1e-9; else if(tolower(*p) == 'p') mul=1e-12; else if(tolower(*p) == 'f') mul=1e-15; else if(tolower(*p) == 'a') mul=1e-18; else mul = 1.0; a *= mul; } return a; } char *my_itoa(int i) { static char s[30]; size_t n; n = my_snprintf(s, S(s), "%d", i); if(xctx) xctx->tok_size = n; return s; } char *dtoa(double i) { static char s[70]; size_t n; n = my_snprintf(s, S(s), "%.8g", i); if(xctx) xctx->tok_size = n; return s; } char *dtoa_eng(double i) { static char s[70]; size_t n; int suffix = 0; double absi = fabs(i); if (absi == 0.0) { suffix = 0;} else if(absi < 1e-23) { i = 0; suffix = 0;} else if(absi >=1e12) { i /= 1e12; suffix = 'T';} else if(absi >=1e9) { i /= 1e9 ; suffix = 'G';} else if(absi >=1e6) { i /= 1e6 ; suffix = 'M';} else if(absi >=1e3) { i /= 1e3 ; suffix = 'k';} else if(absi >=0.1) { suffix = 0;} else if(absi >=1e-3) { i *= 1e3 ; suffix = 'm';} else if(absi >=1e-6) { i *= 1e6 ; suffix = 'u';} else if(absi >=1e-9) { i *= 1e9 ; suffix = 'n';} else if(absi >=1e-12) { i *= 1e12; suffix = 'p';} else if(absi >=1e-15) { i *= 1e15; suffix = 'f';} else { i *= 1e18; suffix = 'a';} if(suffix) { n = my_snprintf(s, S(s), "%.5g%c", i, suffix); } else { n = my_snprintf(s, S(s), "%.5g", i); } if(xctx) xctx->tok_size = n; return s; } char *dtoa_prec(double i) { static char s[70]; size_t n; n = my_snprintf(s, S(s), "%.10e", i); if(xctx) xctx->tok_size = n; return s; } size_t my_mstrcat(int id, char **str, const char *add, ...) { va_list args; const char *append_str; size_t s, a; if(add == NULL) return 0; s = 0; if(*str != NULL) s = strlen(*str); va_start(args, add); append_str = add; do { if( *str != NULL) { if(append_str[0]) { a = strlen(append_str) + 1; my_realloc(id, str, s + a ); memcpy(*str + s, append_str, a); s += a - 1; dbg(3,"my_mstrcat(%d,): reallocated string %s\n", id, *str); } } else { if(append_str[0]) { a = strlen(append_str) + 1; *str = my_malloc(id, a); memcpy(*str, append_str, a); s = a - 1; dbg(3,"my_mstrcat(%d,): allocated string %s\n", id, *str); } } append_str = va_arg(args, const char *); } while(append_str); va_end(args); return s; } size_t my_strcat(int id, char **str, const char *append_str) { size_t s, a; dbg(3,"my_strcat(%d,): str=%s append_str=%s\n", id, *str, append_str); if( *str != NULL) { s = strlen(*str); if(append_str == NULL || append_str[0]=='\0') return s; a = strlen(append_str) + 1; my_realloc(id, str, s + a ); memcpy(*str + s, append_str, a); dbg(3,"my_strcat(%d,): reallocated string %s\n", id, *str); return s + a - 1; } else { /* str = NULL */ if(append_str == NULL || append_str[0] == '\0') return 0; a = strlen(append_str) + 1; *str = my_malloc(id, a ); memcpy(*str, append_str, a); dbg(3,"my_strcat(%d,): allocated string %s\n", id, *str); return a - 1; } } /* same as my_strcat, but appending "" to NULL returns "" instead of NULL */ size_t my_strcat2(int id, char **str, const char *append_str) { size_t s, a; dbg(3,"my_strcat(%d,): str=%s append_str=%s\n", id, *str, append_str); if( *str != NULL) { s = strlen(*str); if(append_str == NULL || append_str[0]=='\0') return s; a = strlen(append_str) + 1; my_realloc(id, str, s + a ); memcpy(*str + s, append_str, a); dbg(3,"my_strcat(%d,): reallocated string %s\n", id, *str); return s + a - 1; } else { /* str == NULL */ if(append_str == NULL ) return 0; a = strlen(append_str) + 1; *str = my_malloc(id, a ); memcpy(*str, append_str, a); dbg(3,"my_strcat(%d,): allocated string %s\n", id, *str); return a - 1; } } size_t my_strncat(int id, char **str, size_t n, const char *append_str) { size_t s, a; dbg(3,"my_strncat(%d,): str=%s append_str=%s\n", id, *str, append_str); a = strlen(append_str) + 1; if(a > n + 1) a = n + 1; if( *str != NULL) { s = strlen(*str); if(append_str == NULL || append_str[0] == '\0') return s; my_realloc(id, str, s + a ); memcpy(*str + s, append_str, a); *(*str + s + a - 1) = '\0'; dbg(3,"my_strncat(%d,): reallocated string %s\n", id, *str); return s + a - 1; } else { if(append_str == NULL || append_str[0] == '\0') return 0; *str = my_malloc(id, a ); memcpy(*str, append_str, a); *(*str + a - 1) = '\0'; dbg(3,"my_strncat(%d,): allocated string %s\n", id, *str); return a - 1; } } void *my_calloc(int id, size_t nmemb, size_t size) { void *ptr; if(size*nmemb > 0) { ptr=calloc(nmemb, size); if(ptr == NULL) fprintf(errfp,"my_calloc(%d,): allocation failure %ld * %ld bytes\n", id, nmemb, size); dbg(3, "\nmy_calloc(%d,): allocating %p , %lu bytes\n", id, ptr, (unsigned long) (size*nmemb)); } else ptr = NULL; return ptr; } void *my_malloc(int id, size_t size) { void *ptr; if(size>0) { ptr=malloc(size); if(ptr == NULL) fprintf(errfp,"my_malloc(%d,): allocation failure for %ld bytes\n", id, size); dbg(3, "\nmy_malloc(%d,): allocating %p , %lu bytes\n", id, ptr, (unsigned long) size); } else ptr=NULL; return ptr; } void my_realloc(int id, void *ptr,size_t size) { void *a; char old[100]; void *tmp; a = *(void **)ptr; my_snprintf(old, S(old), "%p", a); if(size == 0) { free(*(void **)ptr); dbg(3, "\nmy_free(%d,): my_realloc_freeing %p\n",id, *(void **)ptr); *(void **)ptr=NULL; } else { tmp = realloc(*(void **)ptr,size); if(tmp == NULL) { fprintf(errfp,"my_realloc(%d,): allocation failure for %ld bytes\n", id, size); } else { *(void **)ptr = tmp; dbg(3, "\nmy_realloc(%d,): reallocating %s --> %p to %lu bytes\n", id, old, *(void **)ptr,(unsigned long) size); } } } char *my_free(int id, void *ptr) { if(*(void **)ptr) { free(*(void **)ptr); dbg(3, "\nmy_free(%d,): freeing %p\n", id, *(void **)ptr); *(void **)ptr=NULL; } else { dbg(3, "\n--> my_free(%d,): trying to free NULL pointer\n", id); } return NULL; } /* n characters at most are copied, *d will be always NUL terminated if *s does * not fit(d[n-1]='\0') * return # of copied characters */ int my_strncpy(char *d, const char *s, size_t n) { int i = 0; n -= 1; dbg(3, "my_strncpy(): copying %s to %lu\n", s, (unsigned long)d); while( (d[i] = s[i]) ) { if(i == n) { if(s[i] != '\0') dbg(1, "my_strncpy(): overflow, n=%d, s=%s\n", n+1, s); d[i] = '\0'; return i; } ++i; } return i; } char *strtolower(char* s) { char *p; if(s) for(p=s; *p; p++) *p=(char)tolower(*p); return s; } char *strtoupper(char* s) { char *p; if(s) for(p=s; *p; p++) *p=(char)toupper(*p); return s; } /* caller should do hash_names() (once) before (repeatedly) using this function */ void set_inst_prop(int i) { char *ptr; char *tmp = NULL; if(xctx->inst[i].ptr == -1) return; ptr = (xctx->inst[i].ptr+ xctx->sym)->templ; dbg(1, "set_inst_prop(): i=%d, name=%s, prop_ptr = %s, template=%s\n", i, xctx->inst[i].name, xctx->inst[i].prop_ptr, ptr); my_strdup(_ALLOC_ID_, &xctx->inst[i].prop_ptr, ptr); if(get_tok_value(ptr, "name",0)[0]) { my_strdup(_ALLOC_ID_, &tmp, xctx->inst[i].prop_ptr); new_prop_string(i, tmp, tclgetboolvar("disable_unique_names")); /* sets also inst[].instname */ my_free(_ALLOC_ID_, &tmp); } } #define DGT(c) ((c) >= '0' && (c) <= '9') #define SPC(c) ((c) == ' ' || (c) == '\t') /* fast convert (decimal) string to float */ float my_atof(const char *p) { static const float p10[]={ 1e-1f, 1e-2f, 1e-3f, 1e-4f, 1e-5f, 1e-6f, 1e-7f, 1e-8f }; int frac; float sign, value, scale; unsigned int exponent = 0; /* skip initial spaces */ while(SPC(*p)) p++; /* sign */ sign = 1.0; if(*p == '-') { sign = -1.0; ++p; } else if(*p == '+') { ++p; } /* Get digits */ for(value = 0.0; DGT(*p); p++) { value = value * 10.0f + (*p - '0'); } /* get fractional part */ if(*p == '.') { int cnt = 0; ++p; while (DGT(*p)) { if(cnt < 8) value += (*p - '0') * p10[cnt++]; ++p; } } /* Exponent */ frac = 0; scale = 1.0; if((*p == 'e') || (*p == 'E')) { /* Exponent sign */ ++p; if(*p == '-') { frac = 1; ++p; } else if(*p == '+') p++; /* Get exponent. */ for(; DGT(*p); p++) { exponent = exponent * 10 + (*p - '0'); } if(exponent > 38) exponent = 38; /* Scale result */ while(exponent >= 12) { scale *= 1E12f; exponent -= 12; } while(exponent >= 4) { scale *= 1E4f; exponent -= 4; } while(exponent > 0) { scale *= 10.0f; exponent -= 1; } return sign * (frac ? (value / scale) : (value * scale)); } return sign * value; } /* fast convert (decimal) string to double */ double my_atod(const char *p) { static const double p10[]={ 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8, 1e-9, 1e-10, 1e-11, 1e-12, 1e-13, 1e-14, 1e-15, 1e-16, 1e-17, 1e-18 }; int frac; double sign, value, scale; unsigned int exponent = 0; /* skip initial spaces */ while(SPC(*p)) p++; /* sign */ sign = 1.0; if(*p == '-') { sign = -1.0; ++p; } else if(*p == '+') { ++p; } /* Get digits */ for(value = 0.0; DGT(*p); p++) { value = value * 10.0 + (*p - '0'); } /* get fractional part */ if(*p == '.') { int cnt = 0; ++p; while (DGT(*p)) { if(cnt < 18) value += (*p - '0') * p10[cnt++]; ++p; } } /* Exponent */ frac = 0; scale = 1.0; if((*p == 'e') || (*p == 'E')) { /* Exponent sign */ ++p; if(*p == '-') { frac = 1; ++p; } else if(*p == '+') p++; /* Get exponent. */ for(; DGT(*p); p++) { exponent = exponent * 10 + (*p - '0'); } if(exponent > 308) exponent = 308; /* Scale result */ while(exponent >= 50) { scale *= 1E50; exponent -= 50; } while(exponent >= 8) { scale *= 1E8; exponent -= 8; } while(exponent > 0) { scale *= 10.0; exponent -= 1; } return sign * (frac ? (value / scale) : (value * scale)); } return sign * value; } /* super fast count # of newlines (and bytes) in a file */ int count_lines_bytes(int fd, size_t *lines, size_t *bytes) { enum {BUFFER_SIZE=16384}; size_t nread; size_t nls, nbytes; char buf[BUFFER_SIZE]; if(!lines || !bytes) return 0; nls = nbytes = 0; while((nread = read(fd, buf, BUFFER_SIZE)) > 0) { char *p; char *last; if(nread == -1) return 0; nbytes += nread; p = buf; last = buf + nread; while((p = memchr(p, '\n', last - p))) { ++p; ++nls; } } *bytes = nbytes; *lines = nls; return 1; } static int edit_rect_property(int x) { int i, c, n; int drw = 0; const char *attr; int preserve, modified = 0; char *oldprop=NULL; if(x < 0 || x > 2) { fprintf(errfp, "edit_rect_property() : unknown parameter x=%d\n",x); return 0; } my_strdup(_ALLOC_ID_, &oldprop, xctx->rect[xctx->sel_array[0].col][xctx->sel_array[0].n].prop_ptr); if(oldprop && oldprop[0]) { tclsetvar("retval",oldprop); } else { tclsetvar("retval",""); } if(x==0) { xctx->semaphore++; tcleval("text_line {Input property:} 0 normal"); xctx->semaphore--; } else if(x==2) tcleval("viewdata $::retval"); else tcleval("edit_vi_prop {Text:}"); /* x == 1 */ preserve = tclgetboolvar("preserve_unchanged_attrs"); if(strcmp(tclgetvar("tctx::rcode"),"") ) { xctx->push_undo(); for(i=0; ilastsel; ++i) { if(xctx->sel_array[i].type != xRECT) continue; c = xctx->sel_array[i].col; n = xctx->sel_array[i].n; if(oldprop && preserve == 1) { set_different_token(&xctx->rect[c][n].prop_ptr, (char *) tclgetvar("retval"), oldprop); } else { my_strdup(_ALLOC_ID_, &xctx->rect[c][n].prop_ptr, (char *) tclgetvar("retval")); } set_rect_flags(&xctx->rect[c][n]); /* set cached .flags bitmask from attributes */ set_rect_extraptr(0, &xctx->rect[c][n]); attr = get_tok_value(xctx->rect[c][n].prop_ptr,"dash",0); if( strcmp(attr, "") ) { int d = atoi(attr); xctx->rect[c][n].dash = (short)(d >= 0? d : 0); } else xctx->rect[c][n].dash = 0; attr = get_tok_value(xctx->rect[c][n].prop_ptr,"ellipse", 0); if( strcmp(attr, "") ) { int a; int b; if(sscanf(attr, "%d%*[ ,]%d", &a, &b) != 2) { a = 0; b = 360; } xctx->rect[c][n].ellipse_a = a; xctx->rect[c][n].ellipse_b = b; } else { xctx->rect[c][n].ellipse_a = -1; xctx->rect[c][n].ellipse_b = -1; } attr = get_tok_value(xctx->rect[c][n].prop_ptr,"fill", 0); if(!strcmp(attr, "full")) xctx->rect[c][n].fill = 3; else if(!strboolcmp(attr, "false")) xctx->rect[c][n].fill = 0; else xctx->rect[c][n].fill = 1; if( (oldprop && xctx->rect[c][n].prop_ptr && strcmp(oldprop, xctx->rect[c][n].prop_ptr)) || (!oldprop && xctx->rect[c][n].prop_ptr) || (oldprop && !xctx->rect[c][n].prop_ptr)) { modified = 1; if(!drw) { bbox(START, 0.0 , 0.0 , 0.0 , 0.0); } drw = 1; if( xctx->rect[c][n].flags & 1024) { draw_image(0, &xctx->rect[c][n], &xctx->rect[c][n].x1, &xctx->rect[c][n].y1, &xctx->rect[c][n].x2, &xctx->rect[c][n].y2, 0, 0); } bbox(ADD, xctx->rect[c][n].x1, xctx->rect[c][n].y1, xctx->rect[c][n].x2, xctx->rect[c][n].y2); } } if(drw) { bbox(SET , 0.0 , 0.0 , 0.0 , 0.0); draw(); bbox(END , 0.0 , 0.0 , 0.0 , 0.0); } } my_free(_ALLOC_ID_, &oldprop); return modified; } static int edit_line_property(void) { int i, c, n; const char *dash; int preserve, modified = 0; char *oldprop=NULL; my_strdup(_ALLOC_ID_, &oldprop, xctx->line[xctx->sel_array[0].col][xctx->sel_array[0].n].prop_ptr); if(oldprop && oldprop[0]) { tclsetvar("retval", oldprop); } else { tclsetvar("retval",""); } xctx->semaphore++; tcleval("text_line {Input property:} 0 normal"); xctx->semaphore--; preserve = tclgetboolvar("preserve_unchanged_attrs"); if(strcmp(tclgetvar("tctx::rcode"),"") ) { double y1, y2; xctx->push_undo(); bbox(START, 0.0 , 0.0 , 0.0 , 0.0); for(i=0; ilastsel; ++i) { if(xctx->sel_array[i].type != LINE) continue; c = xctx->sel_array[i].col; n = xctx->sel_array[i].n; if(oldprop && preserve == 1) { set_different_token(&xctx->line[c][n].prop_ptr, (char *) tclgetvar("retval"), oldprop); } else { my_strdup(_ALLOC_ID_, &xctx->line[c][n].prop_ptr, (char *) tclgetvar("retval")); } xctx->line[c][n].bus = !strboolcmp(get_tok_value(xctx->line[c][n].prop_ptr,"bus",0), "true"); dash = get_tok_value(xctx->line[c][n].prop_ptr,"dash",0); if( strcmp(dash, "") ) { int d = atoi(dash); xctx->line[c][n].dash = (short)(d >= 0? d : 0); } else xctx->line[c][n].dash = 0; if(xctx->line[c][n].y1 < xctx->line[c][n].y2) { y1 = xctx->line[c][n].y1-INT_BUS_WIDTH(xctx->lw); y2 = xctx->line[c][n].y2+INT_BUS_WIDTH(xctx->lw); } else { y1 = xctx->line[c][n].y1+INT_BUS_WIDTH(xctx->lw); y2 = xctx->line[c][n].y2-INT_BUS_WIDTH(xctx->lw); } bbox(ADD, xctx->line[c][n].x1-INT_BUS_WIDTH(xctx->lw), y1, xctx->line[c][n].x2+INT_BUS_WIDTH(xctx->lw), y2); } bbox(SET , 0.0 , 0.0 , 0.0 , 0.0); draw(); bbox(END , 0.0 , 0.0 , 0.0 , 0.0); modified = 1; } my_free(_ALLOC_ID_, &oldprop); return modified; } static int edit_wire_property(void) { int i, modified = 0; int preserve; char *oldprop=NULL; const char *bus_ptr; my_strdup(_ALLOC_ID_, &oldprop, xctx->wire[xctx->sel_array[0].n].prop_ptr); if(oldprop && oldprop[0]) { tclsetvar("retval", oldprop); } else { tclsetvar("retval",""); } xctx->semaphore++; tcleval("text_line {Input property:} 0 normal"); xctx->semaphore--; preserve = tclgetboolvar("preserve_unchanged_attrs"); if(strcmp(tclgetvar("tctx::rcode"),"") ) { xctx->push_undo(); bbox(START, 0.0 , 0.0 , 0.0 , 0.0); for(i=0; ilastsel; ++i) { int oldbus=0; int k = xctx->sel_array[i].n; if(xctx->sel_array[i].type != WIRE) continue; /* does not seem to be necessary */ /* xctx->prep_hash_wires=0; * xctx->prep_net_structs=0; * xctx->prep_hi_structs=0; */ oldbus = xctx->wire[k].bus; if(oldprop && preserve == 1) { set_different_token(&xctx->wire[k].prop_ptr, (char *) tclgetvar("retval"), oldprop); } else { my_strdup(_ALLOC_ID_, &xctx->wire[k].prop_ptr,(char *) tclgetvar("retval")); } bus_ptr = get_tok_value(xctx->wire[k].prop_ptr,"bus",0); if(!strboolcmp(bus_ptr, "true")) { double ov, y1, y2; ov = INT_BUS_WIDTH(xctx->lw) > xctx->cadhalfdotsize ? INT_BUS_WIDTH(xctx->lw) : CADHALFDOTSIZE; if(xctx->wire[k].y1 < xctx->wire[k].y2) { y1 = xctx->wire[k].y1-ov; y2 = xctx->wire[k].y2+ov; } else { y1 = xctx->wire[k].y1+ov; y2 = xctx->wire[k].y2-ov; } bbox(ADD, xctx->wire[k].x1-ov, y1 , xctx->wire[k].x2+ov , y2 ); xctx->wire[k].bus=1; } else { if(oldbus){ double ov, y1, y2; ov = INT_BUS_WIDTH(xctx->lw)> xctx->cadhalfdotsize ? INT_BUS_WIDTH(xctx->lw) : CADHALFDOTSIZE; if(xctx->wire[k].y1 < xctx->wire[k].y2) { y1 = xctx->wire[k].y1-ov; y2 = xctx->wire[k].y2+ov; } else { y1 = xctx->wire[k].y1+ov; y2 = xctx->wire[k].y2-ov; } bbox(ADD, xctx->wire[k].x1-ov, y1 , xctx->wire[k].x2+ov , y2 ); xctx->wire[k].bus=0; } } } bbox(SET , 0.0 , 0.0 , 0.0 , 0.0); draw(); bbox(END , 0.0 , 0.0 , 0.0 , 0.0); modified = 1; } my_free(_ALLOC_ID_, &oldprop); return modified; } static int edit_arc_property(void) { int old_fill; double x1, y1, x2, y2; int c, i, ii, old_dash, drw = 0; char *oldprop = NULL; const char *dash, *fill_ptr; int preserve, modified = 0; my_strdup(_ALLOC_ID_, &oldprop, xctx->arc[xctx->sel_array[0].col][xctx->sel_array[0].n].prop_ptr); if(oldprop && oldprop[0]) { tclsetvar("retval", oldprop); } else { tclsetvar("retval",""); } xctx->semaphore++; tcleval("text_line {Input property:} 0 normal"); xctx->semaphore--; preserve = tclgetboolvar("preserve_unchanged_attrs"); if(strcmp(tclgetvar("tctx::rcode"),"") ) { xctx->push_undo(); for(ii=0; iilastsel; ii++) { if(xctx->sel_array[ii].type != ARC) continue; i = xctx->sel_array[ii].n; c = xctx->sel_array[ii].col; if(oldprop && preserve == 1) { set_different_token(&xctx->arc[c][i].prop_ptr, (char *) tclgetvar("retval"), oldprop); } else { my_strdup(_ALLOC_ID_, &xctx->arc[c][i].prop_ptr, (char *) tclgetvar("retval")); } old_fill = xctx->arc[c][i].fill; fill_ptr = get_tok_value(xctx->arc[c][i].prop_ptr,"fill",0); if( !strcmp(fill_ptr,"full") ) xctx->arc[c][i].fill =3; /* bit 1: solid fill (not stippled) */ else if( !strboolcmp(fill_ptr,"true") ) xctx->arc[c][i].fill =1; else xctx->arc[c][i].fill =0; old_dash = xctx->arc[c][i].dash; dash = get_tok_value(xctx->arc[c][i].prop_ptr,"dash",0); if( strcmp(dash, "") ) { int d = atoi(dash); xctx->arc[c][i].dash = (short)(d >= 0 ? d : 0); } else xctx->arc[c][i].dash = 0; if(old_fill != xctx->arc[c][i].fill || old_dash != xctx->arc[c][i].dash) { if(!drw) { bbox(START,0.0,0.0,0.0,0.0); drw = 1; } arc_bbox(xctx->arc[c][i].x, xctx->arc[c][i].y, xctx->arc[c][i].r, 0, 360, &x1,&y1,&x2,&y2); bbox(ADD, x1, y1, x2, y2); } } if(drw) { bbox(SET , 0.0 , 0.0 , 0.0 , 0.0); draw(); bbox(END , 0.0 , 0.0 , 0.0 , 0.0); } modified = 1; } return modified; } static int edit_polygon_property(void) { const char *fill_ptr; int old_fill; int oldbezier, bezier; int k; double x1=0., y1=0., x2=0., y2=0.; int c, i, ii, old_dash; int drw = 0; char *oldprop = NULL; const char *dash; int preserve, modified = 0; dbg(1, "edit_property(): input property:\n"); my_strdup(_ALLOC_ID_, &oldprop, xctx->poly[xctx->sel_array[0].col][xctx->sel_array[0].n].prop_ptr); if(oldprop && oldprop[0]) { tclsetvar("retval", oldprop); } else { tclsetvar("retval",""); } xctx->semaphore++; tcleval("text_line {Input property:} 0 normal"); xctx->semaphore--; preserve = tclgetboolvar("preserve_unchanged_attrs"); if(strcmp(tclgetvar("tctx::rcode"),"") ) { xctx->push_undo(); for(ii=0; iilastsel; ii++) { if(xctx->sel_array[ii].type != POLYGON) continue; i = xctx->sel_array[ii].n; c = xctx->sel_array[ii].col; oldbezier = !strboolcmp(get_tok_value(xctx->poly[c][i].prop_ptr,"bezier",0),"true") ; if(oldprop && preserve == 1) { set_different_token(&xctx->poly[c][i].prop_ptr, (char *) tclgetvar("retval"), oldprop); } else { my_strdup(_ALLOC_ID_, &xctx->poly[c][i].prop_ptr, (char *) tclgetvar("retval")); } old_fill = xctx->poly[c][i].fill; old_dash = xctx->poly[c][i].dash; bezier = !strboolcmp(get_tok_value(xctx->poly[c][i].prop_ptr,"bezier",0),"true") ; fill_ptr = get_tok_value(xctx->poly[c][i].prop_ptr,"fill",0); if( !strcmp(fill_ptr,"full") ) xctx->poly[c][i].fill =3; /* bit 1: solid fill (not stippled) */ else if( !strboolcmp(fill_ptr,"true") ) xctx->poly[c][i].fill =1; else xctx->poly[c][i].fill =0; dash = get_tok_value(xctx->poly[c][i].prop_ptr,"dash",0); if( strcmp(dash, "") ) { int d = atoi(dash); xctx->poly[c][i].dash = (short)(d >= 0 ? d : 0); } else xctx->poly[c][i].dash = 0; if(old_fill != xctx->poly[c][i].fill || old_dash != xctx->poly[c][i].dash || oldbezier != bezier) { if(!drw) { bbox(START,0.0,0.0,0.0,0.0); drw = 1; } for(k=0; kpoly[c][i].points; ++k) { if(k==0 || xctx->poly[c][i].x[k] < x1) x1 = xctx->poly[c][i].x[k]; if(k==0 || xctx->poly[c][i].y[k] < y1) y1 = xctx->poly[c][i].y[k]; if(k==0 || xctx->poly[c][i].x[k] > x2) x2 = xctx->poly[c][i].x[k]; if(k==0 || xctx->poly[c][i].y[k] > y2) y2 = xctx->poly[c][i].y[k]; } bbox(ADD, x1-xctx->cadhalfdotsize, y1-xctx->cadhalfdotsize, x2+xctx->cadhalfdotsize, y2+xctx->cadhalfdotsize); } } if(drw) { bbox(SET , 0.0 , 0.0 , 0.0 , 0.0); draw(); bbox(END , 0.0 , 0.0 , 0.0 , 0.0); } modified = 1; } return modified; } /* x=0 use text widget x=1 use vim editor */ static int edit_text_property(int x) { int rot, flip, modified = 0; #if HAS_CAIRO==1 int customfont; #endif int sel, k, text_changed = 0, props_changed = 0, size_changed = 0, tmp; int c,l, preserve; double hsize = 0.4, vsize = 0.4, dtmp; double xx1,yy1,xx2,yy2; double pcx,pcy; /* pin center 20070317 */ char property[100];/* used for float 2 string conv (xscale and yscale) overflow safe */ /* const char *str; */ char *oldprop = NULL; if(x < 0 || x > 2) { fprintf(errfp, "edit_text_property() : unknown parameter x=%d\n",x); return 0; } dbg(1, "edit_text_property(): entering\n"); sel = xctx->sel_array[0].n; my_strdup(_ALLOC_ID_, &oldprop, xctx->text[sel].prop_ptr); if(oldprop && oldprop[0]) tclsetvar("props", oldprop); else tclsetvar("props",""); tclsetvar("retval",xctx->text[sel].txt_ptr); my_snprintf(property, S(property), "%.16g",xctx->text[sel].yscale); tclsetvar("tctx::vsize",property); my_snprintf(property, S(property), "%.16g",xctx->text[sel].xscale); tclsetvar("tctx::hsize",property); if(x==0) { const char *props; xctx->semaphore++; tcleval("enter_text {text:} normal"); xctx->semaphore--; hsize =atof(tclgetvar("tctx::hsize")); vsize =atof(tclgetvar("tctx::vsize")); props = tclgetvar("props"); if(xctx->text[sel].xscale != hsize || xctx->text[sel].yscale != vsize) { size_changed = 1; } if( (oldprop && strcmp(oldprop, tclgetvar("props"))) || (!oldprop && props[0]) ) props_changed = 1; } else if(x==2) tcleval("viewdata $::retval"); else tcleval("edit_vi_prop {Text:}"); /* x == 1 */ preserve = tclgetboolvar("preserve_unchanged_attrs"); if(x == 0 || x == 1) { if(strcmp(xctx->text[sel].txt_ptr, tclgetvar("retval") ) ) { dbg(1, "edit_text_property(): x=%d, text_changed=1\n", x); text_changed=1; } } if(strcmp(tclgetvar("tctx::rcode"),"") ) { char *estr = NULL; dbg(1, "edit_text_property(): tctx::rcode !=\"\"\n"); if(text_changed || size_changed || props_changed) { modified = 1; xctx->push_undo(); } /* set_modify(-2); */ /* ? Not needed, overkill... clear text floater caches */ for(k=0;klastsel; ++k) { if(xctx->sel_array[k].type!=xTEXT) continue; sel=xctx->sel_array[k].n; rot = xctx->text[sel].rot; /* calculate bbox, some cleanup needed here */ flip = xctx->text[sel].flip; #if HAS_CAIRO==1 customfont = set_text_custom_font(&xctx->text[sel]); #endif estr = my_expand(get_text_floater(sel), tclgetintvar("tabstop")); text_bbox(estr, xctx->text[sel].xscale, xctx->text[sel].yscale, (short)rot, (short)flip, xctx->text[sel].hcenter, xctx->text[sel].vcenter, xctx->text[sel].x0, xctx->text[sel].y0, &xx1,&yy1,&xx2,&yy2, &tmp, &dtmp); my_free(_ALLOC_ID_, &estr); #if HAS_CAIRO==1 if(customfont) { cairo_restore(xctx->cairo_ctx); } #endif /* dbg(1, "edit_property(): text props=%s text=%s\n", tclgetvar("props"), tclgetvar("retval")); */ if(text_changed) { double cg; my_free(_ALLOC_ID_, &xctx->text[sel].floater_ptr); cg = tclgetdoublevar("cadgrid"); c = xctx->rects[PINLAYER]; for(l=0;ltext[sel].txt_ptr && !strcmp( (get_tok_value(xctx->rect[PINLAYER][l].prop_ptr, "name",0)), xctx->text[sel].txt_ptr) ) { pcx = (xctx->rect[PINLAYER][l].x1+xctx->rect[PINLAYER][l].x2)/2.0; pcy = (xctx->rect[PINLAYER][l].y1+xctx->rect[PINLAYER][l].y2)/2.0; if( /* 20171206 20171221 */ (fabs( (yy1+yy2)/2 - pcy) < cg/2 && (fabs(xx1 - pcx) < cg*3 || fabs(xx2 - pcx) < cg*3) ) || (fabs( (xx1+xx2)/2 - pcx) < cg/2 && (fabs(yy1 - pcy) < cg*3 || fabs(yy2 - pcy) < cg*3) ) ) { if(x==0) my_strdup(_ALLOC_ID_, &xctx->rect[PINLAYER][l].prop_ptr, subst_token(xctx->rect[PINLAYER][l].prop_ptr, "name", (char *) tclgetvar("retval")) ); else my_strdup(_ALLOC_ID_, &xctx->rect[PINLAYER][l].prop_ptr, subst_token(xctx->rect[PINLAYER][l].prop_ptr, "name", (char *) tclgetvar("retval")) ); } } } my_strdup2(_ALLOC_ID_, &xctx->text[sel].txt_ptr, (char *) tclgetvar("retval")); } if(props_changed) { if(oldprop && preserve) set_different_token(&xctx->text[sel].prop_ptr, (char *) tclgetvar("props"), oldprop); else my_strdup(_ALLOC_ID_, &xctx->text[sel].prop_ptr,(char *) tclgetvar("props")); my_free(_ALLOC_ID_, &xctx->text[sel].floater_ptr); set_text_flags(&xctx->text[sel]); } if(text_changed || props_changed) { get_text_floater(sel); /* update xctx->text[sel].floater_ptr cache */ } if(size_changed) { xctx->text[sel].xscale=hsize; xctx->text[sel].yscale=vsize; } } /* for(k=0;klastsel; ++k) */ draw(); } my_free(_ALLOC_ID_, &oldprop); return modified; } int drc_check(int i) { int j, ret = 0; char *drc = NULL, *res = NULL; char *check_result = NULL; int start = 0; int end = xctx->instances; if(!tcleval("info procs fet_drc")[0]) { return ret; } if(i >= 0 && i < xctx->instances) { start = i; end = i + 1; } for(j = start; j < end; j++) { my_strdup(_ALLOC_ID_, &drc, get_tok_value(xctx->sym[xctx->inst[j].ptr].prop_ptr, "drc", 2)); if(drc) { my_strdup(_ALLOC_ID_, &res, translate3(drc, 1, xctx->inst[j].prop_ptr, xctx->sym[xctx->inst[j].ptr].templ, NULL)); dbg(1, "drc_check(): res = |%s|, drc=|%s|\n", res, drc); if(res) { const char *result; const char *replace_res; replace_res = str_replace(res, "@symname", xctx->sym[xctx->inst[j].ptr].name, '\\'); result = tcleval(replace_res); if(result && result[0]) { ret = 1; my_mstrcat(_ALLOC_ID_, &check_result, result, NULL); } } } } if(drc) my_free(_ALLOC_ID_, &drc); if(res) my_free(_ALLOC_ID_, &res); if(check_result) { if(has_x) { /* tclvareval("alert_ {", check_result, "} {}", NULL); */ statusmsg(check_result, 3); tcleval("show_infotext 1"); } else { dbg(0, "%s\n", check_result); } my_free(_ALLOC_ID_, &check_result); } return ret; } /* x=0 use text widget x=1 use vim editor */ static int update_symbol(const char *result, int x, int selected_inst) { int k, sym_number; int no_change_props=0; int only_different=0; int copy_cell=0; int prefix=0, old_prefix = 0; char *name = NULL, *ptr = NULL, *new_prop = NULL; char symbol[PATH_MAX], *translated_sym = NULL, *old_translated_sym = NULL; int changed_symbol = 0; int pushed=0; int *ii = &xctx->edit_sym_i; /* static var */ int *netl_com = &xctx->netlist_commands; /* static var */ int modified = 0; dbg(1, "update_symbol(): entering, selected_inst = %d\n", selected_inst); *ii = selected_inst; if(!result) { dbg(1, "update_symbol(): edit symbol prop aborted\n"); my_free(_ALLOC_ID_, &xctx->old_prop); return 0; } /* create new_prop updated attribute string */ if(*netl_com && x==1) { my_strdup(_ALLOC_ID_, &new_prop, subst_token(xctx->old_prop, "value", (char *) tclgetvar("retval") ) ); dbg(1, "update_symbol(): new_prop=%s\n", new_prop); dbg(1, "update_symbol(): tcl retval==%s\n", tclgetvar("retval")); } else { my_strdup(_ALLOC_ID_, &new_prop, (char *) tclgetvar("retval")); dbg(1, "update_symbol(): new_prop=%s\n", new_prop); } my_strncpy(symbol, (char *) tclgetvar("symbol") , S(symbol)); dbg(1, "update_symbol(): symbol=%s\n", symbol); no_change_props=tclgetboolvar("no_change_attrs"); only_different=tclgetboolvar("preserve_unchanged_attrs"); copy_cell=tclgetboolvar("user_wants_copy_cell"); /* 20191227 necessary? --> Yes since a symbol copy has already been done in edit_symbol_property() -> tcl edit_prop, this ensures new symbol is loaded from disk. if for some reason a symbol with matching name is loaded in xschem this may be out of sync wrt disk version */ if(copy_cell) { remove_symbols(); link_symbols_to_instances(-1); } /* User edited the Symbol textbox */ if(strcmp(symbol, xctx->inst[*ii].name)) { changed_symbol = 1; } for(k=0;klastsel; ++k) { dbg(1, "update_symbol(): for k loop: k=%d\n", k); if(xctx->sel_array[k].type != ELEMENT) continue; *ii=xctx->sel_array[k].n; old_prefix=(get_tok_value((xctx->sym + xctx->inst[*ii].ptr)->templ, "name",0))[0]; /* 20171220 calculate bbox before changes to correctly redraw areas */ /* must be recalculated as cairo text extents vary with zoom factor. */ symbol_bbox(*ii, &xctx->inst[*ii].x1, &xctx->inst[*ii].y1, &xctx->inst[*ii].x2, &xctx->inst[*ii].y2); my_strdup2(_ALLOC_ID_, &old_translated_sym, translate(*ii, xctx->inst[*ii].name)); /* update property string from tcl dialog */ if(!no_change_props) { if(only_different) { char * ss=NULL; my_strdup(_ALLOC_ID_, &ss, xctx->inst[*ii].prop_ptr); if( set_different_token(&ss, new_prop, xctx->old_prop) ) { if(!pushed) { xctx->push_undo(); pushed=1;} my_strdup(_ALLOC_ID_, &xctx->inst[*ii].prop_ptr, ss); } my_free(_ALLOC_ID_, &ss); } else { if(new_prop) { if(!xctx->inst[*ii].prop_ptr || strcmp(xctx->inst[*ii].prop_ptr, new_prop)) { dbg(1, "update_symbol(): changing prop: |%s| -> |%s|\n", xctx->inst[*ii].prop_ptr, new_prop); if(!pushed) { xctx->push_undo(); pushed=1;} dbg(1, "update_symbol(): *ii=%d, new_prop=%s\n", *ii, new_prop ? new_prop : "NULL"); my_strdup(_ALLOC_ID_, &xctx->inst[*ii].prop_ptr, new_prop); } } else { if(!pushed) { xctx->push_undo(); pushed=1;} my_strdup(_ALLOC_ID_, &xctx->inst[*ii].prop_ptr, ""); } } } /* symbol reference changed? --> sym_number >=0, set prefix to 1st char * to use for inst name (from symbol template) */ prefix = 0; sym_number = -1; my_strdup2(_ALLOC_ID_, &translated_sym, translate(*ii, symbol)); dbg(1, "update_symbol: %s -- %s\n", translated_sym, old_translated_sym); if(changed_symbol || ( !strcmp(symbol, xctx->inst[*ii].name) && strcmp(translated_sym, old_translated_sym) ) ) { sym_number=match_symbol(translated_sym); /* check if exist */ if(sym_number>=0) { prefix=(get_tok_value((xctx->sym+sym_number)->templ, "name",0))[0]; /* get new symbol prefix */ } } if(sym_number>=0) /* changing symbol ! */ { if(!pushed) { xctx->push_undo(); pushed=1;} delete_inst_node(*ii); /* 20180208 fix crashing bug: delete node info if changing symbol */ /* if number of pins is different we must delete these data *before* */ /* changing ysmbol, otherwise *ii might end up deleting non allocated data. */ my_strdup2(_ALLOC_ID_, &xctx->inst[*ii].name, rel_sym_path(symbol)); xctx->inst[*ii].ptr=sym_number; /* update instance to point to new symbol */ } my_free(_ALLOC_ID_, &translated_sym); my_free(_ALLOC_ID_, &old_translated_sym); /* if symbol changed ensure instance name (with new prefix char) is unique */ /* preserve backslashes in name ---------0---------------------------------->. */ my_strdup(_ALLOC_ID_, &name, get_tok_value(xctx->inst[*ii].prop_ptr, "name", 1)); if(name && name[0] ) { dbg(1, "update_symbol(): prefix!='\\0', name=%s\n", name); /* change prefix if changing symbol type; */ if(prefix && old_prefix && old_prefix != prefix) { name[0]=(char)prefix; my_strdup(_ALLOC_ID_, &ptr, subst_token(xctx->inst[*ii].prop_ptr, "name", name) ); } else { my_strdup(_ALLOC_ID_, &ptr, xctx->inst[*ii].prop_ptr); } /* set unique name of current inst */ if(!pushed) { xctx->push_undo(); pushed=1;} if(!k) hash_names(-1, XINSERT); hash_names(*ii, XDELETE); dbg(1, "update_symbol(): delete %s\n", xctx->inst[*ii].instname); new_prop_string(*ii, ptr, /* sets also inst[].instname */ tclgetboolvar("disable_unique_names")); /* set new prop_ptr */ hash_names(*ii, XINSERT); dbg(1, "update_symbol(): insert %s\n", xctx->inst[*ii].instname); } set_inst_flags(&xctx->inst[*ii]); } /* end for(k=0;klastsel; ++k) */ if(pushed) modified = 1; /* new symbol bbox after prop changes (may change due to text length) */ if(modified) { xctx->prep_hash_inst=0; xctx->prep_net_structs=0; xctx->prep_hi_structs=0; symbol_bbox(*ii, &xctx->inst[*ii].x1, &xctx->inst[*ii].y1, &xctx->inst[*ii].x2, &xctx->inst[*ii].y2); if(xctx->hilight_nets) { propagate_hilights(1, 1, XINSERT_NOREPLACE); } /* DRC check */ drc_check(*ii); } /* redraw symbol with new props */ set_modify(-2); /* reset floaters caches */ draw(); my_free(_ALLOC_ID_, &name); my_free(_ALLOC_ID_, &ptr); my_free(_ALLOC_ID_, &new_prop); my_free(_ALLOC_ID_, &xctx->old_prop); return modified; } /* x=0 use text widget x=1 use vim editor */ static int edit_symbol_property(int x, int first_sel) { char *result=NULL; int *ii = &xctx->edit_sym_i; /* static var */ int *netl_com = &xctx->netlist_commands; /* static var */ int modified = 0; *ii=xctx->sel_array[first_sel].n; *netl_com = 0; if ((xctx->inst[*ii].ptr + xctx->sym)->type!=NULL) *netl_com = !strcmp( (xctx->inst[*ii].ptr+ xctx->sym)->type, "netlist_commands"); if(xctx->inst[*ii].prop_ptr!=NULL) { if(*netl_com && x==1) { tclsetvar("retval",get_tok_value( xctx->inst[*ii].prop_ptr,"value",2)); } else { tclsetvar("retval",xctx->inst[*ii].prop_ptr); } } else { tclsetvar("retval",""); } my_strdup(_ALLOC_ID_, &xctx->old_prop, xctx->inst[*ii].prop_ptr); tclsetvar("symbol",xctx->inst[*ii].name); if(x==0) { tcleval("edit_prop {Input property:}"); my_strdup(_ALLOC_ID_, &result, tclresult()); } else { /* edit_vi_netlist_prop will replace \" with " before editing, replace back " with \" when done and wrap the resulting text with quotes ("text") when done */ if(*netl_com && x==1) tcleval("edit_vi_netlist_prop {Input property:}"); else if(x==1) tcleval("edit_vi_prop {Input property:}"); else if(x==2) tcleval("viewdata $::retval"); my_strdup(_ALLOC_ID_, &result, tclresult()); } dbg(1, "edit_symbol_property(): before update_symbol, modified=%d\n", xctx->modified); modified = update_symbol(result, x, *ii); my_free(_ALLOC_ID_, &result); dbg(1, "edit_symbol_property(): done update_symbol, modified=%d\n", modified); *ii=-1; return modified; } void change_elem_order(int n) { xInstance tmpinst; xRect tmpbox; xWire tmpwire; xText tmptext; char tmp_txt[50]; /* overflow safe */ int c, new_n, modified = 0; rebuild_selected_array(); if(xctx->lastsel==1) { if(n < 0) { my_snprintf(tmp_txt, S(tmp_txt), "%d",xctx->sel_array[0].n); tclsetvar("retval",tmp_txt); xctx->semaphore++; tcleval("text_line {Object Sequence number} 0"); xctx->semaphore--; if(strcmp(tclgetvar("tctx::rcode"),"") ) { xctx->push_undo(); modified = 1; xctx->prep_hash_inst=0; xctx->prep_net_structs=0; xctx->prep_hi_structs=0; } sscanf(tclgetvar("retval"), "%d",&new_n); if(new_n < 0) new_n = 0; } else { new_n = n; xctx->push_undo(); modified = 1; xctx->prep_hash_inst=0; xctx->prep_net_structs=0; xctx->prep_hi_structs=0; } if(xctx->sel_array[0].type==ELEMENT) { if(new_n>=xctx->instances) new_n=xctx->instances-1; tmpinst=xctx->inst[new_n]; xctx->inst[new_n]=xctx->inst[xctx->sel_array[0].n]; xctx->inst[xctx->sel_array[0].n]=tmpinst; dbg(1, "change_elem_order(): selected element %d\n", xctx->sel_array[0].n); } else if(xctx->sel_array[0].type==xRECT) { c=xctx->sel_array[0].col; if(new_n>=xctx->rects[c]) new_n=xctx->rects[c]-1; tmpbox=xctx->rect[c][new_n]; xctx->rect[c][new_n]=xctx->rect[c][xctx->sel_array[0].n]; xctx->rect[c][xctx->sel_array[0].n]=tmpbox; dbg(1, "change_elem_order(): selected rect %d\n", xctx->sel_array[0].n); if(c == GRIDLAYER) { if(xctx->graph_lastsel == new_n) xctx->graph_lastsel = xctx->sel_array[0].n; else if(xctx->graph_lastsel == xctx->sel_array[0].n) xctx->graph_lastsel = new_n; } } else if(xctx->sel_array[0].type==WIRE) { if(new_n>=xctx->wires) new_n=xctx->wires-1; tmpwire=xctx->wire[new_n]; xctx->wire[new_n]=xctx->wire[xctx->sel_array[0].n]; xctx->wire[xctx->sel_array[0].n]=tmpwire; dbg(1, "change_elem_order(): selected wire %d\n", xctx->sel_array[0].n); } else if(xctx->sel_array[0].type==xTEXT) { if(new_n>=xctx->texts) new_n=xctx->texts-1; tmptext=xctx->text[new_n]; xctx->text[new_n]=xctx->text[xctx->sel_array[0].n]; xctx->text[xctx->sel_array[0].n]=tmptext; dbg(1, "change_elem_order(): selected text %d\n", xctx->sel_array[0].n); } xctx->need_reb_sel_arr = 1; if(modified) set_modify(1); } } /* replace substring 'rep' in 'str' with 'with', if 'rep' not preceeded by an 'escape' char */ char *str_replace(const char *str, const char *rep, const char *with, int escape) { static char *result = NULL; static size_t size=0; size_t result_pos = 0; size_t rep_len; size_t with_len; const char *s = str; int cond; if(s==NULL || rep == NULL || with == NULL || rep[0] == '\0') { my_free(_ALLOC_ID_, &result); size = 0; return NULL; } rep_len = strlen(rep); with_len = strlen(with); dbg(1, "str_replace(): %s, %s, %s\n", s, rep, with); if( size == 0 ) { size = CADCHUNKALLOC; my_realloc(_ALLOC_ID_, &result, size); } while(*s) { STR_ALLOC(&result, result_pos + with_len + 1, &size); cond = ((s == str) || ((*(s - 1) != escape))) && (!strncmp(s, rep, rep_len)); if(cond) { my_strncpy(result + result_pos, with, with_len + 1); result_pos += with_len; s += rep_len; } else { result[result_pos++] = *s++; } } result[result_pos] = '\0'; dbg(1, "str_replace(): returning %s\n", result); return result; } /* caller should free returned string */ char *str_chars_replace(const char *str, const char *replace_set, const char with) { char *res = NULL; char *s; my_strdup(_ALLOC_ID_, &res, str); s = res; dbg(1, "*str_chars_replace(): %s\n", res); while( *s) { if(strchr(replace_set, *s)) { *s = with; } ++s; } return res; } /* x=0 use tcl text widget x=1 use vim editor x=2 only view data */ void edit_property(int x) { int type, j, modified = 0; if(!has_x) return; rebuild_selected_array(); /* from the .sel field in objects build */ if(xctx->lastsel==0 ) /* the array of selected objs */ { char *new_prop = NULL; if(xctx->netlist_type==CAD_SYMBOL_ATTRS) { if(xctx->schsymbolprop!=NULL) tclsetvar("retval",xctx->schsymbolprop); else tclsetvar("retval",""); } else if(xctx->netlist_type==CAD_VHDL_NETLIST) { if(xctx->schvhdlprop!=NULL) tclsetvar("retval",xctx->schvhdlprop); else tclsetvar("retval",""); } else if(xctx->netlist_type==CAD_VERILOG_NETLIST) { if(xctx->schverilogprop!=NULL) tclsetvar("retval",xctx->schverilogprop); else tclsetvar("retval",""); } else if(xctx->netlist_type==CAD_SPICE_NETLIST) { if(xctx->schprop!=NULL) tclsetvar("retval",xctx->schprop); else tclsetvar("retval",""); } else if(xctx->netlist_type==CAD_TEDAX_NETLIST) { if(xctx->schtedaxprop!=NULL) tclsetvar("retval",xctx->schtedaxprop); else tclsetvar("retval",""); } if(x==0) { xctx->semaphore++; tcleval("text_line {Global schematic property:} 0"); xctx->semaphore--; } else if(x==1) { dbg(1, "edit_property(): executing edit_vi_prop\n"); tcleval("edit_vi_prop {Global schematic property:}"); } else if(x==2) tcleval("viewdata $::retval"); dbg(1, "edit_property(): done executing edit_vi_prop, result=%s\n",tclresult()); dbg(1, "edit_property(): tctx::rcode=%s\n",tclgetvar("tctx::rcode") ); my_strdup(_ALLOC_ID_, &new_prop, (char *) tclgetvar("retval")); tclsetvar("retval", new_prop); my_free(_ALLOC_ID_, &new_prop); if(strcmp(tclgetvar("tctx::rcode"),"") ) { if(xctx->netlist_type==CAD_SYMBOL_ATTRS && (!xctx->schsymbolprop || strcmp(xctx->schsymbolprop, tclgetvar("retval") ) ) ) { xctx->push_undo(); modified = 1; my_strdup(_ALLOC_ID_, &xctx->schsymbolprop, (char *) tclgetvar("retval")); } else if(xctx->netlist_type==CAD_VERILOG_NETLIST && (!xctx->schverilogprop || strcmp(xctx->schverilogprop, tclgetvar("retval") ) ) ) { modified = 1; xctx->push_undo(); my_strdup(_ALLOC_ID_, &xctx->schverilogprop, (char *) tclgetvar("retval")); } else if(xctx->netlist_type==CAD_SPICE_NETLIST && (!xctx->schprop || strcmp(xctx->schprop, tclgetvar("retval") ) ) ) { modified = 1; xctx->push_undo(); my_strdup(_ALLOC_ID_, &xctx->schprop, (char *) tclgetvar("retval")); } else if(xctx->netlist_type==CAD_TEDAX_NETLIST && (!xctx->schtedaxprop || strcmp(xctx->schtedaxprop, tclgetvar("retval") ) ) ) { modified = 1; xctx->push_undo(); my_strdup(_ALLOC_ID_, &xctx->schtedaxprop, (char *) tclgetvar("retval")); } else if(xctx->netlist_type==CAD_VHDL_NETLIST && (!xctx->schvhdlprop || strcmp(xctx->schvhdlprop, tclgetvar("retval") ) ) ) { modified = 1; xctx->push_undo(); my_strdup(_ALLOC_ID_, &xctx->schvhdlprop, (char *) tclgetvar("retval")); } } /* update the bounding box of vhdl "architecture" instances that embed */ /* the xctx->schvhdlprop string. 04102001 */ for(j=0;jinstances; ++j) { if( xctx->inst[j].ptr !=-1 && (xctx->inst[j].ptr+ xctx->sym)->type && !strcmp( (xctx->inst[j].ptr+ xctx->sym)->type, "architecture") ) { dbg(1, "edit_property(): updating vhdl architecture\n"); symbol_bbox(j, &xctx->inst[j].x1, &xctx->inst[j].y1, &xctx->inst[j].x2, &xctx->inst[j].y2); } } /* end for(j...) */ if(modified) set_modify(1); return; } /* if((xctx->lastsel==0 ) */ /* set 'preserve unchanged properties if multiple selection */ if(xctx->lastsel > 1) { tclsetvar("preserve_unchanged_attrs", "1"); } else { tclsetvar("preserve_unchanged_attrs", "0"); } j = set_first_sel(0, -2, 0); type = xctx->sel_array[j].type; switch(type) { case ELEMENT: modified |= edit_symbol_property(x, j); while( x == 0 && tclgetvar("edit_symbol_prop_new_sel")[0] == '1') { unselect_all(1); select_object(xctx->mousex, xctx->mousey, SELECTED, 0, NULL); rebuild_selected_array(); type = xctx->sel_array[0].type; for(j=0; j < xctx->lastsel; j++) { if(xctx->sel_array[j].type == ELEMENT) { type = ELEMENT; break; } } if(xctx->lastsel && type == ELEMENT) { modified |= edit_symbol_property(0, j); } else { break; } } tclsetvar("edit_symbol_prop_new_sel", ""); break; case ARC: modified |= edit_arc_property(); break; case xRECT: modified |= edit_rect_property(x); break; case WIRE: modified |= edit_wire_property(); break; case POLYGON: modified |= edit_polygon_property(); break; case LINE: modified |= edit_line_property(); break; case xTEXT: modified |= edit_text_property(x); break; } if(modified) set_modify(1); }