4107464e99e7a104dc01be0d57a26da83707d55f
[vpp.git] / vppinfra / vppinfra / macros.c
1 /*
2   macros.c - a simple macro expander
3   
4   Copyright (c) 2010, 2014 Cisco and/or its affiliates.
5   
6   * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at:
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17 */
18
19 #include <vppinfra/macros.h>
20
21 static inline int macro_isalnum (i8 c)
22 {
23   if ((c >= 'A' && c <= 'Z')
24       || (c >='a' && c <= 'z')
25       || (c >='0' && c <= '9')
26       || (c == '_'))
27     return 1;
28   return 0;
29 }
30
31 static i8 *builtin_eval(macro_main_t *mm, i8 *varname, i32 complain)
32 {
33     uword *p;
34     i8 *(*fp)(macro_main_t *, i32);
35
36     p = hash_get_mem(mm->the_builtin_eval_hash, varname);
37     if (p == 0)
38         return 0;
39     fp = (void *)(p[0]);
40     return (*fp)(mm, complain);
41 }
42
43 int clib_macro_unset (macro_main_t * mm, char *name)
44 {
45   hash_pair_t * p;
46   u8 * key, * value;
47
48   p = hash_get_pair (mm->the_value_table_hash, name);
49   
50   if (p == 0)
51     return 1;
52
53   key = (u8 *)(p->key);
54   value = (u8 *) (p->value[0]);
55   hash_unset_mem (mm->the_value_table_hash, name);
56
57   vec_free (value);
58   vec_free (key);
59   return 0;
60 }
61
62 int clib_macro_set_value (macro_main_t * mm, char *name, char *value)
63 {
64   u8 * key_copy, *value_copy;
65   int rv;
66
67   rv = clib_macro_unset (mm, name);
68
69   key_copy = format (0, "%s%c", name, 0);
70   value_copy = format (0, "%s%c", value, 0);
71
72   hash_set_mem (mm->the_value_table_hash, key_copy, value_copy);
73   return rv;
74 }
75
76 i8 * clib_macro_get_value (macro_main_t * mm, char *name)
77 {
78   uword * p;
79
80   p = hash_get_mem (mm->the_value_table_hash, name);
81   if (p)
82     return (i8 *)(p[0]);
83   else
84     return 0;
85 }
86
87 /*
88  * eval: takes a string, returns a vector.
89  * looks up $foobar in the variable table.
90  */
91 i8 * clib_macro_eval (macro_main_t *mm, i8 *s, i32 complain)
92 {
93   i8 *rv=0;
94   i8 *varname, *varvalue;
95   i8 *ts;
96
97   while (*s) 
98     {
99       switch(*s) 
100         {
101         case '\\':
102           s++;
103           /* fallthrough */
104
105         default:
106           vec_add1(rv, *s);
107           s++;
108           break;
109
110         case '$':
111           s++;
112           varname = 0;
113           /*
114            * Make vector with variable name in it.
115            */
116           while (*s && (macro_isalnum (*s) || (*s == '_') || (*s == '('))) 
117             {
118
119               /* handle $(foo) */
120               if (*s == '(') 
121                 {
122                   s++;        /* skip '(' */
123                   while (*s && *s != ')') 
124                     {
125                       vec_add1(varname, *s);
126                       s++;
127                     }
128                   if (*s)
129                     s++;    /* skip ')' */
130                   break;
131                 }
132               vec_add1(varname, *s);
133               s++;
134             }
135           /* null terminate */
136           vec_add1(varname, 0);
137           /* Look for a builtin, e.g. $my_hostname */
138           if (!(varvalue = builtin_eval(mm, varname, complain))) 
139             {
140               /* Look in value table */
141               if (! varvalue) 
142                 {
143                   char * tmp = clib_macro_get_value (mm, varname);
144                   if (tmp)
145                     varvalue = (i8 *) format (0, "%s%c", tmp, 0);
146                 }
147 #ifdef CLIB_UNIX
148               /* Look in environment. */
149               if (! varvalue) 
150                 {
151                   char * tmp = getenv (varname);
152                   if (tmp)
153                     varvalue = (i8 *) format (0, "%s%c", tmp, 0);
154                 }
155 #endif /* CLIB_UNIX */
156             }
157           if (varvalue) 
158             {
159               /* recursively evaluate */
160               ts = clib_macro_eval(mm, varvalue, complain);
161               vec_free(varvalue);
162               /* add results to answer */
163               vec_append(rv, ts);
164               /* Remove NULL termination or the results are sad */
165               _vec_len(rv) = vec_len(rv)-1;
166               vec_free(ts);
167             } 
168           else 
169             {
170               if (complain) 
171                 clib_warning ("Undefined Variable Reference: %s\n", varname);
172               vec_append(rv, format(0, "UNSET "));
173               _vec_len(rv) = vec_len(rv)-1;
174                            
175             }
176           vec_free(varname);
177         }
178     }
179   vec_add1(rv, 0);
180   return(rv);
181 }
182
183 /*
184  * eval: takes a string, returns a vector.
185  * looks up $foobar in the variable table.
186  */
187 i8 *clib_macro_eval_dollar (macro_main_t *mm, i8 *s, i32 complain)
188 {
189   i8 *s2;
190   i8 *rv;
191
192   s2 = (i8 *)format (0, "$(%s)%c", s, 0);
193   rv = clib_macro_eval(mm, s2, complain);
194   vec_free (s2);
195   return (rv);
196 }
197
198 void clib_macro_add_builtin (macro_main_t *mm, char *name, void * eval_fn)
199 {
200   hash_set_mem(mm->the_builtin_eval_hash, name, (uword) eval_fn);
201 }
202
203 #ifdef CLIB_UNIX
204 static i8 *eval_hostname (macro_main_t *mm, i32 complain)
205 {
206   char tmp[128];
207   if (gethostname (tmp, sizeof(tmp)))
208     return ((i8 *) format (0, "gethostname-error%c", 0));
209   return ((i8 *) format (0, "%s%c", tmp, 0));
210 }
211 #endif
212
213 void clib_macro_init(macro_main_t * mm)
214 {
215   if (mm->the_builtin_eval_hash != 0)
216     {
217       clib_warning ("mm %llx already initialized", (u64)mm);
218       return;
219     }
220
221   mm->the_builtin_eval_hash = hash_create_string(0, sizeof(uword));
222   mm->the_value_table_hash = hash_create_string(0, sizeof(uword));
223     
224 #ifdef CLIB_UNIX
225   hash_set_mem(mm->the_builtin_eval_hash, "hostname",
226                (uword) eval_hostname);
227 #endif
228 }
229
230 void clib_macro_free(macro_main_t * mm)
231 {
232   hash_pair_t * p;
233   u8 ** strings_to_free = 0;
234   int i;
235
236   hash_free (mm->the_builtin_eval_hash);
237
238   hash_foreach_pair (p, mm->the_value_table_hash, 
239   ({
240     vec_add1 (strings_to_free, (u8 *) (p->key));
241     vec_add1 (strings_to_free, (u8 *) (p->value[0]));
242   }));
243
244   for (i = 0; i < vec_len (strings_to_free); i++)
245     vec_free (strings_to_free[i]);
246   vec_free (strings_to_free);
247   hash_free (mm->the_value_table_hash);
248 }