PIPS
properties.l
Go to the documentation of this file.
1 /*
2 
3  $Id: properties.l 23385 2017-06-15 09:11:36Z guillou $
4 
5  Copyright 1989-2016 MINES ParisTech
6 
7  This file is part of PIPS.
8 
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
12  any later version.
13 
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.
17 
18  See the GNU General Public License for more details.
19 
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/>.
22 
23 */
24 %option nounput
25 %option noinput
26 
27 %{
28 #ifdef HAVE_CONFIG_H
29  #include "pips_config.h"
30 #endif
31 /*
32  * - 01/1996: PIPS_PROPERTIESRC added. FC.
33  * - 09/1997: save/load properties in database. FC.
34  */
35 
36 /* The syntax of a property list. */
37 
38 #include <stdio.h>
39 #include <string.h>
40 #include <stdlib.h>
41 
42 #include "genC.h"
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"
47 
48 /* FC 2015-07-19
49  * #include "pipsdbm.h"
50  * avoid include cycle pipsdbm -> properties -> pipsdbm
51  * there is still a link cycle.
52  */
53 string db_get_meta_data_directory(void);
54 
55 #define TTRUE 10
56 #define TFALSE 11
57 #define TIDENT 12
58 #define TNUMB 13
59 #define TSTRING 14
60 #define ENDOFLINE 15
61 
62 /* properties are stored in this hash table (string -> property)
63  * for fast accesses.
64  */
65 static hash_table pl = (hash_table) NULL;
66 static bool update_property = false;
67 
68 /* A flag used to avoid infinite recursion in the case of use error here */
69 static size_t pending_errors = false;
70 
71 
72 /* Call pips_user_error and notice it first */ \
73 #define property_user_error(...) do { \
74  pending_errors++; \
75  user_error(__FUNCTION__, __VA_ARGS__); \
76 } while(0)
77 
78 #ifdef FLEX_SCANNER
79 
80 /* We may parse strings or files...
81  */
82 static char * string_to_parse = (char*) 0; /* shared pointer! */
83 
84 #define YY_INPUT(buffer, result, max_size) \
85 { \
86  unsigned int i = 0; \
87  if (string_to_parse) /* we're parsing a string */ \
88  { \
89  while (i<(unsigned int)max_size \
90  && *string_to_parse) \
91  buffer[i++] = *string_to_parse++; \
92  } \
93  else /* it's a file */ \
94  { \
95  int c; \
96  while (i<(unsigned int)max_size \
97  && (c=getc(yyin))!=EOF) \
98  buffer[i++] = (char) c; \
99  } \
100  result = i==0? YY_NULL: i; \
101 }
102 
103 static void parse_properties(bool processing_p);
104 
105 void parse_properties_string(char *s, bool processing_p)
106 {
107  yyrestart(NULL); // In case, lex has been interrupted by an error
108  update_property = true;
109  string_to_parse = s;
110  parse_properties(processing_p);
111  string_to_parse = (char *) 0;
112  update_property = false;
113 }
114 
115 #else /* ATT/POSIX just seem to like parsing yyin... */
116 
117 void parse_properties_string(char *s, bool processing_p)
118 {
119  property_user_error("cannot parse string (%s) without flex\n", s);
120 }
121 
122 #endif /* FLEX_SCANNER */
123 
124 %}
125 
126 %%
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",
136  *yytext, *yytext);
137  }
138 %%
139 
140 int yywrap(void)
141 { return 1;}
142 
143 static bool property_equal_p(property p1, property p2)
144 {
145  bool equal_p = (property_tag(p1)==property_tag(p2));
146 
147  if(equal_p) {
148  switch(property_tag(p1)) {
149  case is_property_int:
150  equal_p = (property_int(p1)==property_int(p2));
151  break;
152  case is_property_bool:
153  equal_p = (property_bool(p1)==property_bool(p2));
154  break;
155  case is_property_string:
156  equal_p = (strcmp(property_string(p1),property_string(p2))==0);
157  break;
158  default:
159  property_user_error("illegal tag %d\n", property_tag(p1));
160  }
161  }
162 
163  return equal_p;
164 }
165 
166 static string property_tag_to_string(tag t)
167 {
168  switch (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";
173  }
174 }
175 
176 static void parse_properties(bool processing_p)
177 {
178  int tok;
179  property pr = property_undefined, opr;
180 
181  if (!pl) pl = hash_table_make(hash_string, 0); /* lazy init */
182 
183  while ((tok = yylex()))
184  {
185  string n;
186 
187  if (tok != TIDENT) {
188  property_user_error("Syntax error: ident expected, got %d for -%s-.\n", tok, yytext);
189  return;
190  }
191 
192  n = strdup(yytext);
193 
194  if (update_property && property_undefined_p(get_property(n, true))) {
195  // property_user_error("Unknown property \"%s\" to update\n", n);
196  pending_errors++; \
197  user_error(__FUNCTION__, "Unknown property \"%s\" to update\n", n);
198  free(n);
199  return;
200  }
201 
202  switch (tok = yylex())
203  {
204  case TTRUE:
205  pr = make_property_bool(true);
206  break;
207  case TFALSE:
208  pr = make_property_bool(false);
209  break;
210  case TNUMB:
211  pr = make_property_int(atol(yytext));
212  break;
213  case TSTRING: {
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, '"'); // "
218  if (q == NULL) {
219  property_user_error("Ill. string : \"%s\".\n", yytext);
220  free(n);
221  return;
222  }
223  *q = '\0';
224  pr = make_property_string(s);
225  break;
226  }
227  default:
228  property_user_error("Unknown value \"%s\" for property \"%s\". Check the tpips script that is used.\n", yytext, n);
229  return;
230  }
231 
232  if ((opr = (property) hash_get(pl, n)) !=
233  (property) HASH_UNDEFINED_VALUE)
234  {
235  if (property_tag(opr)!=property_tag(pr))
236  {
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)),
241  n);
242  free_property(pr);
243  }
244  else if(processing_p
245  && strcmp(n, "CONSISTENCY_ENFORCED_P")==0
246  && !property_equal_p(pr, opr))
247  {
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 "
252  "process.\n", n);
253  free_property(pr);
254  }
255  else
256  {
257  hash_update(pl, n, (char *) pr);
258  if(!property_equal_p(pr, opr))
259  pips_debug(1,"property %s redefined\n", n);
260  free_property(opr);
261  }
262  }
263  else
264  hash_put(pl, n, (char *) pr);
265  free(n);
266  }
267 }
268 
269 static void parse_properties_file(FILE * fd)
270 {
271  string_to_parse = NULL;
272  update_property = false; // FI: Tpips/property.tpips, to survive
273  // the open after the close
274  yyrestart(fd);
275  // FI: we assume no PIPS database is open when parsing a property file
276  parse_properties(false);
277 }
278 
279 static void read_properties(void)
280 {
281  _UNUSED_ void * ignore_me = (void*) prop_get_leng;
282 
283  // avoid unused function warning...
284  FILE * old_prop = NULL;
285 
286  if (pl != (hash_table) NULL)
287  return;
288  /* else */
289  pl = hash_table_make(hash_string, 0);
290  update_property = false;
291 
292  /* Default properties:
293  *
294  * if the variable PIPS_PROPERTIESRC is defined in the environment,
295  * it contains the absolute property file name to use;
296  *
297  * else: ${PIPS_ROOT}/etc/properties.rc
298  */
299  if( (prop_in = fopen_config(PROPERTIES_RC,NULL,"PIPS_PROPERTIESRC")) )
300  {
301  parse_properties_file(prop_in);
302  fclose(prop_in), prop_in = (FILE*) NULL;
303  }
304 
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.
308  */
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) {
313  char opfn[100];
314  int stat = fscanf(old_prop, "%s", opfn);
315  assert(stat != EOF);
316 
317  if ((prop_in = fopen_config(opfn, NULL, NULL)) != NULL) {
318  parse_properties_file(prop_in);
319  fclose(prop_in), prop_in = (FILE*) NULL;
320  }
321  fclose(old_prop), old_prop = (FILE*) NULL;
322  }
323  free(pfn);
324 
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?
327  */
328  if ((prop_in = fopen(PROPERTIES_RC, "r")) != NULL) {
329  parse_properties_file(prop_in);
330  fclose(prop_in), prop_in = (FILE*) NULL;
331  }
332 }
333 
334 bool properties_initialized_p(void)
335 {
336  return pl != NULL;
337 }
338 
339 /* Test if we have too many property errors pending
340 
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...
343  */
344 bool too_many_property_errors_pending_p()
345 {
346  /* Well, this limit should depend on the actual implementation of
347  user_error, that may change in tpips, pyps, wpips... It should be
348  configurable... */
349  return pending_errors > 3;
350 }
351 
352 
353 /* Reset the pending stuff in case we where able to cope with... */
354 void reset_property_error() {
355  pending_errors = 0;
356 }
357 
358 
359 /* Get a property
360 
361  @param[in] name is the name of the property to look-up
362 
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
366 
367  @return the property or abort() if it does not exist and cool is false
368 */
369 property get_property(const char* name, bool cool)
370 {
371  property p = property_undefined;
372  property u = (property) HASH_UNDEFINED_VALUE;
373 
374  if (!pl)
375  read_properties(); /* rather lazy... */
376 
377  p = (property) hash_get(pl, name);
378 
379  if (p == u) {
380  if (!cool) {
381  property_user_error("unknown property: --%s--\n", name);
382  }
383  /* else since error does not return */
384  p = property_undefined;
385  }
386  return p;
387 }
388 
389 
390 /* Get the value of a bool property
391 
392  @param[in] name is the name of the property to look-up
393 
394  @return the bool value
395 */
396 bool get_bool_property(const char* name)
397 {
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);
402  }
403  return(property_bool(p));
404 }
405 
406 
407 /* Set the value of a bool property
408 
409  @param[in] name is the name of the property to set
410 
411  @param[in] b is the value to store in the property
412 */
413 void set_bool_property(const char* name, bool b)
414 {
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;
419 }
420 
421 
422 /* Get the value of a string property
423 
424  @param[in] name is the name of the property to look-up
425 
426  @return the string value, not to be freed
427 */
428 const char* get_string_property(const char* name)
429 {
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);
434 }
435 
436 
437 /* Get the value of a string property or ask to the user if it does not
438  has a non null string value
439 
440  @param[in] name is the name of the property to look-up
441 
442  @param[in] question is the sentence to display when asking for a value
443  because the property has no valid string value
444 
445  @return the string value, not to be freed
446 */
447 const char* get_string_property_or_ask(const char* name,const char question[])
448 {
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);
455 
456  return s;
457 }
458 
459 
460 /* Set the value of a string property
461 
462  @param[in] name is the name of the property to set
463 
464  @param[in] s is the value to store in the property. This string is
465  strdup()ed in this function
466 */
467 void set_string_property(const char* name, const char* s)
468 {
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);
474 }
475 
476 
477 /* Get the value of an integer property
478 
479  @param[in] name is the name of the property to look-up
480 
481  @return the integer value
482 */
483 int
484 get_int_property(const char* name)
485 {
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);
490 }
491 
492 
493 /* Set the value of an integer property
494 
495  @param[in] name is the name of the property to set
496 
497  @param[in] i is the value to store in the property
498 */
499 void set_int_property(const char* name, int i)
500 {
501  property p = get_property(name, false);
502  if (! property_int_p(p))
503  property_user_error("property %s is not int\n", name);
504  property_int(p) = i;
505 }
506 
507 void fprint_property_direct(FILE * fd, const char* pname)
508 {
509  property p = get_property(pname, true);
510  if (property_undefined_p(p))
511  fprintf(fd, "# undefined property %s\n", pname);
512  else
513  switch (property_tag(p)) {
514  case is_property_bool:
515  fprintf(fd, "%s", property_bool(p)? "TRUE": "FALSE");
516  break;
517  case is_property_int:
518  fprintf(fd, "%td", property_int(p));
519  break;
520  case is_property_string:
521  fprintf(fd, "%s", property_string(p));
522  break;
523  default:
524  fprintf(fd, "# bad property type for %s\n", pname);
525  }
526 
527 }
528 
529 void fprint_property(FILE * fd, const char* pname)
530 {
531  property p = get_property(pname, true);
532  if (property_undefined_p(p))
533  fprintf(fd, "# undefined property %s\n", pname);
534  else
535  switch (property_tag(p)) {
536  case is_property_bool:
537  fprintf(fd, "%s %s\n", pname, property_bool(p)? "TRUE": "FALSE");
538  break;
539  case is_property_int:
540  fprintf(fd, "%s %td\n", pname, property_int(p));
541  break;
542  case is_property_string:
543  fprintf(fd, "%s \"%s\"\n", pname, property_string(p));
544  break;
545  default:
546  fprintf(fd, "# bad property type for %s\n", pname);
547  }
548 }
549 
550 void fprint_properties(FILE * fd)
551 {
552  if (pl)
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);
561  }
562  else fprintf(fd, "# NO PIPS PROPERTIES...\n");
563 }
564 
565 /******************************************************* TOP-LEVEL INTERFACE */
566 
567 static string get_property_file_name(void)
568 {
569  string dir_name = db_get_meta_data_directory(),name;
570  int stat = asprintf(&name,"%s/properties",dir_name);
571  assert(stat != -1);
572  free(dir_name); return name;
573 }
574 
575 /* when opening a workspace, retrieve the properties.
576  * @return whether okay
577  */
578 bool open_properties(void)
579 {
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);
586  free(file_name);
587  return true;
588 }
589 
590 /* on close, save the properties in the current workspace.
591  * this is called from close_workspace, so there is some current one.
592  */
593 void save_properties(void)
594 {
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);
599  free(file_name);
600 }
601 
602 /* the creation is demand driven from get_property...
603  */