1 /*
2 * part-property.c
3 *
4 *
5 * Authors:
6 * Richard Hult <rhult@hem.passagen.se>
7 * Ricardo Markiewicz <rmarkie@fi.uba.ar>
8 * Andres de Barbara <adebarbara@fi.uba.ar>
9 * Marc Lorber <lorber.marc@wanadoo.fr>
10 *
11 * Web page: https://ahoi.io/project/oregano
12 *
13 * Copyright (C) 1999-2001 Richard Hult
14 * Copyright (C) 2003,2006 Ricardo Markiewicz
15 * Copyright (C) 2009-2012 Marc Lorber
16 *
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License as
19 * published by the Free Software Foundation; either version 2 of the
20 * License, or (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 * General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public
28 * License along with this program; if not, write to the
29 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
30 * Boston, MA 02110-1301, USA.
31 */
32
33 #include <glib.h>
34 #include <string.h>
35
36 #include "part.h"
37 #include "part-property.h"
38
39 // Gets the name of a macro variable.
40 //
41 // @param str str
42 // @param cls1 returns first conditional clause
43 // @param cls2 returns second clause
44 // @param sz returns number of characters parsed
45 // @return the name of a macro variable
get_macro_name(char macro,const char * str,char ** cls1,char ** cls2,size_t * sz)46 static char *get_macro_name (char macro, const char *str, char **cls1, char **cls2, size_t *sz)
47 {
48 char separators[] = {",.;/|()"};
49 GString *out;
50 const char *q, *qend;
51 char *csep = NULL;
52 size_t sln;
53 int rc = 0;
54 char *ret;
55
56 sln = strlen (str) + 1;
57 *sz = 0;
58 *cls1 = *cls2 = NULL;
59 qend = str + sln;
60 out = g_string_sized_new (sln);
61
62 // Get the name
63 for (q = str; (*q) && (*q != ' ') && !(csep = strchr (separators, *q)); q++) {
64 if (q > qend) {
65 g_warning ("Expand macro error.");
66 rc = 1;
67 break;
68 }
69 out = g_string_append_c (out, *q);
70 }
71
72 // if error found, return here
73 if (rc)
74 goto error;
75
76 // Look for conditional clauses
77 if (csep && macro != '@' && macro != '&') {
78 // get the first one
79 GString *aux;
80 q++; // skip the separator and store the clause in tmp
81 aux = g_string_new ("");
82 for (; (*q) && (*q != *csep); q++)
83 g_string_append_c (aux, *q);
84
85 if (!*q) {
86 g_string_free (aux, TRUE);
87 goto error;
88 }
89
90 *cls1 = aux->str;
91 q++; // skip the end-of-clause separator
92 g_string_free (aux, FALSE);
93
94 // Check for the second one
95 if ((*q) && (csep = strchr (separators, *q))) {
96 q++; // skip the separator and store in tmp
97 aux = g_string_new ("");
98 for (; (*q) && (*q != *csep); q++)
99 g_string_append_c (aux, *q);
100
101 if (!(*q)) {
102 g_free (*cls1);
103 *cls1 = NULL;
104 goto error;
105 }
106
107 *cls2 = aux->str;
108 q++; // skip the end-of-clause separator
109 g_string_free (aux, FALSE);
110 }
111 }
112
113 *sz = out->len + (*cls1 != NULL ? strlen(*cls1) + 2 : 0) + (*cls2 != NULL ? strlen(*cls2) + 2 : 0);
114 ret = NULL;
115 if (out->len > 0) {
116 out = g_string_append_c (out, '\0');
117 ret = g_strdup (out->str);
118 }
119 g_string_free (out, TRUE);
120
121 return ret;
122
123 error:
124 g_string_free (out, TRUE);
125 return NULL;
126 }
127
128 // Rules:
129 // @<id> value of <id>. If no value, error
130 // &<id> value of <id> if <id> is defined
131 // ?<id>s...s text between s...s separators if <id> defined
132 // ?<id>s...ss...s text between 1st s...s separators if <id> defined
133 // else 2nd s...s clause
134 // ~<id>s...s text between s...s separators if <id> undefined
135 // ~<id>s...ss...s text between 1st s...s separators if <id> undefined
136 // else 2nd s...s clause
137 // #<id>s...s text between s...s separators if <id> defined, but
138 // delete rest of template if <id> undefined
139
140 // Separators can be any of {',', '.', ';', '/', '|', '(', ')'}.
141 // For an opening-closing pair of
142 // separators the same character has to be used.
143
144 // Examples:
145 // R^@refdes %1 %2 @value
146 // V^@refdes %+ %- SIN(@offset @ampl @freq 0 0)
147 // ?DC|DC @DC|
part_property_expand_macros(Part * part,char * string)148 char *part_property_expand_macros (Part *part, char *string)
149 {
150 static char mcode[] = {"@?~#&"};
151 char *value;
152 char *tmp0, *temp, *qn, *q0, *t0;
153 char *cls1, *cls2;
154 GString *out;
155 size_t sln;
156 char *ret;
157
158 g_return_val_if_fail (part != NULL, NULL);
159 g_return_val_if_fail (IS_PART (part), NULL);
160 g_return_val_if_fail (string != NULL, NULL);
161
162 cls1 = cls2 = q0 = NULL;
163
164 tmp0 = temp = g_strdup (string);
165
166 out = g_string_new ("");
167
168 for (temp = string; *temp;) {
169 // Look for any of the macro char codes.
170 if (strchr (mcode, *temp)) {
171 qn = get_macro_name (*temp, temp + 1, &cls1, &cls2, &sln);
172 if (qn == NULL)
173 return NULL;
174 value = part_get_property (part, qn);
175 if ((*temp == '@' || *temp == '&') && value) {
176 out = g_string_append (out, value);
177 } else if (*temp == '&' && !value) {
178 g_warning ("expand macro error: macro %s undefined", qn);
179 g_free (qn);
180 return NULL;
181 } else if (*temp == '?' || *temp == '~') {
182 if (cls1 == NULL) {
183 g_warning ("error in template: %s", temp);
184 g_free (qn);
185 return NULL;
186 }
187 q0 = (value ? (*temp == '?' ? cls1 : cls2) : (*temp == '?' ? cls2 : cls1));
188 if (q0) {
189 t0 = part_property_expand_macros (part, q0);
190 if (!t0) {
191 g_warning ("error in template: %s", temp);
192 g_free (qn);
193 } else {
194 out = g_string_append (out, t0);
195 g_free (t0);
196 }
197 }
198 } else if (*temp == '#') {
199 if (value) {
200 t0 = part_property_expand_macros (part, value);
201 if (!t0) {
202 g_warning ("error in template: %s", temp);
203 g_free (qn);
204 } else {
205 out = g_string_append (out, t0);
206 g_free (t0);
207 }
208 } else
209 *(temp + sln) = 0;
210 }
211 temp += 1;
212 temp += sln;
213 g_free (qn);
214 g_free (cls1);
215 g_free (cls2);
216 } else {
217 if (*temp == '\\') {
218 temp++;
219 switch (*temp) {
220 case 'n':
221 out = g_string_append_c (out, '\n');
222 break;
223 case 't':
224 out = g_string_append_c (out, '\t');
225 break;
226 case 'r':
227 out = g_string_append_c (out, '\r');
228 break;
229 case 'f':
230 out = g_string_append_c (out, '\f');
231 }
232 temp++;
233 } else {
234 out = g_string_append_c (out, *temp);
235 temp++;
236 }
237 }
238 }
239
240 g_free (tmp0);
241
242 out = g_string_append_c (out, '\0');
243 ret = g_strdup (out->str);
244 g_string_free (out, TRUE);
245
246 return ret;
247 }
248
249 /**
250 * see #168
251 */
update_connection_designators(Part * part,char ** prop,int * node_ctr)252 void update_connection_designators(Part *part, char **prop, int *node_ctr)
253 {
254 if (prop == NULL || *prop == NULL)
255 return;
256 if (node_ctr == NULL)
257 return;
258 if (part == NULL || !IS_PART(part))
259 return;
260
261 char *temp = *prop;
262 GString *out = g_string_new ("");
263
264 int breakout = FALSE;
265 while (!breakout) {
266 char **prop_split = g_regex_split_simple("[@?~#&%].*", temp, 0, 0);
267 if (prop_split[0] == NULL) {
268 g_strfreev(prop_split);
269 break;
270 }
271 temp += strlen(prop_split[0]);
272 g_string_append_printf(out, "%s", prop_split[0]);
273 g_strfreev(prop_split);
274 char macro = *temp;
275 temp++;
276 switch (macro) {
277 case '%':
278 {
279 char **prop_split = g_regex_split_simple(" .*", temp, 0, 0);
280 temp += strlen(prop_split[0]);
281 g_string_append_printf(out, "%%%d", (*node_ctr)++);
282 g_strfreev(prop_split);
283 break;
284 }
285 case '@':
286 {
287 char **prop_split = g_regex_split_simple("[,.;/|() ].*", temp, 0, 0);
288 temp += strlen(prop_split[0]);
289 char *prop_ref_name = prop_split[0];
290 char **prop_ref_value = part_get_property_ref(part, prop_ref_name);
291 g_string_append_printf(out, "@%s", prop_ref_name);
292 update_connection_designators(part, prop_ref_value, node_ctr);
293 g_strfreev(prop_split);
294 break;
295 }
296 case '&':
297 {
298 char **prop_split = g_regex_split_simple("[,.;/|() ].*", temp, 0, 0);
299 temp += strlen(prop_split[0]);
300 char *prop_ref_name = prop_split[0];
301 char **prop_ref_value = part_get_property_ref(part, prop_ref_name);
302 g_string_append_printf(out, "&%s", prop_ref_name);
303 if (prop_ref_value != NULL && *prop_ref_value != NULL)
304 update_connection_designators(part, prop_ref_value, node_ctr);
305 g_strfreev(prop_split);
306 break;
307 }
308 case '?':
309 case '~':
310 {
311 char **prop_split = g_regex_split_simple("([,.;/|()])(.*?)(\\g{-3})(?(?=[,.;/|()])([,.;/|()])(.*?)(\\g{-3})).*", temp, 0, 0);
312 char *prop_ref_name = g_strdup(prop_split[0]);
313 char separator1 = *prop_split[1];
314 char *cls1 = g_strdup(prop_split[2]);
315 //separator1 == *prop_split_name[3]
316 char separator2 = prop_split[4] != NULL ? *prop_split[4] : 0;
317 char *cls2 = NULL;
318 if (separator2 != 0) {
319 cls2 = g_strdup(prop_split[5]);
320 }
321 char **prop_ref_value = part_get_property_ref(part, prop_ref_name);
322 for (int i = 0; prop_split[i] != NULL; i++)
323 temp += strlen(prop_split[i]);
324 g_strfreev(prop_split);
325
326 if (
327 (macro == '?' && prop_ref_value != NULL && *prop_ref_value != NULL)
328 ||
329 (macro == '~' && (prop_ref_value == NULL || *prop_ref_value == NULL))
330 )
331 update_connection_designators(part, &cls1, node_ctr);
332 else if (cls2 != NULL)
333 update_connection_designators(part, &cls2, node_ctr);
334
335 g_string_append_printf(out, "%c%s%c%s%c", macro, prop_ref_name, separator1, cls1, separator1);
336 if (cls2 != NULL) {
337 g_string_append_printf(out, "%c%s%c", separator2, cls2, separator2);
338 g_free(cls2);
339 }
340
341 g_free(cls1);
342 g_free(prop_ref_name);
343 break;
344 }
345 case '#':
346 {
347 char **prop_split = g_regex_split_simple("([,.;/|()])(.*?)(\\g{-3}).*", temp, 0, 0);
348 char *prop_ref_name = g_strdup(prop_split[0]);
349 char separator = *prop_split[1];
350 char *cls = g_strdup(prop_split[2]);
351 //separator == *prop_split_name[3]
352 char **prop_ref_value = part_get_property_ref(part, prop_ref_name);
353 for (int i = 0; prop_split[i] != NULL; i++)
354 temp += strlen(prop_split[i]);
355 g_strfreev(prop_split);
356
357 if (prop_ref_value != NULL && *prop_ref_value != NULL) {
358 update_connection_designators(part, &cls, node_ctr);
359 g_string_append_printf(out, "#%s%c%s%c", prop_ref_name, separator, cls, separator);
360 } else {
361 g_string_append_printf(out, "#%s%c%s%c%s", prop_ref_name, separator, cls, separator, temp);
362 breakout = TRUE;
363 }
364
365 g_free(cls);
366 g_free(prop_ref_name);
367 break;
368 }
369 default:
370 {
371 breakout = TRUE;
372 break;
373 }
374 }
375 }
376 g_free(*prop);
377 *prop = out->str;
378 g_string_free (out, FALSE);
379 return;
380 }
381