/* * COPYRIGHT * * sch-rnd - modular/flexible schematics editor - sch-rnd (executable) * Copyright (C) 2022 Tibor 'Igor2' Palinkas * * (Supported by NLnet NGI0 PET Fund in 2022) * * 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., 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "conf_core.h" #include "sheet.h" #include "project.h" #include "multi.h" csch_sheet_t *sch_rnd_multi_switch_to(csch_sheet_t *s) { return (csch_sheet_t *)rnd_multi_switch_to(s == NULL ? NULL : &s->hidlib); } void sch_rnd_multi_switch_to_initial(csch_sheet_t *s) { rnd_multi_switch_to_(&s->hidlib); } void sch_rnd_multi_switch_to_delta(csch_sheet_t *curr, int step) { rnd_multi_switch_to_delta(curr == NULL ? NULL : &curr->hidlib, step); } static csch_project_t *sch_rnd_multi_load_project_(const char *project_fn) { csch_project_t *prj = htsp_get(&rnd_projects, project_fn); if (prj == NULL) { char *pfn = rnd_strdup(project_fn); prj = csch_load_project_by_sheet_name(pfn, 0, rnd_conf.rc.quiet); if (prj->hdr.loadname != NULL) { htsp_set(&rnd_projects, prj->hdr.loadname, prj); free(pfn); } else { TODO("memleak: this leaks pfn; happens when loading a sheet without a project file"); htsp_set(&rnd_projects, pfn, prj); } } return prj; } csch_project_t *sch_rnd_multi_load_project(const char *load_fn, const char *project_fn_in) { char *project_fn, *project_dir; char *freeme1, *freeme2; csch_project_t *prj; if (project_fn_in != NULL) { char *realp = rnd_lrealpath(project_fn_in); if (realp == NULL) return NULL; prj = sch_rnd_multi_load_project_(realp); free(realp); return prj; } /* there may be no existing project file - assume project is the directory */ project_dir = freeme1 = rnd_dirname_real(load_fn); project_fn = freeme2 = rnd_concat(project_dir, "/project.lht", NULL); prj = sch_rnd_multi_load_project_(project_fn); free(freeme1); free(freeme2); return prj; } static csch_sheet_t *sch_rnd_multi_load_(const char *fn, const char *fmt, const char *project_fn, int *is_project, rnd_conflist_t *root_sheets_out, rnd_conflist_t *aux_sheets_out); /* Append all unique file names to fns (keeps order) */ static void map_all_sheets(const rnd_conflist_t *list, vts0_t *fns) { rnd_conf_listitem_t *item; const char *shfn; int idx, n; rnd_conf_loop_list_str(list, item, shfn, idx) { int found = 0; for(n = 0; n < fns->used; n++) { if (strcmp(fns->array[n], shfn) == 0) { rnd_message(RND_MSG_ERROR, "Redundant file name in project: %s\n", shfn); found = 1; break; } } if (!found) vts0_append(fns, rnd_strdup(shfn)); } } /* Load a project file from prj_fn and all sheets; return first sheet */ static csch_sheet_t *sch_rnd_multi_load_project_sheets(const char *prj_fn) { csch_project_t *prj; csch_sheet_t *res = NULL; vts0_t fns = {0}; long n, aux_start; char *prj_fn_real; /* need to load by real name for comparisons/lookups/hash */ prj_fn_real = rnd_lrealpath(prj_fn); if (prj_fn_real == NULL) return NULL; prj = sch_rnd_multi_load_project_(prj_fn_real); free(prj_fn_real); if (prj == NULL) return NULL; rnd_conf_load_project(prj_fn, NULL); rnd_conf_merge_all("prj/root_sheets"); rnd_conf_merge_all("prj/aux_sheets"); map_all_sheets(&conf_core.prj.root_sheets, &fns); aux_start = fns.used; map_all_sheets(&conf_core.prj.aux_sheets, &fns); /* load usign raw "loadname" picked up from the project file because it may be a symlink or relative to the project file */ if (fns.used > 0) { for(n = 0; n < fns.used; n++) { csch_sheet_t *sheet = sch_rnd_multi_load_(fns.array[n], NULL, prj_fn, NULL, NULL, NULL); if (sheet != NULL) { if (res == NULL) res = sheet; sheet->stype = (n >= aux_start) ? CSCH_SHTY_AUX : CSCH_SHTY_ROOT; } free(fns.array[n]); } } else rnd_message(RND_MSG_ERROR, "Can not load project file '%s': does not name any sheet\n", prj_fn); vts0_uninit(&fns); return res; } int sch_rnd_is_fn_project_file(const char *fn) { const char *end = fn + strlen(fn) - 11; return strcmp(end, "project.lht") == 0; } /* sch-rnd, app-specific tasks on any newly loaded sheet */ static void sch_rnd_multi_load_post(csch_sheet_t *sheet) { rnd_tool_select_by_name(&sheet->hidlib, "arrow"); /* this loads and initializes sheet engines; this must come before events because events may rely on engines already initialized (e.g. devmap) */ sch_rnd_prj_postproc((csch_project_t *)sheet->hidlib.project); rnd_event(&sheet->hidlib, CSCH_EVENT_SHEET_POSTLOAD, NULL); rnd_event(&sheet->hidlib, RND_EVENT_LOAD_POST, NULL); } static csch_sheet_t *sch_rnd_multi_load_(const char *fn, const char *fmt, const char *project_fn, int *is_project, rnd_conflist_t *root_sheets_out, rnd_conflist_t *aux_sheets_out) { csch_sheet_t *sheet; csch_project_t *prj; const char *real_fn = fn; char *freeme = NULL; int lres; rnd_conf_state_t *ncs; FILE *f = NULL; void *cookie, *io; if ((project_fn == NULL) && ((fmt == NULL) || (*fmt == '\0'))) { if (sch_rnd_is_fn_project_file(fn)) { if (is_project != NULL) *is_project = 1; return sch_rnd_multi_load_project_sheets(fn); } } /* try load as bundled */ { prj = htsp_get(&rnd_projects, (char *)fn); if (prj != NULL) { /* already loaded */ if (prj->hdr.designs.used > 0) return prj->hdr.designs.array[0]; return NULL; } cookie = csch_test_parse_bundled(NULL, real_fn, CSCH_IOTYP_SHEET, &f, &io); if (cookie != NULL) { if (f != NULL) { sheet = csch_load_bundled_sheets(cookie, io, f, fn, sch_rnd_multi_load_post); fclose(f); if (root_sheets_out != NULL) memcpy(root_sheets_out, &conf_core.prj.root_sheets, sizeof(rnd_conflist_t)); if (sheet != NULL) { prj = (csch_project_t *)sheet->hidlib.project; htsp_set(&rnd_projects, (char *)fn, prj); } return sheet; } } } /* not bundled; single sheet loader with all the extra rounds for project files */ if (is_project != NULL) *is_project = 0; rnd_conf_multi_pre_new_design(&ncs); rnd_conf_reset(RND_CFR_DESIGN, fn); prj = sch_rnd_multi_load_project(fn, project_fn); rnd_conf_merge_all(NULL); /* get font settings right for the text update in the loader */ /* If sheet name is not absolute and there is a project file being loaded, sheet name needs to be translated relative to the project file's dir */ if ((project_fn != NULL) && !rnd_is_path_abs(fn)) { const char *s, *sep = NULL; /* check if project file is not in ./; sep is the last path separator */ for(s = project_fn; *s != '\0'; s++) { if ((*s == '/') || (*s == RND_DIR_SEPARATOR_C)) sep = s; } /* project file is not in ./, change real_fn */ if (sep != NULL) { gds_t tmp = {0}; gds_append_len(&tmp, project_fn, sep - project_fn + 1); /* +1 to keep the sep in project file's path */ gds_append_str(&tmp, real_fn); real_fn = freeme = tmp.array; } } lres = csch_project_load_sheet(prj, real_fn, fmt, &sheet, rnd_conf.rc.quiet, f); if (f != NULL) fclose(f); rnd_conf_multi_post_new_design(&ncs, &sheet->hidlib); if (lres != 0) { free(freeme); return NULL; } rnd_multi_load_prj_for_dsg(&sheet->hidlib); rnd_conf_state_new_design(&sheet->hidlib); rnd_conf_merge_all(NULL); /* to get the project file applied */ if (root_sheets_out != NULL) memcpy(root_sheets_out, &conf_core.prj.root_sheets, sizeof(rnd_conflist_t)); if (aux_sheets_out != NULL) memcpy(aux_sheets_out, &conf_core.prj.aux_sheets, sizeof(rnd_conflist_t)); sch_rnd_multi_load_post(sheet); rnd_conf_state_save(sheet->hidlib.saved_rnd_conf); free(freeme); return sheet; } csch_sheet_t *sch_rnd_multi_load(const char *fn, const char *fmt, int *is_project) { rnd_conflist_t roots = {0}, aux = {0}; csch_sheet_t *sheet; sheet = sch_rnd_multi_load_(fn, fmt, NULL, is_project, &roots, &aux); if ((sheet != NULL) && (sheet->stype == CSCH_SHTY_unknown)) { /* rnd_conf_merge_all("prj/root_sheets"); rnd_conf_merge_all("prj/aux_sheets");*/ csch_sheet_type_detect(sheet, &roots, &aux); } /* Do not free roots or aux, they are owned by the conf system */ return sheet; } csch_sheet_t *sch_rnd_app_load_sheet(csch_project_t *proj, const char *path) { int is_project; csch_sheet_t *res; rnd_design_t *last; last = rnd_multi_switch_to(NULL); /* empty conf so we can overwrite */ res = sch_rnd_multi_load_(path, NULL, proj->hdr.loadname, &is_project, NULL, NULL); res->stype = CSCH_SHTY_EXTERNAL; rnd_multi_switch_to_(last); /* activate the original */ return res; } static csch_sheet_t *sch_rnd_multi_new_empty_(csch_project_t *prj, const char *fn) { csch_sheet_t *sheet; static csch_project_t *prj_none = NULL; rnd_conf_state_t *ncs; rnd_conf_multi_pre_new_design(&ncs); rnd_conf_reset(RND_CFR_DESIGN, NULL); if ((prj == NULL) && (fn != NULL)) prj = sch_rnd_multi_load_project(fn, NULL); if (prj == NULL) { if (prj_none == NULL) { prj_none = csch_project_alloc(); htsp_set(&rnd_projects, rnd_strdup(""), prj_none); } prj = prj_none; } sheet = sch_rnd_sheet_new(prj); /* set filename before postprocessing the sheet so that libs dependeing on $(rc.path.design) already have it filled in */ if (fn == NULL) fn = ""; if (!rnd_is_dir(NULL, fn)) { /* If we failed to load a file with a given file name and created an empty sheet instead, remember the file name for the new sheet so {f s} saves using that name specified originally */ if (sheet != NULL) { sheet->hidlib.loadname = rnd_strdup(fn); sheet->hidlib.fullpath = rnd_strdup(fn); } } else rnd_message(RND_MSG_ERROR, "Supplied sheet name '%s' is a directory, can't save sheet by that name.\n", fn); rnd_conf_multi_post_new_design(&ncs,&sheet->hidlib); if (sheet != NULL) { rnd_multi_load_prj_for_dsg(&sheet->hidlib); rnd_conf_state_new_design(&sheet->hidlib); rnd_conf_merge_all(NULL); /* to get the project file applied */ sch_rnd_prj_postproc(prj); rnd_tool_select_by_name(&sheet->hidlib, "arrow"); rnd_event(&sheet->hidlib, CSCH_EVENT_SHEET_POSTLOAD, NULL); rnd_event(&sheet->hidlib, RND_EVENT_LOAD_POST, NULL); } else { TODO("multi: memleak"); /* sch_rnd_prj_free(prj);*/ } rnd_conf_state_save(sheet->hidlib.saved_rnd_conf); return sheet; } csch_sheet_t *sch_rnd_multi_new_empty(const char *fn) { return sch_rnd_multi_new_empty_(NULL, fn); } csch_sheet_t *sch_rnd_multi_new_empty_in_prj(csch_project_t *prj, const char *path) { return sch_rnd_multi_new_empty_(prj, path); } void sch_rnd_multi_unload(csch_sheet_t *sheet) { csch_project_t *prj; if (sheet == NULL) sheet = (csch_sheet_t *)rnd_multi_get_current(); if (sheet == NULL) return; prj = (csch_project_t *)sheet->hidlib.project; if (prj != NULL) sch_rnd_prj_remove_sheet(sheet); csch_sheet_uninit(sheet); rnd_conf_free(RND_CFR_DESIGN); rnd_conf_state_del_design(&sheet->hidlib); } const char sch_rnd_multi_cookie[] = "sch-rnd/multi.c"; void sch_rnd_multi_init(void) { rnd_conf_state_plug_reg(&conf_core, sizeof(conf_core), sch_rnd_multi_cookie); } void sch_rnd_multi_uninit(void) { rnd_conf_state_plug_unreg_all_cookie(sch_rnd_multi_cookie); } void sch_rnd_sheet_free_all(void) { rnd_design_t *curr, *next; htsp_entry_t *e; /* free all projects (will free all sheets too) */ while((e = htsp_first(&rnd_projects)) != NULL) { csch_project_t *prj = e->value; htsp_delentry(&rnd_projects, e); csch_project_free(prj); } /* just in case anything left outside of known projects */ for(curr = gdl_first(&rnd_designs); curr != NULL; curr = next) { next = gdl_next(&rnd_designs, curr); csch_sheet_uninit((csch_sheet_t *)curr); rnd_conf_state_del_design(curr); /* also removes curr from rnd_designs */ free(curr); } }