3 $Id: properties.l 23385 2017-06-15 09:11:36Z guillou $
5 Copyright 1989-2016 MINES ParisTech
7 This file is part of PIPS.
9 PIPS is free software: you can redistribute it and/or modify it
10 under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
14 PIPS is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with PIPS. If not, see <http://www.gnu.org/licenses/>.
29 #include "pips_config.h"
32 * - 01/1996: PIPS_PROPERTIESRC added. FC.
33 * - 09/1997: save/load properties in database. FC.
36 /* The syntax of a property list. */
43 #include "misc.h" // user_error stuff
44 #include "constants.h" // PROPERTIES_RC & OLD_PROPERTIES_RC
45 #include "naming.h" // get_script_directory_name
46 #include "properties.h"
49 * #include "pipsdbm.h"
50 * avoid include cycle pipsdbm -> properties -> pipsdbm
51 * there is still a link cycle.
53 string db_get_meta_data_directory(void);
62 /* properties are stored in this hash table (string -> property)
65 static hash_table pl = (hash_table) NULL;
66 static bool update_property = false;
68 /* A flag used to avoid infinite recursion in the case of use error here */
69 static size_t pending_errors = false;
72 /* Call pips_user_error and notice it first */ \
73 #define property_user_error(...) do { \
75 user_error(__FUNCTION__, __VA_ARGS__); \
80 /* We may parse strings or files...
82 static char * string_to_parse = (char*) 0; /* shared pointer! */
84 #define YY_INPUT(buffer, result, max_size) \
87 if (string_to_parse) /* we're parsing a string */ \
89 while (i<(unsigned int)max_size \
90 && *string_to_parse) \
91 buffer[i++] = *string_to_parse++; \
93 else /* it's a file */ \
96 while (i<(unsigned int)max_size \
97 && (c=getc(yyin))!=EOF) \
98 buffer[i++] = (char) c; \
100 result = i==0? YY_NULL: i; \
103 static void parse_properties(bool processing_p);
105 void parse_properties_string(char *s, bool processing_p)
107 yyrestart(NULL); // In case, lex has been interrupted by an error
108 update_property = true;
110 parse_properties(processing_p);
111 string_to_parse = (char *) 0;
112 update_property = false;
115 #else /* ATT/POSIX just seem to like parsing yyin... */
117 void parse_properties_string(char *s, bool processing_p)
119 property_user_error("cannot parse string (%s) without flex\n", s);
122 #endif /* FLEX_SCANNER */
127 TRUE { return(TTRUE); }
128 FALSE { return(TFALSE); }
129 -?[0-9]+ { return(TNUMB); }
130 [-_a-zA-Z][-_a-zA-Z0-9]* { return(TIDENT); }
131 \"[^\"]*\" { return(TSTRING); }
132 ^[ \t]*#.*$ { /* comments are skipped. */ }
133 [ \t\n]* { /* blanks are skipped. */ }
134 = { /* ignore =: foo=bla same as foo bla */ }
135 . { fprintf(stderr, "skipping unexpected char %c (%d)\n",
143 static bool property_equal_p(property p1, property p2)
145 bool equal_p = (property_tag(p1)==property_tag(p2));
148 switch(property_tag(p1)) {
149 case is_property_int:
150 equal_p = (property_int(p1)==property_int(p2));
152 case is_property_bool:
153 equal_p = (property_bool(p1)==property_bool(p2));
155 case is_property_string:
156 equal_p = (strcmp(property_string(p1),property_string(p2))==0);
159 property_user_error("illegal tag %d\n", property_tag(p1));
166 static string property_tag_to_string(tag t)
169 case is_property_int: return "int";
170 case is_property_bool: return "bool";
171 case is_property_string: return "string";
172 default: return "UNKNOW property type";
176 static void parse_properties(bool processing_p)
179 property pr = property_undefined, opr;
181 if (!pl) pl = hash_table_make(hash_string, 0); /* lazy init */
183 while ((tok = yylex()))
188 property_user_error("Syntax error: ident expected, got %d for -%s-.\n", tok, yytext);
194 if (update_property && property_undefined_p(get_property(n, true))) {
195 // property_user_error("Unknown property \"%s\" to update\n", n);
197 user_error(__FUNCTION__, "Unknown property \"%s\" to update\n", n);
202 switch (tok = yylex())
205 pr = make_property_bool(true);
208 pr = make_property_bool(false);
211 pr = make_property_int(atol(yytext));
214 /* GO: We need to free(s) so the skeep now
215 the quote and do not use s+1 anymore*/
216 char *s = strdup(yytext + 1);
217 char *q = strrchr(s, '"'); // "
219 property_user_error("Ill. string : \"%s\".\n", yytext);
224 pr = make_property_string(s);
228 property_user_error("Unknown value \"%s\" for property \"%s\". Check the tpips script that is used.\n", yytext, n);
232 if ((opr = (property) hash_get(pl, n)) !=
233 (property) HASH_UNDEFINED_VALUE)
235 if (property_tag(opr)!=property_tag(pr))
237 property_user_error("cannot change type from %s to %s "
238 "when updating property %s\n",
239 property_tag_to_string(property_tag(opr)),
240 property_tag_to_string(property_tag(pr)),
245 && strcmp(n, "CONSISTENCY_ENFORCED_P")==0
246 && !property_equal_p(pr, opr))
248 property_user_error("Property %s cannot be redefined when "
249 "processing is started because consistenty "
250 "can no longer be enforced. Move the set "
251 "property command earlier in the compilaton "
257 hash_update(pl, n, (char *) pr);
258 if(!property_equal_p(pr, opr))
259 pips_debug(1,"property %s redefined\n", n);
264 hash_put(pl, n, (char *) pr);
269 static void parse_properties_file(FILE * fd)
271 string_to_parse = NULL;
272 update_property = false; // FI: Tpips/property.tpips, to survive
273 // the open after the close
275 // FI: we assume no PIPS database is open when parsing a property file
276 parse_properties(false);
279 static void read_properties(void)
281 _UNUSED_ void * ignore_me = (void*) prop_get_leng;
283 // avoid unused function warning...
284 FILE * old_prop = NULL;
286 if (pl != (hash_table) NULL)
289 pl = hash_table_make(hash_string, 0);
290 update_property = false;
292 /* Default properties:
294 * if the variable PIPS_PROPERTIESRC is defined in the environment,
295 * it contains the absolute property file name to use;
297 * else: ${PIPS_ROOT}/etc/properties.rc
299 if( (prop_in = fopen_config(PROPERTIES_RC,NULL,"PIPS_PROPERTIESRC")) )
301 parse_properties_file(prop_in);
302 fclose(prop_in), prop_in = (FILE*) NULL;
305 /* An older version of properties.rc might be useful for the current
306 * PIPS execution. This is used to maintain backward compatibility
307 * for old non-regression tests.
309 string pfn = string_undefined;
310 string sdn = get_script_directory_name();
311 pfn = strdup(concatenate(sdn, "/", OLD_PROPERTIES_RC, NULL));
312 if ((old_prop = fopen(pfn, "r")) != NULL) {
314 int stat = fscanf(old_prop, "%s", opfn);
317 if ((prop_in = fopen_config(opfn, NULL, NULL)) != NULL) {
318 parse_properties_file(prop_in);
319 fclose(prop_in), prop_in = (FILE*) NULL;
321 fclose(old_prop), old_prop = (FILE*) NULL;
325 /* Update the standard properties with a locally defined ./properties.rc
326 * Is . the cwd or the directory where the .tpips script has been found?
328 if ((prop_in = fopen(PROPERTIES_RC, "r")) != NULL) {
329 parse_properties_file(prop_in);
330 fclose(prop_in), prop_in = (FILE*) NULL;
334 bool properties_initialized_p(void)
339 /* Test if we have too many property errors pending
341 It is useful in pips_user_error to avoid infinite recursion where
342 properties are used there to change the behaviour of pips_user_error...
344 bool too_many_property_errors_pending_p()
346 /* Well, this limit should depend on the actual implementation of
347 user_error, that may change in tpips, pyps, wpips... It should be
349 return pending_errors > 3;
353 /* Reset the pending stuff in case we where able to cope with... */
354 void reset_property_error() {
361 @param[in] name is the name of the property to look-up
363 @param[in] cool is a bool setting the behaviour if the property does
364 not exist. If the property does not exist and cool is true, it return
365 property_undefined but if false it aborts
367 @return the property or abort() if it does not exist and cool is false
369 property get_property(const char* name, bool cool)
371 property p = property_undefined;
372 property u = (property) HASH_UNDEFINED_VALUE;
375 read_properties(); /* rather lazy... */
377 p = (property) hash_get(pl, name);
381 property_user_error("unknown property: --%s--\n", name);
383 /* else since error does not return */
384 p = property_undefined;
390 /* Get the value of a bool property
392 @param[in] name is the name of the property to look-up
394 @return the bool value
396 bool get_bool_property(const char* name)
398 property p = get_property(name, false);
399 if (! property_bool_p(p)) {
400 // FI: Could be an internal error...
401 property_user_error("Property \"%s\" is not boolean.\n", name);
403 return(property_bool(p));
407 /* Set the value of a bool property
409 @param[in] name is the name of the property to set
411 @param[in] b is the value to store in the property
413 void set_bool_property(const char* name, bool b)
415 property p = get_property(name, false);
416 if (! property_bool_p(p))
417 property_user_error("property %s is not bool\n", name);
418 property_bool(p) = b;
422 /* Get the value of a string property
424 @param[in] name is the name of the property to look-up
426 @return the string value, not to be freed
428 const char* get_string_property(const char* name)
430 property p = get_property(name, false);
431 if (! property_string_p(p))
432 property_user_error("property %s is not string\n", name);
433 return property_string(p);
437 /* Get the value of a string property or ask to the user if it does not
438 has a non null string value
440 @param[in] name is the name of the property to look-up
442 @param[in] question is the sentence to display when asking for a value
443 because the property has no valid string value
445 @return the string value, not to be freed
447 const char* get_string_property_or_ask(const char* name,const char question[])
449 property p = get_property(name, false);
450 if (! property_string_p(p))
451 property_user_error("property %s is not string\n", name);
452 string s = property_string(p);
453 while(!s || string_undefined_p(s) || s[0] == '\0' )
454 s = user_request(question);
460 /* Set the value of a string property
462 @param[in] name is the name of the property to set
464 @param[in] s is the value to store in the property. This string is
465 strdup()ed in this function
467 void set_string_property(const char* name, const char* s)
469 property p = get_property(name, false);
470 if (! property_string_p(p))
471 property_user_error("property %s is not string\n", name);
472 free(property_string(p));
473 property_string(p) = strdup(s);
477 /* Get the value of an integer property
479 @param[in] name is the name of the property to look-up
481 @return the integer value
484 get_int_property(const char* name)
486 property p = get_property(name, false);
487 if (! property_int_p(p))
488 property_user_error("property %s is not int\n", name);
489 return property_int(p);
493 /* Set the value of an integer property
495 @param[in] name is the name of the property to set
497 @param[in] i is the value to store in the property
499 void set_int_property(const char* name, int i)
501 property p = get_property(name, false);
502 if (! property_int_p(p))
503 property_user_error("property %s is not int\n", name);
507 void fprint_property_direct(FILE * fd, const char* pname)
509 property p = get_property(pname, true);
510 if (property_undefined_p(p))
511 fprintf(fd, "# undefined property %s\n", pname);
513 switch (property_tag(p)) {
514 case is_property_bool:
515 fprintf(fd, "%s", property_bool(p)? "TRUE": "FALSE");
517 case is_property_int:
518 fprintf(fd, "%td", property_int(p));
520 case is_property_string:
521 fprintf(fd, "%s", property_string(p));
524 fprintf(fd, "# bad property type for %s\n", pname);
529 void fprint_property(FILE * fd, const char* pname)
531 property p = get_property(pname, true);
532 if (property_undefined_p(p))
533 fprintf(fd, "# undefined property %s\n", pname);
535 switch (property_tag(p)) {
536 case is_property_bool:
537 fprintf(fd, "%s %s\n", pname, property_bool(p)? "TRUE": "FALSE");
539 case is_property_int:
540 fprintf(fd, "%s %td\n", pname, property_int(p));
542 case is_property_string:
543 fprintf(fd, "%s \"%s\"\n", pname, property_string(p));
546 fprintf(fd, "# bad property type for %s\n", pname);
550 void fprint_properties(FILE * fd)
553 { // dump all sorted properties in fd.
554 gen_array_t array = gen_array_make(0);
555 fprintf(fd, "# PIPS PROPERTIES\n");
556 HASH_MAP(name, p, gen_array_dupappend(array, name), pl);
557 gen_array_sort(array);
558 GEN_ARRAY_FOREACH(string, name, array)
559 fprint_property(fd, name);
560 gen_array_full_free(array);
562 else fprintf(fd, "# NO PIPS PROPERTIES...\n");
565 /******************************************************* TOP-LEVEL INTERFACE */
567 static string get_property_file_name(void)
569 string dir_name = db_get_meta_data_directory(),name;
570 int stat = asprintf(&name,"%s/properties",dir_name);
572 free(dir_name); return name;
575 /* when opening a workspace, retrieve the properties.
576 * @return whether okay
578 bool open_properties(void)
580 string file_name = get_property_file_name();
581 FILE * file = check_fopen(file_name, "r");
582 if (!file) return false;
583 if (pl) hash_table_free(pl), pl = (hash_table) NULL; /* lazy clean... */
584 parse_properties_file(file);
585 safe_fclose(file, file_name);
590 /* on close, save the properties in the current workspace.
591 * this is called from close_workspace, so there is some current one.
593 void save_properties(void)
595 string file_name = get_property_file_name();
596 FILE * file = safe_fopen(file_name, "w");
597 fprint_properties(file);
598 safe_fclose(file, file_name);
602 /* the creation is demand driven from get_property...