/* * COPYRIGHT * * cschem - modular/flexible schematics editor - libcschem (core library) * Copyright (C) 2024 Tibor 'Igor2' Palinkas * * (Supported by NLnet NGI0 Entrust Fund in 2024) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version.* * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 31 Milk Street, # 960789 Boston, MA 02196 USA * * Contact: * Project page: http://repo.hu/projects/sch-rnd * contact lead developer: http://www.repo.hu/projects/sch-rnd/contact.html * mailing list: http://www.repo.hu/projects/sch-rnd/contact.html */ #include "config.h" #include #include #include #include #include #include #include "concrete.h" #include "cnc_grp.h" #include "project.h" #include "plug_library.h" #include "hierarchy.h" #define HSEP '/' #define MAX_DEPTH 32 csch_sheet_t *(*csch_app_load_sheet_cb)(csch_project_t *proj, const char *path) = NULL; csch_sheet_t *csch_hier_find_by_uuid(const csch_sheet_t *sheet, const char *uuid, int add_to_prj_as_aux) { rnd_message(RND_MSG_ERROR, "cschem/child/uuid not yet implemented"); TODO("hierarchical: implement me\n"); return NULL; } /* sname is sheet's loadname (short name), name:nlen is the requested name and its strlen() */ RND_INLINE int sheet_name_eq(const char *sname, const char *name, long nlen) { long slen = strlen(sname); if (slen < nlen) return 0; /* sheet name too short, can't match */ if (strcmp(sname+slen-nlen, name) == 0) { if (slen == nlen) return 1; if ((slen > nlen) && (sname[slen-nlen-1] == '/')) /* avoid suffix match */ return 1; } /* ignore .rs ending */ if ((slen >= nlen+3) && (strcmp(sname+slen-3, ".rs") == 0)) { if (strncmp(sname+slen-nlen-3, name, nlen) == 0) { if ((slen-3 > nlen) && (sname[slen-nlen-3-1] == '/')) /* avoid suffix match */ return 1; if (slen-3 == nlen) return 1; /* full length match */ } } /* ignore .lht/sch ending for historic reasons */ if ((slen >= nlen+4) && ((strcmp(sname+slen-4, ".lht") == 0) || (strcmp(sname+slen-4, ".sch") == 0))) { if (strncmp(sname+slen-nlen-4, name, nlen) == 0) { if ((slen-4 > nlen) && (sname[slen-nlen-4-1] == '/')) /* avoid suffix match */ return 1; if (slen-4 == nlen) return 1; /* full length match */ } } return 0; } csch_sheet_t *sch_rnd_multi_load(const char *fn, const char *fmt, int *is_project); static csch_sheet_t *hlib_find_by_name(csch_project_t *proj, csch_lib_t *lib, const char *name, long nlen, int add_to_prj_as_aux) { long n; if (lib == NULL) return NULL; switch(lib->type) { case CSCH_SLIB_STATIC: if (sheet_name_eq(lib->name, name, nlen)) { csch_sheet_t *res; rnd_message(RND_MSG_INFO, "Attempting to load '%s' for sheet named '%s'...\n", lib->realpath, name); res = csch_app_load_sheet_cb(proj, lib->realpath); return res; } break; case CSCH_SLIB_PARAMETRIC: case CSCH_SLIB_invalid: return NULL; case CSCH_SLIB_DIR: for(n = 0; n < lib->children.used; n++) { csch_sheet_t *res = hlib_find_by_name(proj, lib->children.array[n], name, nlen, add_to_prj_as_aux); if (res != NULL) return res; } break; } return NULL; } csch_sheet_t *csch_hier_find_by_name(const csch_sheet_t *sheet, const char *name, int add_to_prj_as_aux) { csch_project_t *prj = (csch_project_t *)sheet->hidlib.project; long n, nlen = strlen(name); csch_lib_master_t *master; htsp_entry_t *e; /* search in project first */ for(n = 0; n < prj->hdr.designs.used; n++) { csch_sheet_t *sheet = prj->hdr.designs.array[n]; if (sheet_name_eq(sheet->hidlib.loadname, name, nlen)) return sheet; } rnd_message(RND_MSG_INFO, "Hierarchic: sheet named '%s' not found in the project, searching the hlibrary...\n", name); /* search the hlibrary */ if (csch_app_load_sheet_cb == NULL) return NULL; master = csch_lib_get_master("hlibrary", 0); if (master == NULL) return NULL; for(e = htsp_first(&master->roots); e != NULL; e = htsp_next(&master->roots, e)) { csch_sheet_t *res = hlib_find_by_name((csch_project_t *)sheet->hidlib.project, e->value, name, nlen, add_to_prj_as_aux); if (res != NULL) return res; } return NULL; } csch_sheet_t *csch_hier_find_by_path(const csch_sheet_t *sheet, const char *path, int add_to_prj_as_aux) { csch_project_t *prj = (csch_project_t *)sheet->hidlib.project; csch_sheet_t *res = NULL; char *dirname, *new_path, *nr_path; long n; /* refuse invalid and absolute path */ if ((path == NULL) || (path[0] == '/')) return NULL; dirname = rnd_dirname_real(sheet->hidlib.fullpath); new_path = rnd_concat(dirname, "/", path, NULL); free(dirname); nr_path = rnd_lrealpath(new_path); if (nr_path == NULL) { rnd_message(RND_MSG_ERROR, "Hierarchic: sheet with path '%s' not found on the file system...\n", new_path); free(new_path); goto done; } free(new_path); /* search in project first */ for(n = 0; n < prj->hdr.designs.used; n++) { csch_sheet_t *sh = prj->hdr.designs.array[n]; if (strcmp(sh->hidlib.fullpath, nr_path) == 0) { res = sh; goto done; } } rnd_message(RND_MSG_INFO, "Hierarchic: sheet with path '%s' not found in the project, loading from the file system...\n", path); res = csch_app_load_sheet_cb(prj, nr_path); done:; free(nr_path); return res; } RND_INLINE const char *get_sym_attr(csch_cgrp_t *sym, const char *key) { const csch_attrib_t *aname; if (sym->hdr.type == CSCH_CTYPE_GRP_REF) aname = csch_cgrp_ref_get_attr(sym, key); else aname = csch_attrib_get(&sym->attr, key); if (aname == NULL) return NULL; return aname->val; } int csch_hier_find_sym_child(const csch_sheet_t *sheet, csch_cgrp_t *sym, csch_sheet_t **child_out, int add_to_prj_as_aux) { const char *type, *val; csch_sheet_t *child = NULL; val = get_sym_attr(sym, "cschem/child/uuid"); if (val != NULL) { child = csch_hier_find_by_uuid(sheet, val, add_to_prj_as_aux); type = "uid"; goto yes; } val = get_sym_attr(sym, "cschem/child/name"); if (val != NULL) { child = csch_hier_find_by_name(sheet, val, add_to_prj_as_aux); type = "name"; goto yes; } val = get_sym_attr(sym, "cschem/child/path"); if (val != NULL) { child = csch_hier_find_by_path(sheet, val, add_to_prj_as_aux); type = "path"; goto yes; } /* no (known) cschem/child/ attribute */ return 0; yes:; if (child == NULL) { rnd_message(RND_MSG_ERROR, "Failed to find hierarchic child-sheet referenced by %s '%s'\n", type, val); return -1; } *child_out = child; return 1; } int csch_hier_find_child(const csch_sheet_t *sheet, const char *chuuid, const char *chname, const char *chpath, csch_sheet_t **child_out, int add_to_prj_as_aux) { const char *type, *val; csch_sheet_t *child = NULL; if (chuuid != NULL) { child = csch_hier_find_by_uuid(sheet, chuuid, add_to_prj_as_aux); type = "uid"; val = chuuid; goto yes; } if (chname != NULL) { child = csch_hier_find_by_name(sheet, chname, add_to_prj_as_aux); type = "name"; val = chname; goto yes; } if (chpath != NULL) { child = csch_hier_find_by_path(sheet, chpath, add_to_prj_as_aux); type = "path"; val = chpath; goto yes; } /* no (known) cschem/child/ attribute */ return 0; yes:; if (child == NULL) { rnd_message(RND_MSG_ERROR, "Failed to find hierarchic child-sheet referenced by %s '%s'\n", type, val); return -1; } *child_out = child; return 1; } void csch_hier_path_free(csch_hier_path_t *hpath) { gds_uninit(&hpath->prefix); hpath->hlev = NULL; hpath->hdepth = -1; } void csch_hier_path_init(csch_hier_path_t *hpath, csch_abstract_t *abst) { memset(hpath, 0, sizeof(csch_hier_path_t)); hpath->hlev = abst->hroot; } csch_hlevel_t *csch_hlevel_new(csch_hlevel_t *parent, csch_cgrp_t *sym, const char *name) { csch_hlevel_t *hlev = calloc(sizeof(csch_hlevel_t), 1); if (parent != NULL) { hlev->parent = parent; gdl_append(&parent->children, hlev, link); } htsp_init(&hlev->nets, strhash, strkeyeq); htsp_init(&hlev->comps, strhash, strkeyeq); hlev->name = (name == NULL) ? NULL : rnd_strdup(name); hlev->hdepth = (parent == NULL) ? 0 : parent->hdepth+1; return hlev; } void csch_hlevel_free_tree(csch_hlevel_t *hlev) { csch_hlevel_t *f; for(f = gdl_first(&hlev->children); f != NULL; f = gdl_first(&hlev->children)) { gdl_remove(&hlev->children, f, link); csch_hlevel_free_tree(f); } htsp_uninit(&hlev->nets); htsp_uninit(&hlev->comps); free(hlev->name); free(hlev); } int csch_hier_enter(csch_hier_path_t *hpath, csch_hier_temp_t *tmp, csch_cgrp_t *sym, const char *name) { if (hpath->hdepth > MAX_DEPTH) { rnd_message(RND_MSG_ERROR, "Hierarchy recursion too deep (%ld)\n(The hierarchy graph shall not contain loops)\n", hpath->hdepth); return -1; } if (hpath->prefix.used > MAX_DEPTH*8) { rnd_message(RND_MSG_ERROR, "Hierarchy recursion too deep, prefix too long (%ld)\n(The hierarchy graph shall not contain loops)\n", hpath->prefix.used); return -1; } tmp->prefix_len = hpath->prefix.used; gds_append_str(&hpath->prefix, name); gds_append(&hpath->prefix, HSEP); /* rnd_trace("HIER: enter [%d '%s']\n", hpath->path.used+1, hpath->prefix.array);*/ hpath->hlev = csch_hlevel_new(hpath->hlev, sym, name); hpath->hdepth++; return 0; } void csch_hier_leave(csch_hier_path_t *hpath, csch_hier_temp_t *tmp) { /* rnd_trace("HIER: leave [%d '%s']\n", hpath->path.used, hpath->prefix.array);*/ hpath->hlev = hpath->hlev->parent; hpath->hdepth--; hpath->prefix.used = tmp->prefix_len; hpath->prefix.array[hpath->prefix.used] = '\0'; assert(hpath->hdepth >= 0); } void csch_hier_build_path(gds_t *prefix, csch_hlevel_t *hlev) { if ((hlev == NULL) || (hlev->parent == NULL)) return; /* root path is empty */ while((hlev != NULL) && (hlev->name != NULL)) { gds_insert_len(prefix, 0, "/", 1); gds_insert_len(prefix, 0, hlev->name, strlen(hlev->name)); hlev = hlev->parent; } }