1 /*
2  * (C) Copyright 2005- ECMWF.
3  *
4  * This software is licensed under the terms of the Apache Licence Version 2.0
5  * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6  *
7  * In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
8  * virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
9  */
10 
11 /***************************************************************************
12  *   Jean Baptiste Filippi - 01.11.2005                                    *
13  *   Enrico  Fucile                                                        *
14  *                                                                         *
15  ***************************************************************************/
16 #include "grib_api_internal.h"
17 /*
18    This is used by make_class.pl
19 
20    START_CLASS_DEF
21    CLASS      = action
22    SUPER      = action_class_gen
23    IMPLEMENTS = dump
24    IMPLEMENTS = destroy
25    MEMBERS    = grib_concept_value* concept
26    MEMBERS    = char* basename
27    MEMBERS    = char* masterDir
28    MEMBERS    = char* localDir
29    MEMBERS    = int nofail
30    END_CLASS_DEF
31 
32  */
33 
34 /* START_CLASS_IMP */
35 
36 /*
37 
38 Don't edit anything between START_CLASS_IMP and END_CLASS_IMP
39 Instead edit values between START_CLASS_DEF and END_CLASS_DEF
40 or edit "action.class" and rerun ./make_class.pl
41 
42 */
43 
44 static void init_class(grib_action_class*);
45 static void dump(grib_action* d, FILE*, int);
46 static void destroy(grib_context*, grib_action*);
47 
48 
49 typedef struct grib_action_concept
50 {
51     grib_action act;
52     /* Members defined in gen */
53     long len;
54     grib_arguments* params;
55     /* Members defined in concept */
56     grib_concept_value* concept;
57     char* basename;
58     char* masterDir;
59     char* localDir;
60     int nofail;
61 } grib_action_concept;
62 
63 extern grib_action_class* grib_action_class_gen;
64 
65 static grib_action_class _grib_action_class_concept = {
66     &grib_action_class_gen,      /* super                     */
67     "action_class_concept",      /* name                      */
68     sizeof(grib_action_concept), /* size                      */
69     0,                           /* inited */
70     &init_class,                 /* init_class */
71     0,                           /* init                      */
72     &destroy,                    /* destroy */
73 
74     &dump, /* dump                      */
75     0,     /* xref                      */
76 
77     0, /* create_accessor*/
78 
79     0, /* notify_change */
80     0, /* reparse */
81     0, /* execute */
82 };
83 
84 grib_action_class* grib_action_class_concept = &_grib_action_class_concept;
85 
init_class(grib_action_class * c)86 static void init_class(grib_action_class* c)
87 {
88     c->xref            = (*(c->super))->xref;
89     c->create_accessor = (*(c->super))->create_accessor;
90     c->notify_change   = (*(c->super))->notify_change;
91     c->reparse         = (*(c->super))->reparse;
92     c->execute         = (*(c->super))->execute;
93 }
94 /* END_CLASS_IMP */
95 
96 #if GRIB_PTHREADS
97 static pthread_once_t once   = PTHREAD_ONCE_INIT;
98 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
99 
init()100 static void init()
101 {
102     pthread_mutexattr_t attr;
103     pthread_mutexattr_init(&attr);
104     pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
105     pthread_mutex_init(&mutex, &attr);
106     pthread_mutexattr_destroy(&attr);
107 }
108 #elif GRIB_OMP_THREADS
109 static int once = 0;
110 static omp_nest_lock_t mutex;
111 
init()112 static void init()
113 {
114     GRIB_OMP_CRITICAL(lock_action_class_concept_c)
115     {
116         if (once == 0) {
117             omp_init_nest_lock(&mutex);
118             once = 1;
119         }
120     }
121 }
122 #endif
123 
124 static grib_concept_value* get_concept(grib_handle* h, grib_action_concept* self);
125 
action_concept_get_concept(grib_accessor * a)126 grib_concept_value* action_concept_get_concept(grib_accessor* a)
127 {
128     return get_concept(grib_handle_of_accessor(a), (grib_action_concept*)a->creator);
129 }
130 
action_concept_get_nofail(grib_accessor * a)131 int action_concept_get_nofail(grib_accessor* a)
132 {
133     grib_action_concept* self = (grib_action_concept*)a->creator;
134     return self->nofail;
135 }
136 
grib_action_create_concept(grib_context * context,const char * name,grib_concept_value * concept,const char * basename,const char * name_space,const char * defaultkey,const char * masterDir,const char * localDir,const char * ecmfDir,int flags,int nofail)137 grib_action* grib_action_create_concept(grib_context* context,
138                                         const char* name,
139                                         grib_concept_value* concept,
140                                         const char* basename, const char* name_space, const char* defaultkey,
141                                         const char* masterDir, const char* localDir, const char* ecmfDir, int flags, int nofail)
142 {
143     grib_action_concept* a = NULL;
144     grib_action_class* c   = grib_action_class_concept;
145     grib_action* act       = (grib_action*)grib_context_malloc_clear_persistent(context, c->size);
146     act->op                = grib_context_strdup_persistent(context, "concept");
147 
148     act->cclass  = c;
149     a            = (grib_action_concept*)act;
150     act->context = context;
151     act->flags   = flags;
152 
153     if (name_space)
154         act->name_space = grib_context_strdup_persistent(context, name_space);
155 
156     if (basename)
157         a->basename = grib_context_strdup_persistent(context, basename);
158     else
159         a->basename = NULL;
160 
161     if (masterDir)
162         a->masterDir = grib_context_strdup_persistent(context, masterDir);
163     else
164         a->masterDir = NULL;
165 
166     if (localDir)
167         a->localDir = grib_context_strdup_persistent(context, localDir);
168     else
169         a->localDir = NULL;
170 
171     if (defaultkey)
172         act->defaultkey = grib_context_strdup_persistent(context, defaultkey);
173 
174     a->concept = concept;
175     if (concept) {
176         grib_concept_value* conc_val = concept;
177         grib_trie* index             = grib_trie_new(context);
178         while (conc_val) {
179             conc_val->index = index;
180             grib_trie_insert_no_replace(index, conc_val->name, conc_val);
181             conc_val = conc_val->next;
182         }
183     }
184     act->name = grib_context_strdup_persistent(context, name);
185 
186     a->nofail = nofail;
187 
188     return act;
189 }
190 
dump(grib_action * act,FILE * f,int lvl)191 static void dump(grib_action* act, FILE* f, int lvl)
192 {
193     int i = 0;
194 
195     for (i = 0; i < lvl; i++)
196         grib_context_print(act->context, f, "     ");
197 
198     printf("concept(%s) { ", act->name);
199     printf("\n");
200 
201     for (i = 0; i < lvl; i++)
202         grib_context_print(act->context, f, "     ");
203     printf("}\n");
204 }
205 
destroy(grib_context * context,grib_action * act)206 static void destroy(grib_context* context, grib_action* act)
207 {
208     grib_action_concept* self = (grib_action_concept*)act;
209 
210     grib_concept_value* v = self->concept;
211     if (v) {
212         grib_trie_delete_container(v->index);
213     }
214     while (v) {
215         grib_concept_value* n = v->next;
216         grib_concept_value_delete(context, v);
217         v = n;
218     }
219     grib_context_free_persistent(context, self->masterDir);
220     grib_context_free_persistent(context, self->localDir);
221     grib_context_free_persistent(context, self->basename);
222 }
223 
get_concept_impl(grib_handle * h,grib_action_concept * self)224 static grib_concept_value* get_concept_impl(grib_handle* h, grib_action_concept* self)
225 {
226     char buf[4096] = {0,};
227     char master[1024] = {0,};
228     char local[1024] = {0,};
229     char masterDir[1024] = {0,};
230     size_t lenMasterDir = 1024;
231     char key[4096]      = {0,};
232     char* full = 0;
233     int id;
234 
235     grib_context* context = ((grib_action*)self)->context;
236     grib_concept_value* c = NULL;
237 
238     if (self->concept != NULL)
239         return self->concept;
240 
241     Assert(self->masterDir);
242     grib_get_string(h, self->masterDir, masterDir, &lenMasterDir);
243 
244     sprintf(buf, "%s/%s", masterDir, self->basename);
245 
246     grib_recompose_name(h, NULL, buf, master, 1);
247 
248     if (self->localDir) {
249         char localDir[1024] = {0,};
250         size_t lenLocalDir = 1024;
251         grib_get_string(h, self->localDir, localDir, &lenLocalDir);
252         sprintf(buf, "%s/%s", localDir, self->basename);
253         grib_recompose_name(h, NULL, buf, local, 1);
254     }
255 
256     sprintf(key, "%s%s", master, local);
257 
258     id = grib_itrie_get_id(h->context->concepts_index, key);
259     if ((c = h->context->concepts[id]) != NULL)
260         return c;
261 
262     if (*local && (full = grib_context_full_defs_path(context, local)) != NULL) {
263         c = grib_parse_concept_file(context, full);
264         grib_context_log(h->context, GRIB_LOG_DEBUG,
265                          "Loading concept %s from %s", ((grib_action*)self)->name, full);
266     }
267 
268     full = grib_context_full_defs_path(context, master);
269 
270     if (c) {
271         grib_concept_value* last = c;
272         while (last->next)
273             last = last->next;
274         if (full) {
275             last->next = grib_parse_concept_file(context, full);
276         }
277     }
278     else if (full) {
279         c = grib_parse_concept_file(context, full);
280     }
281     else {
282         grib_context_log(context, GRIB_LOG_FATAL,
283                          "unable to find definition file %s in %s:%s\nDefinition files path=\"%s\"",
284                          self->basename, master, local, context->grib_definition_files_path);
285         return NULL;
286     }
287 
288     if (full) {
289         grib_context_log(h->context, GRIB_LOG_DEBUG,
290                          "Loading concept %s from %s", ((grib_action*)self)->name, full);
291     }
292 
293     h->context->concepts[id] = c;
294     if (c) {
295         grib_trie* index = grib_trie_new(context);
296         while (c) {
297             c->index = index;
298             grib_trie_insert_no_replace(index, c->name, c);
299             c = c->next;
300         }
301     }
302 
303     return h->context->concepts[id];
304 }
305 
get_concept(grib_handle * h,grib_action_concept * self)306 static grib_concept_value* get_concept(grib_handle* h, grib_action_concept* self)
307 {
308     grib_concept_value* result = NULL;
309     GRIB_MUTEX_INIT_ONCE(&once, &init)
310     GRIB_MUTEX_LOCK(&mutex);
311 
312     result = get_concept_impl(h, self);
313 
314     GRIB_MUTEX_UNLOCK(&mutex);
315     return result;
316 }
317 
concept_condition_expression_true(grib_handle * h,grib_concept_condition * c,char * exprVal)318 static int concept_condition_expression_true(grib_handle* h, grib_concept_condition* c, char* exprVal)
319 {
320     long lval;
321     long lres      = 0;
322     int ok         = 0;
323     int err        = 0;
324     const int type = grib_expression_native_type(h, c->expression);
325 
326     switch (type) {
327         case GRIB_TYPE_LONG:
328             grib_expression_evaluate_long(h, c->expression, &lres);
329             ok = (grib_get_long(h, c->name, &lval) == GRIB_SUCCESS) &&
330                  (lval == lres);
331             if (ok)
332                 sprintf(exprVal, "%ld", lres);
333             break;
334 
335         case GRIB_TYPE_DOUBLE: {
336             double dval;
337             double dres = 0.0;
338             grib_expression_evaluate_double(h, c->expression, &dres);
339             ok = (grib_get_double(h, c->name, &dval) == GRIB_SUCCESS) &&
340                  (dval == dres);
341             if (ok)
342                 sprintf(exprVal, "%g", dres);
343             break;
344         }
345 
346         case GRIB_TYPE_STRING: {
347             const char* cval;
348             char buf[80];
349             char tmp[80];
350             size_t len  = sizeof(buf);
351             size_t size = sizeof(tmp);
352 
353             ok = (grib_get_string(h, c->name, buf, &len) == GRIB_SUCCESS) &&
354                  ((cval = grib_expression_evaluate_string(h, c->expression, tmp, &size, &err)) != NULL) &&
355                  (err == 0) && (strcmp(buf, cval) == 0);
356             if (ok)
357                 sprintf(exprVal, "%s", cval);
358             break;
359         }
360 
361         default:
362             /* TODO: */
363             break;
364     }
365     return ok;
366 }
367 
368 /* Caller has to allocate space for the result.
369  * INPUTS: h, key and value (can be NULL)
370  * OUTPUT: result
371  * Example: key='typeOfLevel' whose value is 'mixedLayerDepth',
372  * result='typeOfFirstFixedSurface=169,typeOfSecondFixedSurface=255'
373  */
get_concept_condition_string(grib_handle * h,const char * key,const char * value,char * result)374 int get_concept_condition_string(grib_handle* h, const char* key, const char* value, char* result)
375 {
376     int err         = 0;
377     int length      = 0;
378     char strVal[64] = {0,};
379     char exprVal[256] = {0,};
380     const char* pValue                = value;
381     size_t len                        = sizeof(strVal);
382     grib_concept_value* concept_value = NULL;
383     grib_accessor* acc                = grib_find_accessor(h, key);
384     if (!acc)
385         return GRIB_NOT_FOUND;
386 
387     if (!value) {
388         err = grib_get_string(h, key, strVal, &len);
389         if (err)
390             return GRIB_INTERNAL_ERROR;
391         pValue = strVal;
392     }
393 
394     concept_value = action_concept_get_concept(acc);
395     while (concept_value) {
396         grib_concept_condition* concept_condition = concept_value->conditions;
397         if (strcmp(pValue, concept_value->name) == 0) {
398             while (concept_condition) {
399                 grib_expression* expression = concept_condition->expression;
400                 const char* condition_name  = concept_condition->name;
401                 Assert(expression);
402                 if (concept_condition_expression_true(h, concept_condition, exprVal) && strcmp(condition_name, "one") != 0) {
403                     length += sprintf(result + length, "%s%s=%s",
404                                       (length == 0 ? "" : ","), condition_name, exprVal);
405                 }
406                 concept_condition = concept_condition->next;
407             }
408         }
409 
410         concept_value = concept_value->next;
411     }
412     if (length == 0)
413         return GRIB_CONCEPT_NO_MATCH;
414     return GRIB_SUCCESS;
415 }
416