girara
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
template.c
Go to the documentation of this file.
1 /* See LICENSE file for license and copyright information */
2 
3 #include "template.h"
4 #include "utils.h"
5 #include "datastructures.h"
6 
7 #include <glib.h>
8 
9 G_DEFINE_TYPE(GiraraTemplate, girara_template, G_TYPE_OBJECT)
10 
11 
14 typedef struct private_s {
15  char* base;
16  GRegex* variable_regex;
18  girara_list_t* variables_in_base;
19  girara_list_t* variables;
20  bool valid;
21 } private_t;
22 
23 typedef struct private_s GiraraTemplatePrivate;
24 
25 #define GET_PRIVATE(obj) \
26  (G_TYPE_INSTANCE_GET_PRIVATE((obj), GIRARA_TYPE_TEMPLATE, private_t))
27 
31 typedef struct variable_s {
32  char* name;
33  char* value;
34 } variable_t;
35 
36 static variable_t*
37 new_variable(const char* name)
38 {
39  if (name == NULL) {
40  return NULL;
41  }
42 
43  variable_t* variable = g_try_malloc0(sizeof(variable_t));
44  if (variable == NULL) {
45  return NULL;
46  }
47 
48  variable->name = g_strdup(name);
49  variable->value = g_strdup("");
50 
51  return variable;
52 }
53 
54 static void
55 free_variable(void* data)
56 {
57  variable_t* variable = data;
58 
59  g_free(variable->name);
60  g_free(variable->value);
61 
62  variable->name = NULL;
63  variable->value = NULL;
64 
65  g_free(variable);
66 }
67 
68 static int
69 compare_variable_name(const void* data1, const void* data2)
70 {
71  const variable_t* variable = data1;
72  const char* name = data2;
73 
74  if (variable == NULL) {
75  return -1;
76  }
77 
78  return g_strcmp0(variable->name, name);
79 }
80 
81 /* Methods */
82 static void dispose(GObject* object);
83 static void finalize(GObject* object);
84 static void set_property(GObject* object, guint prop_id,
85  const GValue* value, GParamSpec* pspec);
86 static void get_property(GObject* object, guint prop_id, GValue* value,
87  GParamSpec* pspec);
88 static void base_changed(GiraraTemplate* object);
89 static void variable_changed(GiraraTemplate* object, const char* name);
90 static void template_changed(GiraraTemplate* object);
91 
92 /* Properties */
93 enum {
96 };
97 
98 /* Signals */
99 enum {
104 };
105 
106 static guint signals[LAST_SIGNAL] = { 0 };
107 
108 /* Class init */
109 static void
110 girara_template_class_init(GiraraTemplateClass* class)
111 {
112  /* add private members */
113  g_type_class_add_private(class, sizeof(private_t));
114 
115  /* overwrite methods */
116  GObjectClass* object_class = G_OBJECT_CLASS(class);
117  object_class->dispose = dispose;
118  object_class->finalize = finalize;
119  object_class->set_property = set_property;
120  object_class->get_property = get_property;
121 
122  class->base_changed = base_changed;
123  class->variable_changed = variable_changed;
124  class->changed = template_changed;
125 
126  /* add properties */
127  g_object_class_install_property(object_class, PROP_BASE,
128  g_param_spec_object("base",
129  "base template",
130  "String used as base for the template.",
132  G_PARAM_WRITABLE | G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
133 
134  /* add signals */
135  signals[BASE_CHANGED] = g_signal_new("base-changed",
137  G_SIGNAL_RUN_FIRST,
138  G_STRUCT_OFFSET(GiraraTemplateClass, base_changed),
139  NULL,
140  NULL,
141  NULL,
142  G_TYPE_NONE,
143  0);
144 
145  signals[VARIABLE_CHANGED] = g_signal_new("variable-changed",
147  G_SIGNAL_RUN_FIRST,
148  G_STRUCT_OFFSET(GiraraTemplateClass, variable_changed),
149  NULL,
150  NULL,
151  NULL,
152  G_TYPE_NONE,
153  1,
154  G_TYPE_STRING);
155 
156  signals[TEMPLATE_CHANGED] = g_signal_new("changed",
158  G_SIGNAL_RUN_FIRST,
159  G_STRUCT_OFFSET(GiraraTemplateClass, changed),
160  NULL,
161  NULL,
162  NULL,
163  G_TYPE_NONE,
164  0);
165 }
166 
167 /* GObject init */
168 static void
169 girara_template_init(GiraraTemplate* history)
170 {
171  GError* error = NULL;
172  GRegex* regex = g_regex_new("@([A-Za-z0-9][A-Za-z0-9_-]*)@",
173  G_REGEX_OPTIMIZE, 0, &error);
174  if (regex == NULL) {
175  girara_error("Failed to create regex: %s", error->message);
176  g_error_free(error);
177  }
178 
179  GRegex* check_regex = g_regex_new("^[A-Za-z0-9][A-Za-z0-9_-]*$",
180  G_REGEX_OPTIMIZE, 0, &error);
181  if (check_regex == NULL) {
182  girara_error("Failed to create regex: %s", error->message);
183  g_regex_unref(regex);
184  g_error_free(error);
185  }
186 
187  private_t* priv = GET_PRIVATE(history);
188  priv->base = g_strdup("");
189  priv->variable_regex = regex;
190  priv->variable_check_regex = check_regex;
191  priv->variables_in_base = girara_list_new2(g_free);
192  priv->variables = girara_list_new2(free_variable);
193  priv->valid = true;
194 }
195 
196 /* GObject dispose */
197 static void
198 dispose(GObject* object)
199 {
200  private_t* priv = GET_PRIVATE(object);
201 
202  g_regex_unref(priv->variable_regex);
203  g_regex_unref(priv->variable_check_regex);
204 
205  priv->variable_regex = NULL;
206  priv->variable_check_regex = NULL;
207 
208  G_OBJECT_CLASS(girara_template_parent_class)->dispose(object);
209 }
210 
211 /* GObject finalize */
212 static void
213 finalize(GObject* object)
214 {
215  private_t* priv = GET_PRIVATE(object);
216 
217  g_free(priv->base);
220 
221  priv->base = NULL;
222  priv->variables_in_base = NULL;
223  priv->variables = NULL;
224 
225  G_OBJECT_CLASS(girara_template_parent_class)->finalize(object);
226 }
227 
228 /* GObject set_property */
229 static void
230 set_property(GObject* obj, guint prop_id, const GValue* value,
231  GParamSpec* pspec)
232 {
233  GiraraTemplate* object = GIRARA_TEMPLATE(obj);
234 
235  switch (prop_id) {
236  case PROP_BASE: {
237  girara_template_set_base(object, g_value_get_string(value));
238  break;
239  }
240  default:
241  G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
242  }
243 }
244 
245 /* GObject get_property */
246 static void
247 get_property(GObject* obj, guint prop_id, GValue* value,
248  GParamSpec* pspec)
249 {
250  GiraraTemplate* object = GIRARA_TEMPLATE(obj);
251 
252  switch (prop_id) {
253  case PROP_BASE:
254  g_value_set_string(value, girara_template_get_base(object));
255  break;
256  default:
257  G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
258  }
259 }
260 
261 /* Object new */
262 GiraraTemplate*
263 girara_template_new(const char* base)
264 {
265  GObject* obj = g_object_new(GIRARA_TYPE_TEMPLATE, NULL);
266  g_return_val_if_fail(obj, NULL);
267  if (obj == NULL) {
268  return NULL;
269  }
270 
271  GiraraTemplate* object = GIRARA_TEMPLATE(obj);
272  if (base != NULL) {
273  girara_template_set_base(object, base);
274  }
275  return object;
276 }
277 
278 void
279 girara_template_set_base(GiraraTemplate* object, const char* base)
280 {
281  g_return_if_fail(GIRARA_IS_TEMPLATE(object));
282 
283  private_t* priv = GET_PRIVATE(object);
284  if (g_strcmp0(base, priv->base) != 0) {
285  g_free(priv->base);
286  priv->base = g_strdup(base != NULL ? base : "");
287 
288  g_signal_emit(object, signals[BASE_CHANGED], 0);
289  g_signal_emit(object, signals[TEMPLATE_CHANGED], 0);
290  }
291 }
292 
293 const char*
294 girara_template_get_base(GiraraTemplate* object)
295 {
296  g_return_val_if_fail(GIRARA_IS_TEMPLATE(object), NULL);
297 
298  private_t* priv = GET_PRIVATE(object);
299  return priv->base;
300 }
301 
302 static void
303 base_changed(GiraraTemplate* object)
304 {
305  private_t* priv = GET_PRIVATE(object);
307  priv->valid = true;
308 
309  GMatchInfo* match_info = NULL;
310  if (g_regex_match(priv->variable_regex, priv->base, 0, &match_info) == true) {
311  while (g_match_info_matches(match_info) == true) {
312  char* variable = g_match_info_fetch(match_info, 1);
313  char* found = girara_list_find(priv->variables_in_base,
314  (girara_compare_function_t)g_strcmp0, variable);
315 
316  if (priv->valid == true) {
317  if (girara_list_find(priv->variables, compare_variable_name,
318  variable) == NULL) {
319  priv->valid = false;
320  }
321  }
322 
323  if (found == NULL) {
324  girara_list_append(priv->variables_in_base, variable);
325  } else {
326  g_free(variable);
327  }
328 
329  g_match_info_next(match_info, NULL);
330  }
331  }
332  g_match_info_free(match_info);
333 }
334 
335 static void
336 variable_changed(GiraraTemplate* object, const char* GIRARA_UNUSED(name))
337 {
338  private_t* priv = GET_PRIVATE(object);
339  priv->valid = true;
340 
341  GIRARA_LIST_FOREACH(priv->variables_in_base, char*, iter, variable)
342  if (priv->valid == true &&
343  girara_list_find(priv->variables, compare_variable_name,
344  variable) == NULL) {
345  priv->valid = false;
346  }
347  GIRARA_LIST_FOREACH_END(priv->variables_in_base, char*, iter, variable);
348 }
349 
350 static void
351 template_changed(GiraraTemplate* GIRARA_UNUSED(object))
352 {
353 }
354 
355 girara_list_t*
357 {
358  g_return_val_if_fail(GIRARA_IS_TEMPLATE(object), NULL);
359 
360  private_t* priv = GET_PRIVATE(object);
361  return priv->variables_in_base;
362 }
363 
364 bool
365 girara_template_add_variable(GiraraTemplate* object, const char* name)
366 {
367  g_return_val_if_fail(GIRARA_IS_TEMPLATE(object), false);
368  g_return_val_if_fail(name != NULL, false);
369 
370  private_t* priv = GET_PRIVATE(object);
371 
372  if (g_regex_match(priv->variable_check_regex, name, 0, NULL) == FALSE) {
373  girara_debug("'%s' is not a valid variable name.", name);
374  return false;
375  }
376 
377  variable_t* variable = girara_list_find(priv->variables, compare_variable_name,
378  name);
379  if (variable != NULL) {
380  girara_debug("Variable '%s' already exists.", name);
381  return false;
382  }
383 
384  variable = new_variable(name);
385  if (variable == NULL) {
386  girara_debug("Could not create new variable.");
387  return false;
388  }
389 
390  girara_list_append(priv->variables, variable);
391  g_signal_emit(object, signals[VARIABLE_CHANGED], 0, name);
392  g_signal_emit(object, signals[TEMPLATE_CHANGED], 0);
393 
394  return true;
395 }
396 
397 void
398 girara_template_set_variable_value(GiraraTemplate* object, const char* name,
399  const char* value)
400 {
401  g_return_if_fail(GIRARA_IS_TEMPLATE(object));
402  g_return_if_fail(name != NULL);
403  g_return_if_fail(value != NULL);
404 
405  private_t* priv = GET_PRIVATE(object);
406 
407  variable_t* variable = girara_list_find(priv->variables, compare_variable_name,
408  name);
409  if (variable == NULL) {
410  girara_error("Variable '%s' does not exist.", name);
411  return;
412  }
413 
414  if (g_strcmp0(variable->value, value) != 0)
415  {
416  g_free(variable->value);
417  variable->value = g_strdup(value);
418 
419  g_signal_emit(object, signals[VARIABLE_CHANGED], 0, name);
420  g_signal_emit(object, signals[TEMPLATE_CHANGED], 0);
421  }
422 }
423 
424 static gboolean
425 eval_replace_cb(const GMatchInfo* info, GString* res, void* data)
426 {
427  girara_list_t* variables = data;
428 
429  char* name = g_match_info_fetch(info, 1);
430  variable_t* variable = girara_list_find(variables, compare_variable_name,
431  name);
432  g_return_val_if_fail(variable != NULL, TRUE);
433 
434  g_string_append(res, variable->value);
435  g_free(name);
436 
437  return FALSE;
438 }
439 
440 char*
441 girara_template_evaluate(GiraraTemplate* object)
442 {
443  g_return_val_if_fail(GIRARA_IS_TEMPLATE(object), NULL);
444 
445  private_t* priv = GET_PRIVATE(object);
446  if (priv->valid == false) {
447  girara_error("Base contains variables that do not have a value assigned.");
448  return NULL;
449  }
450 
451  return g_regex_replace_eval(priv->variable_regex, priv->base, -1, 0, 0,
452  eval_replace_cb, priv->variables, NULL);
453 }
bool valid
Definition: template.c:20
GRegex * variable_regex
Definition: template.c:16
GType girara_template_get_type(void)
#define girara_debug(...)
Definition: utils.h:119
void girara_list_append(girara_list_t *list, void *data)
GRegex * variable_check_regex
Definition: template.c:17
girara_list_t * girara_list_new2(girara_free_function_t gfree)
#define GET_PRIVATE(obj)
Definition: template.c:25
void girara_list_free(girara_list_t *list)
void * girara_list_find(girara_list_t *list, girara_compare_function_t compare, const void *data)
int(* girara_compare_function_t)(const void *data1, const void *data2)
Definition: types.h:134
GiraraTemplate * girara_template_new(const char *base)
Definition: template.c:263
#define GIRARA_UNUSED(x)
Definition: macros.h:21
void girara_template_set_variable_value(GiraraTemplate *object, const char *name, const char *value)
Definition: template.c:398
const char * girara_template_get_base(GiraraTemplate *object)
Definition: template.c:294
char * girara_template_evaluate(GiraraTemplate *object)
Definition: template.c:441
char * value
Definition: template.c:33
struct private_s GiraraTemplatePrivate
Definition: template.c:23
#define girara_error(...)
Definition: utils.h:134
#define GIRARA_IS_TEMPLATE(obj)
Definition: template.h:27
bool girara_template_add_variable(GiraraTemplate *object, const char *name)
Definition: template.c:365
void girara_template_set_base(GiraraTemplate *object, const char *base)
Definition: template.c:279
girara_list_t * girara_template_referenced_variables(GiraraTemplate *object)
Definition: template.c:356
char * base
Definition: template.c:15
girara_list_t * variables
Definition: template.c:19
void girara_list_clear(girara_list_t *list)
char * name
Definition: template.c:32
#define GIRARA_TYPE_TEMPLATE
Definition: template.h:21
#define GIRARA_TEMPLATE(obj)
Definition: template.h:23
#define GIRARA_LIST_FOREACH_END(list, type, iter, data)
#define GIRARA_LIST_FOREACH(list, type, iter, data)
girara_list_t * variables_in_base
Definition: template.c:18