Fix leaks and invalid memory accesses
[lttv.git] / lttv / lttv / attribute.c
1 /* This file is part of the Linux Trace Toolkit viewer
2 * Copyright (C) 2003-2004 Michel Dagenais
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License Version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
16 * MA 02111-1307, USA.
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <string.h>
24 #include <lttv/attribute.h>
25 #include <ltt/ltt.h>
26 #include <ltt/compiler.h>
27
28 typedef union _AttributeValue {
29 int dv_int;
30 unsigned dv_uint;
31 long dv_long;
32 unsigned long dv_ulong;
33 float dv_float;
34 double dv_double;
35 LttTime dv_time;
36 gpointer dv_pointer;
37 char *dv_string;
38 GObject *dv_gobject;
39 } AttributeValue;
40
41
42 typedef struct _Attribute {
43 LttvAttributeName name;
44 LttvAttributeType type;
45 AttributeValue value;
46 gboolean is_named;
47 } Attribute;
48
49
50 static __inline__ LttvAttributeValue address_of_value(LttvAttributeType t,
51 AttributeValue *v)
52 {
53 LttvAttributeValue va;
54
55 switch(t) {
56 case LTTV_INT: va.v_int = &v->dv_int; break;
57 case LTTV_UINT: va.v_uint = &v->dv_uint; break;
58 case LTTV_LONG: va.v_long = &v->dv_long; break;
59 case LTTV_ULONG: va.v_ulong = &v->dv_ulong; break;
60 case LTTV_FLOAT: va.v_float = &v->dv_float; break;
61 case LTTV_DOUBLE: va.v_double = &v->dv_double; break;
62 case LTTV_TIME: va.v_time = &v->dv_time; break;
63 case LTTV_POINTER: va.v_pointer = &v->dv_pointer; break;
64 case LTTV_STRING: va.v_string = &v->dv_string; break;
65 case LTTV_GOBJECT: va.v_gobject = &v->dv_gobject; break;
66 case LTTV_NONE: break;
67 }
68 return va;
69 }
70
71
72 AttributeValue init_value(LttvAttributeType t)
73 {
74 AttributeValue v;
75
76 switch(t) {
77 case LTTV_INT: v.dv_int = 0; break;
78 case LTTV_UINT: v.dv_uint = 0; break;
79 case LTTV_LONG: v.dv_long = 0; break;
80 case LTTV_ULONG: v.dv_ulong = 0; break;
81 case LTTV_FLOAT: v.dv_float = 0; break;
82 case LTTV_DOUBLE: v.dv_double = 0; break;
83 case LTTV_TIME: v.dv_time.tv_sec = 0; v.dv_time.tv_nsec = 0; break;
84 case LTTV_POINTER: v.dv_pointer = NULL; break;
85 case LTTV_STRING: v.dv_string = NULL; break;
86 case LTTV_GOBJECT: v.dv_gobject = NULL; break;
87 case LTTV_NONE: break;
88 }
89 return v;
90 }
91
92
93 unsigned int
94 lttv_attribute_get_number(LttvAttribute *self)
95 {
96 return self->attributes->len;
97 }
98
99 gboolean
100 lttv_attribute_named(LttvAttribute *self, gboolean *homogeneous)
101 {
102 *homogeneous = FALSE;
103 return TRUE;
104 }
105
106 LttvAttributeType
107 lttv_attribute_get(LttvAttribute *self, unsigned i, LttvAttributeName *name,
108 LttvAttributeValue *v, gboolean *is_named)
109 {
110 Attribute *a;
111
112 a = &g_array_index(self->attributes, Attribute, i);
113 *name = a->name;
114 *v = address_of_value(a->type, &(a->value));
115 *is_named = a->is_named;
116 return a->type;
117 }
118
119
120 LttvAttributeType
121 lttv_attribute_get_by_name(LttvAttribute *self, LttvAttributeName name,
122 LttvAttributeValue *v)
123 {
124 Attribute *a;
125
126 unsigned i;
127
128 gpointer p;
129
130 p = g_hash_table_lookup(self->names, GUINT_TO_POINTER(name));
131 if(p == NULL) return LTTV_NONE;
132
133 i = GPOINTER_TO_UINT(p);
134 i--;
135 a = &g_array_index(self->attributes, Attribute, i);
136 *v = address_of_value(a->type, &(a->value));
137 return a->type;
138 }
139
140
141 LttvAttributeValue
142 lttv_attribute_add(LttvAttribute *self, LttvAttributeName name,
143 LttvAttributeType t)
144 {
145 unsigned int i;
146
147 Attribute a, *pa;
148
149 i = GPOINTER_TO_UINT(g_hash_table_lookup(self->names, GUINT_TO_POINTER(name)));
150 if(i != 0) g_error("duplicate entry in attribute table");
151
152 a.name = name;
153 a.is_named = 1;
154 a.type = t;
155 a.value = init_value(t);
156 g_array_append_val(self->attributes, a);
157 i = self->attributes->len - 1;
158 pa = &g_array_index(self->attributes, Attribute, i);
159 g_hash_table_insert(self->names, GUINT_TO_POINTER(name),
160 GUINT_TO_POINTER(i + 1));
161 return address_of_value(t, &(pa->value));
162 }
163
164 LttvAttributeValue
165 lttv_attribute_add_unnamed(LttvAttribute *self, LttvAttributeName name,
166 LttvAttributeType t)
167 {
168 unsigned int i;
169
170 Attribute a, *pa;
171
172 i = GPOINTER_TO_UINT(g_hash_table_lookup(self->names, GUINT_TO_POINTER(name)));
173 if(i != 0) g_error("duplicate entry in attribute table");
174
175 a.name = name;
176 a.is_named = 0;
177 a.type = t;
178 a.value = init_value(t);
179 g_array_append_val(self->attributes, a);
180 i = self->attributes->len - 1;
181 pa = &g_array_index(self->attributes, Attribute, i);
182 g_hash_table_insert(self->names, GUINT_TO_POINTER(name),
183 GUINT_TO_POINTER(i + 1));
184 return address_of_value(t, &(pa->value));
185 }
186
187
188 /* Remove an attribute */
189
190 void
191 lttv_attribute_remove(LttvAttribute *self, unsigned i)
192 {
193 Attribute *a;
194
195 a = &g_array_index(self->attributes, Attribute, i);
196
197 /* If the element is a gobject, unreference it. */
198 if(a->type == LTTV_GOBJECT && a->value.dv_gobject != NULL)
199 g_object_unref(a->value.dv_gobject);
200
201 /* Remove the array element and its entry in the name index */
202
203 g_hash_table_remove(self->names, GUINT_TO_POINTER(a->name));
204 g_array_remove_index_fast(self->attributes, i);
205
206 /* The element used to replace the removed element has its index entry
207 all wrong now. Reinsert it with its new position. */
208
209 if(likely(self->attributes->len != i)){
210 g_hash_table_remove(self->names, GUINT_TO_POINTER(a->name));
211 g_hash_table_insert(self->names, GUINT_TO_POINTER(a->name), GUINT_TO_POINTER(i + 1));
212 }
213 }
214
215 void
216 lttv_attribute_remove_by_name(LttvAttribute *self, LttvAttributeName name)
217 {
218 unsigned int i;
219
220 i = GPOINTER_TO_UINT(g_hash_table_lookup(self->names, GUINT_TO_POINTER(name)));
221 if(unlikely(i == 0)) g_error("remove by name non existent attribute");
222
223 lttv_attribute_remove(self, i - 1);
224 }
225
226 /* Create an empty iattribute object and add it as an attribute under the
227 specified name, or return an existing iattribute attribute. If an
228 attribute of that name already exists but is not a GObject supporting the
229 iattribute interface, return NULL. */
230
231 /*CHECK*/LttvAttribute*
232 lttv_attribute_find_subdir(LttvAttribute *self, LttvAttributeName name)
233 {
234 unsigned int i;
235
236 Attribute a;
237
238 LttvAttribute *new;
239
240 i = GPOINTER_TO_UINT(g_hash_table_lookup(self->names, GUINT_TO_POINTER(name)));
241 if(likely(i != 0)) {
242 a = g_array_index(self->attributes, Attribute, i - 1);
243 if(likely(a.type == LTTV_GOBJECT && LTTV_IS_IATTRIBUTE(a.value.dv_gobject))) {
244 return LTTV_ATTRIBUTE(a.value.dv_gobject);
245 }
246 else return NULL;
247 }
248 new = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL);
249 *(lttv_attribute_add(self, name, LTTV_GOBJECT).v_gobject) = G_OBJECT(new);
250 return (LttvAttribute *)new;
251 }
252
253 /*CHECK*/LttvAttribute*
254 lttv_attribute_find_subdir_unnamed(LttvAttribute *self, LttvAttributeName name)
255 {
256 unsigned int i;
257
258 Attribute a;
259
260 LttvAttribute *new;
261
262 i = GPOINTER_TO_UINT(g_hash_table_lookup(self->names, GUINT_TO_POINTER(name)));
263 if(likely(i != 0)) {
264 a = g_array_index(self->attributes, Attribute, i - 1);
265 if(likely(a.type == LTTV_GOBJECT && LTTV_IS_IATTRIBUTE(a.value.dv_gobject))) {
266 return LTTV_ATTRIBUTE(a.value.dv_gobject);
267 }
268 else return NULL;
269 }
270 new = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL);
271 *(lttv_attribute_add_unnamed(self, name, LTTV_GOBJECT).v_gobject)
272 = G_OBJECT(new);
273 return (LttvAttribute *)new;
274 }
275
276 gboolean
277 lttv_attribute_find(LttvAttribute *self, LttvAttributeName name,
278 LttvAttributeType t, LttvAttributeValue *v)
279 {
280 unsigned int i;
281
282 Attribute *a;
283
284 i = GPOINTER_TO_UINT(g_hash_table_lookup(self->names, GUINT_TO_POINTER(name)));
285 if(likely(i != 0)) {
286 a = &g_array_index(self->attributes, Attribute, i - 1);
287 if(unlikely(a->type != t)) return FALSE;
288 *v = address_of_value(t, &(a->value));
289 return TRUE;
290 }
291
292 *v = lttv_attribute_add(self, name, t);
293 return TRUE;
294 }
295
296 gboolean
297 lttv_attribute_find_unnamed(LttvAttribute *self, LttvAttributeName name,
298 LttvAttributeType t, LttvAttributeValue *v)
299 {
300 unsigned i;
301
302 Attribute *a;
303
304 i = GPOINTER_TO_UINT(g_hash_table_lookup(self->names, GUINT_TO_POINTER(name)));
305 if(likely(i != 0)) {
306 a = &g_array_index(self->attributes, Attribute, i - 1);
307 if(unlikely(a->type != t)) return FALSE;
308 *v = address_of_value(t, &(a->value));
309 return TRUE;
310 }
311
312 *v = lttv_attribute_add_unnamed(self, name, t);
313 return TRUE;
314 }
315
316
317 /*void lttv_attribute_recursive_free(LttvAttribute *self)
318 {
319 int i, nb;
320
321 Attribute *a;
322
323 nb = self->attributes->len;
324
325 for(i = 0 ; i < nb ; i++) {
326 a = &g_array_index(self->attributes, Attribute, i);
327 if(a->type == LTTV_GOBJECT && LTTV_IS_ATTRIBUTE(a->value.dv_gobject)) {
328 lttv_attribute_recursive_free((LttvAttribute *)(a->value.dv_gobject));
329 }
330 }
331 g_object_unref(self);
332 }*/
333
334
335 void lttv_attribute_recursive_add(LttvAttribute *dest, LttvAttribute *src)
336 {
337 int i, nb;
338
339 Attribute *a;
340
341 LttvAttributeValue value;
342 gboolean retval;
343
344 nb = src->attributes->len;
345
346 for(i = 0 ; i < nb ; i++) {
347 a = &g_array_index(src->attributes, Attribute, i);
348 if(a->type == LTTV_GOBJECT && LTTV_IS_ATTRIBUTE(a->value.dv_gobject)) {
349 if(a->is_named)
350 lttv_attribute_recursive_add(
351 /*CHECK*/(LttvAttribute *)lttv_attribute_find_subdir(dest, a->name),
352 (LttvAttribute *)(a->value.dv_gobject));
353 else
354 lttv_attribute_recursive_add(
355 /*CHECK*/(LttvAttribute *)lttv_attribute_find_subdir_unnamed(
356 dest, a->name), (LttvAttribute *)(a->value.dv_gobject));
357 }
358 else {
359 if(a->is_named) {
360 retval= lttv_attribute_find(dest, a->name, a->type, &value);
361 g_assert(retval);
362 }
363 else {
364 retval= lttv_attribute_find_unnamed(dest, a->name, a->type, &value);
365 g_assert(retval);
366 }
367 switch(a->type) {
368 case LTTV_INT:
369 *value.v_int += a->value.dv_int;
370 break;
371 case LTTV_UINT:
372 *value.v_uint += a->value.dv_uint;
373 break;
374 case LTTV_LONG:
375 *value.v_long += a->value.dv_long;
376 break;
377 case LTTV_ULONG:
378 *value.v_ulong += a->value.dv_ulong;
379 break;
380 case LTTV_FLOAT:
381 *value.v_float += a->value.dv_float;
382 break;
383 case LTTV_DOUBLE:
384 *value.v_double += a->value.dv_double;
385 break;
386 case LTTV_TIME:
387 *value.v_time = ltt_time_add(*value.v_time, a->value.dv_time);
388 break;
389 case LTTV_POINTER:
390 break;
391 case LTTV_STRING:
392 break;
393 case LTTV_GOBJECT:
394 break;
395 case LTTV_NONE:
396 break;
397 }
398 }
399 }
400 }
401
402
403 static void
404 print_indent(FILE *fp, int pos)
405 {
406 int i;
407
408 for(i = 0 ; i < pos ; i++) putc(' ', fp);
409 }
410
411
412 void
413 lttv_attribute_write_xml(LttvAttribute *self, FILE *fp, int pos, int indent)
414 {
415 int i, nb;
416
417 Attribute *a;
418
419 nb = self->attributes->len;
420
421 fprintf(fp,"<ATTRS>\n");
422 for(i = 0 ; i < nb ; i++) {
423 a = &g_array_index(self->attributes, Attribute, i);
424 print_indent(fp, pos);
425 fprintf(fp, "<ATTR NAME=\"%s\" ", g_quark_to_string(a->name));
426 if(a->type == LTTV_GOBJECT && LTTV_IS_ATTRIBUTE(a->value.dv_gobject)) {
427 fprintf(fp, "TYPE=ATTRS>");
428 lttv_attribute_write_xml((LttvAttribute *)(a->value.dv_gobject), fp,
429 pos + indent, indent);
430 }
431 else {
432 switch(a->type) {
433 case LTTV_INT:
434 fprintf(fp, "TYPE=INT VALUE=%d/>\n", a->value.dv_int);
435 break;
436 case LTTV_UINT:
437 fprintf(fp, "TYPE=UINT VALUE=%u/>\n", a->value.dv_uint);
438 break;
439 case LTTV_LONG:
440 fprintf(fp, "TYPE=LONG VALUE=%ld/>\n", a->value.dv_long);
441 break;
442 case LTTV_ULONG:
443 fprintf(fp, "TYPE=ULONG VALUE=%lu/>\n", a->value.dv_ulong);
444 break;
445 case LTTV_FLOAT:
446 fprintf(fp, "TYPE=FLOAT VALUE=%f/>\n", a->value.dv_float);
447 break;
448 case LTTV_DOUBLE:
449 fprintf(fp, "TYPE=DOUBLE VALUE=%f/>\n", a->value.dv_double);
450 break;
451 case LTTV_TIME:
452 fprintf(fp, "TYPE=TIME SEC=%lu NSEC=%lu/>\n", a->value.dv_time.tv_sec,
453 a->value.dv_time.tv_nsec);
454 break;
455 case LTTV_POINTER:
456 fprintf(fp, "TYPE=POINTER VALUE=%p/>\n", a->value.dv_pointer);
457 break;
458 case LTTV_STRING:
459 fprintf(fp, "TYPE=STRING VALUE=\"%s\"/>\n", a->value.dv_string);
460 break;
461 case LTTV_GOBJECT:
462 fprintf(fp, "TYPE=GOBJECT VALUE=%p/>\n", a->value.dv_gobject);
463 break;
464 case LTTV_NONE:
465 fprintf(fp, "TYPE=NONE/>\n");
466 break;
467 }
468 }
469 }
470 print_indent(fp, pos);
471 fprintf(fp,"</ATTRS>\n");
472 }
473
474
475 void
476 lttv_attribute_read_xml(LttvAttribute *self, FILE *fp)
477 {
478 int res;
479
480 char buffer[256], type[10];
481
482 LttvAttributeName name;
483
484 LttvAttributeValue value;
485
486 LttvAttribute *subtree;
487
488 fscanf(fp,"<ATTRS>");
489 while(1) {
490 res = fscanf(fp, "<ATTR NAME=\"%256[^\"]\" TYPE=%10[^ >]", buffer, type);
491 g_assert(res == 2);
492 name = g_quark_from_string(buffer);
493 if(strcmp(type, "ATTRS") == 0) {
494 fscanf(fp, ">");
495 subtree = lttv_attribute_find_subdir(self, name);
496 lttv_attribute_read_xml(subtree, fp);
497 }
498 else if(strcmp(type, "INT") == 0) {
499 value = lttv_attribute_add(self, name, LTTV_INT);
500 res = fscanf(fp, " VALUE=%d/>", value.v_int);
501 g_assert(res == 1);
502 }
503 else if(strcmp(type, "UINT") == 0) {
504 value = lttv_attribute_add(self, name, LTTV_UINT);
505 res = fscanf(fp, " VALUE=%u/>", value.v_uint);
506 g_assert(res == 1);
507 }
508 else if(strcmp(type, "LONG") == 0) {
509 value = lttv_attribute_add(self, name, LTTV_LONG);
510 res = fscanf(fp, " VALUE=%ld/>", value.v_long);
511 g_assert(res == 1);
512 }
513 else if(strcmp(type, "ULONG") == 0) {
514 value = lttv_attribute_add(self, name, LTTV_ULONG);
515 res = fscanf(fp, " VALUE=%lu/>", value.v_ulong);
516 g_assert(res == 1);
517 }
518 else if(strcmp(type, "FLOAT") == 0) {
519 float d;
520 value = lttv_attribute_add(self, name, LTTV_FLOAT);
521 res = fscanf(fp, " VALUE=%f/>", &d);
522 *(value.v_float) = d;
523 g_assert(res == 1);
524 }
525 else if(strcmp(type, "DOUBLE") == 0) {
526 value = lttv_attribute_add(self, name, LTTV_DOUBLE);
527 res = fscanf(fp, " VALUE=%lf/>", value.v_double);
528 g_assert(res == 1);
529 }
530 else if(strcmp(type, "TIME") == 0) {
531 value = lttv_attribute_add(self, name, LTTV_TIME);
532 res = fscanf(fp, " SEC=%lu NSEC=%lu/>", &(value.v_time->tv_sec),
533 &(value.v_time->tv_nsec));
534 g_assert(res == 2);
535 }
536 else if(strcmp(type, "POINTER") == 0) {
537 value = lttv_attribute_add(self, name, LTTV_POINTER);
538 res = fscanf(fp, " VALUE=%p/>", value.v_pointer);
539 g_error("Cannot read a pointer");
540 }
541 else if(strcmp(type, "STRING") == 0) {
542 value = lttv_attribute_add(self, name, LTTV_STRING);
543 res = fscanf(fp, " VALUE=\"%256[^\"]\"/>", buffer);
544 *(value.v_string) = g_strdup(buffer);
545 g_assert(res == 1);
546 }
547 else if(strcmp(type, "GOBJECT") == 0) {
548 value = lttv_attribute_add(self, name, LTTV_GOBJECT);
549 res = fscanf(fp, " VALUE=%p/>", value.v_gobject);
550 g_error("Cannot read a pointer");
551 }
552 else if(strcmp(type, "NONE") == 0) {
553 value = lttv_attribute_add(self, name, LTTV_NONE);
554 fscanf(fp, "/>");
555 }
556 else g_error("Unknown type to read");
557 }
558 fscanf(fp,"</ATTRS>");
559 }
560
561 static LttvAttribute *
562 new_attribute (LttvAttribute *self)
563 {
564 return g_object_new(LTTV_ATTRIBUTE_TYPE, NULL);
565 }
566
567
568 static void
569 attribute_interface_init (gpointer g_iface, gpointer iface_data)
570 {
571 LttvIAttributeClass *klass = (LttvIAttributeClass *)g_iface;
572
573 klass->new_attribute = (LttvIAttribute* (*) (LttvIAttribute *self))
574 new_attribute;
575
576 klass->get_number = (unsigned int (*) (LttvIAttribute *self))
577 lttv_attribute_get_number;
578
579 klass->named = (gboolean (*) (LttvIAttribute *self, gboolean *homogeneous))
580 lttv_attribute_named;
581
582 klass->get = (LttvAttributeType (*) (LttvIAttribute *self, unsigned i,
583 LttvAttributeName *name, LttvAttributeValue *v, gboolean *is_named))
584 lttv_attribute_get;
585
586 klass->get_by_name = (LttvAttributeType (*) (LttvIAttribute *self,
587 LttvAttributeName name, LttvAttributeValue *v))
588 lttv_attribute_get_by_name;
589
590 klass->add = (LttvAttributeValue (*) (LttvIAttribute *self,
591 LttvAttributeName name, LttvAttributeType t)) lttv_attribute_add;
592
593 klass->add_unnamed = (LttvAttributeValue (*) (LttvIAttribute *self,
594 LttvAttributeName name, LttvAttributeType t)) lttv_attribute_add_unnamed;
595
596 klass->remove = (void (*) (LttvIAttribute *self, unsigned i))
597 lttv_attribute_remove;
598
599 klass->remove_by_name = (void (*) (LttvIAttribute *self,
600 LttvAttributeName name)) lttv_attribute_remove_by_name;
601
602 klass->find_subdir = (LttvIAttribute* (*) (LttvIAttribute *self,
603 LttvAttributeName name)) lttv_attribute_find_subdir;
604
605 klass->find_subdir = (LttvIAttribute* (*) (LttvIAttribute *self,
606 LttvAttributeName name)) lttv_attribute_find_subdir_unnamed;
607 }
608
609 static void
610 attribute_instance_init (GTypeInstance *instance, gpointer g_class)
611 {
612 LttvAttribute *self = (LttvAttribute *)instance;
613 self->names = g_hash_table_new(g_direct_hash,
614 g_direct_equal);
615 self->attributes = g_array_new(FALSE, FALSE, sizeof(Attribute));
616 }
617
618
619 static void
620 attribute_finalize (LttvAttribute *self)
621 {
622 guint i;
623 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "attribute_finalize()");
624
625 for(i=0;i<self->attributes->len;i++) {
626 lttv_attribute_remove(self, i);
627 }
628
629 g_hash_table_destroy(self->names);
630 g_array_free(self->attributes, TRUE);
631 }
632
633
634 static void
635 attribute_class_init (LttvAttributeClass *klass)
636 {
637 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
638
639 gobject_class->finalize = (void (*)(GObject *self))attribute_finalize;
640 }
641
642 GType
643 lttv_attribute_get_type (void)
644 {
645 static GType type = 0;
646 if (type == 0) {
647 static const GTypeInfo info = {
648 sizeof (LttvAttributeClass),
649 NULL, /* base_init */
650 NULL, /* base_finalize */
651 (GClassInitFunc) attribute_class_init, /* class_init */
652 NULL, /* class_finalize */
653 NULL, /* class_data */
654 sizeof (LttvAttribute),
655 0, /* n_preallocs */
656 (GInstanceInitFunc) attribute_instance_init, /* instance_init */
657 NULL /* value handling */
658 };
659
660 static const GInterfaceInfo iattribute_info = {
661 (GInterfaceInitFunc) attribute_interface_init, /* interface_init */
662 NULL, /* interface_finalize */
663 NULL /* interface_data */
664 };
665
666 type = g_type_register_static (G_TYPE_OBJECT, "LttvAttributeType", &info,
667 0);
668 g_type_add_interface_static (type, LTTV_IATTRIBUTE_TYPE, &iattribute_info);
669 }
670 return type;
671 }
672
673
This page took 0.061966 seconds and 4 git commands to generate.