Fix ctf writer lints
[babeltrace.git] / formats / ctf / writer / writer.c
1 /*
2 * writer.c
3 *
4 * Babeltrace CTF Writer
5 *
6 * Copyright 2013 EfficiOS Inc.
7 *
8 * Author: Jérémie Galarneau <jeremie.galarneau@efficios.com>
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a copy
11 * of this software and associated documentation files (the "Software"), to deal
12 * in the Software without restriction, including without limitation the rights
13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included in
18 * all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * SOFTWARE.
27 */
28
29 #include <babeltrace/ctf-writer/writer.h>
30 #include <babeltrace/ctf-writer/clock.h>
31 #include <babeltrace/ctf-writer/clock-internal.h>
32 #include <babeltrace/ctf-writer/writer-internal.h>
33 #include <babeltrace/ctf-writer/event-types-internal.h>
34 #include <babeltrace/ctf-writer/event-fields-internal.h>
35 #include <babeltrace/ctf-writer/functor-internal.h>
36 #include <babeltrace/ctf-writer/stream-internal.h>
37 #include <babeltrace/ctf-writer/stream.h>
38 #include <babeltrace/compiler.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <sys/stat.h>
42 #include <errno.h>
43 #include <unistd.h>
44 #include <fcntl.h>
45 #include <inttypes.h>
46
47 #define DEFAULT_IDENTIFIER_SIZE 128
48 #define DEFAULT_METADATA_STRING_SIZE 4096
49
50 static
51 void environment_variable_destroy(struct environment_variable *var);
52 static
53 void bt_ctf_writer_destroy(struct bt_ctf_ref *ref);
54 static
55 int init_trace_packet_header(struct bt_ctf_writer *writer);
56 static
57 int create_stream_file(struct bt_ctf_writer *writer,
58 struct bt_ctf_stream *stream);
59 static
60 void stream_flush_cb(struct bt_ctf_stream *stream,
61 struct bt_ctf_writer *writer);
62
63 static
64 const char * const reserved_keywords_str[] = {"align", "callsite",
65 "const", "char", "clock", "double", "enum", "env", "event",
66 "floating_point", "float", "integer", "int", "long", "short", "signed",
67 "stream", "string", "struct", "trace", "typealias", "typedef",
68 "unsigned", "variant", "void" "_Bool", "_Complex", "_Imaginary"};
69
70 static
71 const unsigned int field_type_aliases_alignments[] = {
72 [FIELD_TYPE_ALIAS_UINT5_T] = 1,
73 [FIELD_TYPE_ALIAS_UINT8_T ... FIELD_TYPE_ALIAS_UINT16_T] = 8,
74 [FIELD_TYPE_ALIAS_UINT27_T] = 1,
75 [FIELD_TYPE_ALIAS_UINT32_T ... FIELD_TYPE_ALIAS_UINT64_T] = 8,
76 };
77
78 static
79 const unsigned int field_type_aliases_sizes[] = {
80 [FIELD_TYPE_ALIAS_UINT5_T] = 5,
81 [FIELD_TYPE_ALIAS_UINT8_T] = 8,
82 [FIELD_TYPE_ALIAS_UINT16_T] = 16,
83 [FIELD_TYPE_ALIAS_UINT27_T] = 27,
84 [FIELD_TYPE_ALIAS_UINT32_T] = 32,
85 [FIELD_TYPE_ALIAS_UINT64_T] = 64,
86 };
87
88 static GHashTable *reserved_keywords_set;
89 static int init_done;
90 static int global_data_refcount;
91
92 struct bt_ctf_writer *bt_ctf_writer_create(const char *path)
93 {
94 struct bt_ctf_writer *writer = NULL;
95
96 if (!path) {
97 goto error;
98 }
99
100 writer = g_new0(struct bt_ctf_writer, 1);
101 if (!writer) {
102 goto error;
103 }
104
105 bt_ctf_writer_set_byte_order(writer, BT_CTF_BYTE_ORDER_NATIVE);
106 bt_ctf_ref_init(&writer->ref_count);
107 writer->path = g_string_new(path);
108 if (!writer->path) {
109 goto error_destroy;
110 }
111
112 /* Create trace directory if necessary and open a metadata file */
113 if (g_mkdir_with_parents(path, S_IRWXU | S_IRWXG)) {
114 perror("g_mkdir_with_parents");
115 goto error_destroy;
116 }
117
118 writer->trace_dir_fd = open(path, O_RDONLY | O_DIRECTORY,
119 S_IRWXU | S_IRWXG);
120 if (writer->trace_dir_fd < 0) {
121 perror("open");
122 goto error_destroy;
123 }
124
125 writer->metadata_fd = openat(writer->trace_dir_fd, "metadata",
126 O_WRONLY | O_CREAT | O_TRUNC,
127 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
128 writer->environment = g_ptr_array_new_with_free_func(
129 (GDestroyNotify)environment_variable_destroy);
130 writer->clocks = g_ptr_array_new_with_free_func(
131 (GDestroyNotify)bt_ctf_clock_put);
132 writer->streams = g_ptr_array_new_with_free_func(
133 (GDestroyNotify)bt_ctf_stream_put);
134 writer->stream_classes = g_ptr_array_new_with_free_func(
135 (GDestroyNotify)bt_ctf_stream_class_put);
136 if (!writer->environment || !writer->clocks ||
137 !writer->stream_classes || !writer->streams) {
138 goto error_destroy;
139 }
140
141 /* Generate a trace UUID */
142 uuid_generate(writer->uuid);
143 if (init_trace_packet_header(writer)) {
144 goto error_destroy;
145 }
146
147 return writer;
148 error_destroy:
149 unlinkat(writer->trace_dir_fd, "metadata", 0);
150 bt_ctf_writer_destroy(&writer->ref_count);
151 writer = NULL;
152 error:
153 return writer;
154 }
155
156 void bt_ctf_writer_destroy(struct bt_ctf_ref *ref)
157 {
158 struct bt_ctf_writer *writer;
159
160 if (!ref) {
161 return;
162 }
163
164 writer = container_of(ref, struct bt_ctf_writer, ref_count);
165 bt_ctf_writer_flush_metadata(writer);
166 if (writer->path) {
167 g_string_free(writer->path, TRUE);
168 }
169
170 if (writer->trace_dir_fd > 0) {
171 if (close(writer->trace_dir_fd)) {
172 perror("close");
173 abort();
174 }
175 }
176
177 if (writer->metadata_fd > 0) {
178 if (close(writer->metadata_fd)) {
179 perror("close");
180 abort();
181 }
182 }
183
184 if (writer->environment) {
185 g_ptr_array_free(writer->environment, TRUE);
186 }
187
188 if (writer->clocks) {
189 g_ptr_array_free(writer->clocks, TRUE);
190 }
191
192 if (writer->streams) {
193 g_ptr_array_free(writer->streams, TRUE);
194 }
195
196 if (writer->stream_classes) {
197 g_ptr_array_free(writer->stream_classes, TRUE);
198 }
199
200 bt_ctf_field_type_put(writer->trace_packet_header_type);
201 bt_ctf_field_put(writer->trace_packet_header);
202 g_free(writer);
203 }
204
205 struct bt_ctf_stream *bt_ctf_writer_create_stream(struct bt_ctf_writer *writer,
206 struct bt_ctf_stream_class *stream_class)
207 {
208 int ret;
209 int stream_class_found = 0;
210 size_t i;
211 int stream_fd;
212 struct bt_ctf_stream *stream = NULL;
213
214 if (!writer || !stream_class) {
215 goto error;
216 }
217
218 stream = bt_ctf_stream_create(stream_class);
219 if (!stream) {
220 goto error;
221 }
222
223 stream_fd = create_stream_file(writer, stream);
224 if (stream_fd < 0 || bt_ctf_stream_set_fd(stream, stream_fd)) {
225 goto error;
226 }
227
228 bt_ctf_stream_set_flush_callback(stream, (flush_func)stream_flush_cb,
229 writer);
230 ret = bt_ctf_stream_class_set_byte_order(stream->stream_class,
231 writer->byte_order == LITTLE_ENDIAN ?
232 BT_CTF_BYTE_ORDER_LITTLE_ENDIAN : BT_CTF_BYTE_ORDER_BIG_ENDIAN);
233 if (ret) {
234 goto error;
235 }
236
237 for (i = 0; i < writer->stream_classes->len; i++) {
238 if (writer->stream_classes->pdata[i] == stream->stream_class) {
239 stream_class_found = 1;
240 }
241 }
242
243 if (!stream_class_found) {
244 if (bt_ctf_stream_class_set_id(stream->stream_class,
245 writer->next_stream_id++)) {
246 goto error;
247 }
248
249 bt_ctf_stream_class_get(stream->stream_class);
250 g_ptr_array_add(writer->stream_classes, stream->stream_class);
251 }
252
253 bt_ctf_stream_get(stream);
254 g_ptr_array_add(writer->streams, stream);
255 writer->frozen = 1;
256 return stream;
257 error:
258 bt_ctf_stream_put(stream);
259 return NULL;
260 }
261
262 int bt_ctf_writer_add_environment_field(struct bt_ctf_writer *writer,
263 const char *name,
264 const char *value)
265 {
266 struct environment_variable *var = NULL;
267 char *escaped_value = NULL;
268 int ret = 0;
269
270 if (!writer || !name || !value || validate_identifier(name)) {
271 ret = -1;
272 goto error;
273 }
274
275 if (strchr(name, ' ')) {
276 ret = -1;
277 goto error;
278 }
279
280 var = g_new0(struct environment_variable, 1);
281 if (!var) {
282 ret = -1;
283 goto error;
284 }
285
286 escaped_value = g_strescape(value, NULL);
287 if (!escaped_value) {
288 ret = -1;
289 goto error;
290 }
291
292 var->name = g_string_new(name);
293 var->value = g_string_new(escaped_value);
294 g_free(escaped_value);
295 if (!var->name || !var->value) {
296 ret = -1;
297 goto error;
298 }
299
300 g_ptr_array_add(writer->environment, var);
301 return ret;
302
303 error:
304 if (var && var->name) {
305 g_string_free(var->name, TRUE);
306 }
307
308 if (var && var->value) {
309 g_string_free(var->value, TRUE);
310 }
311
312 g_free(var);
313 return ret;
314 }
315
316 int bt_ctf_writer_add_clock(struct bt_ctf_writer *writer,
317 struct bt_ctf_clock *clock)
318 {
319 int ret = 0;
320 struct search_query query = { .value = clock, .found = 0 };
321
322 if (!writer || !clock) {
323 ret = -1;
324 goto end;
325 }
326
327 /* Check for duplicate clocks */
328 g_ptr_array_foreach(writer->clocks, value_exists, &query);
329 if (query.found) {
330 ret = -1;
331 goto end;
332 }
333
334 bt_ctf_clock_get(clock);
335 g_ptr_array_add(writer->clocks, clock);
336 end:
337 return ret;
338 }
339
340 BT_HIDDEN
341 const char *get_byte_order_string(int byte_order)
342 {
343 const char *string;
344
345 switch (byte_order) {
346 case LITTLE_ENDIAN:
347 string = "le";
348 break;
349 case BIG_ENDIAN:
350 string = "be";
351 break;
352 default:
353 string = "unknown";
354 break;
355 }
356
357 return string;
358 }
359
360 static
361 void append_trace_metadata(struct bt_ctf_writer *writer,
362 struct metadata_context *context)
363 {
364 unsigned char *uuid = writer->uuid;
365 int ret;
366
367 g_string_append(context->string, "trace {\n");
368
369 g_string_append(context->string, "\tmajor = 1;\n");
370 g_string_append(context->string, "\tminor = 8;\n");
371
372 g_string_append_printf(context->string,
373 "\tuuid = \"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\";\n",
374 uuid[0], uuid[1], uuid[2], uuid[3],
375 uuid[4], uuid[5], uuid[6], uuid[7],
376 uuid[8], uuid[9], uuid[10], uuid[11],
377 uuid[12], uuid[13], uuid[14], uuid[15]);
378 g_string_append_printf(context->string, "\tbyte_order = %s;\n",
379 get_byte_order_string(writer->byte_order));
380
381 g_string_append(context->string, "\tpacket.header := ");
382 context->current_indentation_level++;
383 g_string_assign(context->field_name, "");
384 ret = bt_ctf_field_type_serialize(writer->trace_packet_header_type,
385 context);
386 assert(!ret);
387 context->current_indentation_level--;
388
389 g_string_append(context->string, ";\n};\n\n");
390 }
391
392 static
393 void append_env_field_metadata(struct environment_variable *var,
394 struct metadata_context *context)
395 {
396 g_string_append_printf(context->string, "\t%s = \"%s\";\n",
397 var->name->str, var->value->str);
398 }
399
400 static
401 void append_env_metadata(struct bt_ctf_writer *writer,
402 struct metadata_context *context)
403 {
404 if (writer->environment->len == 0) {
405 return;
406 }
407
408 g_string_append(context->string, "env {\n");
409 g_ptr_array_foreach(writer->environment,
410 (GFunc)append_env_field_metadata, context);
411 g_string_append(context->string, "};\n\n");
412 }
413
414 char *bt_ctf_writer_get_metadata_string(struct bt_ctf_writer *writer)
415 {
416 char *metadata = NULL;
417 struct metadata_context *context = NULL;
418 int err = 0;
419 size_t i;
420
421 if (!writer) {
422 goto end;
423 }
424
425 context = g_new0(struct metadata_context, 1);
426 if (!context) {
427 goto end;
428 }
429
430 context->field_name = g_string_sized_new(DEFAULT_IDENTIFIER_SIZE);
431 context->string = g_string_sized_new(DEFAULT_METADATA_STRING_SIZE);
432 g_string_append(context->string, "/* CTF 1.8 */\n\n");
433 append_trace_metadata(writer, context);
434 append_env_metadata(writer, context);
435 g_ptr_array_foreach(writer->clocks,
436 (GFunc)bt_ctf_clock_serialize, context);
437
438 for (i = 0; i < writer->stream_classes->len; i++) {
439 err = bt_ctf_stream_class_serialize(
440 writer->stream_classes->pdata[i], context);
441 if (err) {
442 goto error;
443 }
444 }
445
446 metadata = context->string->str;
447 error:
448 g_string_free(context->string, err ? TRUE : FALSE);
449 g_string_free(context->field_name, TRUE);
450 g_free(context);
451 end:
452 return metadata;
453 }
454
455 void bt_ctf_writer_flush_metadata(struct bt_ctf_writer *writer)
456 {
457 int ret;
458 char *metadata_string = NULL;
459
460 if (!writer) {
461 goto end;
462 }
463
464 metadata_string = bt_ctf_writer_get_metadata_string(writer);
465 if (!metadata_string) {
466 goto end;
467 }
468
469 if (lseek(writer->metadata_fd, 0, SEEK_SET) == (off_t)-1) {
470 perror("lseek");
471 goto end;
472 }
473
474 if (ftruncate(writer->metadata_fd, 0)) {
475 perror("ftruncate");
476 goto end;
477 }
478
479 ret = write(writer->metadata_fd, metadata_string,
480 strlen(metadata_string));
481 if (ret < 0) {
482 perror("write");
483 goto end;
484 }
485 end:
486 g_free(metadata_string);
487 }
488
489 int bt_ctf_writer_set_byte_order(struct bt_ctf_writer *writer,
490 enum bt_ctf_byte_order byte_order)
491 {
492 int ret = 0;
493 int internal_byte_order;
494
495 if (!writer || writer->frozen) {
496 ret = -1;
497 goto end;
498 }
499
500 switch (byte_order) {
501 case BT_CTF_BYTE_ORDER_NATIVE:
502 internal_byte_order = (G_BYTE_ORDER == G_LITTLE_ENDIAN) ?
503 LITTLE_ENDIAN : BIG_ENDIAN;
504 break;
505 case BT_CTF_BYTE_ORDER_LITTLE_ENDIAN:
506 internal_byte_order = LITTLE_ENDIAN;
507 break;
508 case BT_CTF_BYTE_ORDER_BIG_ENDIAN:
509 case BT_CTF_BYTE_ORDER_NETWORK:
510 internal_byte_order = BIG_ENDIAN;
511 break;
512 default:
513 ret = -1;
514 goto end;
515 }
516
517 writer->byte_order = internal_byte_order;
518 if (writer->trace_packet_header_type ||
519 writer->trace_packet_header) {
520 init_trace_packet_header(writer);
521 }
522 end:
523 return ret;
524 }
525
526 void bt_ctf_writer_get(struct bt_ctf_writer *writer)
527 {
528 if (!writer) {
529 return;
530 }
531
532 bt_ctf_ref_get(&writer->ref_count);
533 }
534
535 void bt_ctf_writer_put(struct bt_ctf_writer *writer)
536 {
537 if (!writer) {
538 return;
539 }
540
541 bt_ctf_ref_put(&writer->ref_count, bt_ctf_writer_destroy);
542 }
543
544 BT_HIDDEN
545 int validate_identifier(const char *input_string)
546 {
547 int ret = 0;
548 char *string = NULL;
549 char *save_ptr, *token;
550
551 if (!input_string || input_string[0] == '\0') {
552 ret = -1;
553 goto end;
554 }
555
556 string = strdup(input_string);
557 if (!string) {
558 ret = -1;
559 goto end;
560 }
561
562 token = strtok_r(string, " ", &save_ptr);
563 while (token) {
564 if (g_hash_table_contains(reserved_keywords_set,
565 GINT_TO_POINTER(g_quark_from_string(token)))) {
566 ret = -1;
567 goto end;
568 }
569
570 token = strtok_r(NULL, " ", &save_ptr);
571 }
572 end:
573 free(string);
574 return ret;
575 }
576
577 BT_HIDDEN
578 struct bt_ctf_field_type *get_field_type(enum field_type_alias alias)
579 {
580 unsigned int alignment, size;
581 struct bt_ctf_field_type *field_type;
582
583 if (alias >= NR_FIELD_TYPE_ALIAS) {
584 return NULL;
585 }
586
587 alignment = field_type_aliases_alignments[alias];
588 size = field_type_aliases_sizes[alias];
589 field_type = bt_ctf_field_type_integer_create(size);
590 bt_ctf_field_type_set_alignment(field_type, alignment);
591 return field_type;
592 }
593
594 static
595 int init_trace_packet_header(struct bt_ctf_writer *writer)
596 {
597 size_t i;
598 int ret = 0;
599 struct bt_ctf_field *trace_packet_header = NULL,
600 *magic = NULL, *uuid_array = NULL;
601 struct bt_ctf_field_type *_uint32_t =
602 get_field_type(FIELD_TYPE_ALIAS_UINT32_T);
603 struct bt_ctf_field_type *_uint8_t =
604 get_field_type(FIELD_TYPE_ALIAS_UINT8_T);
605 struct bt_ctf_field_type *trace_packet_header_type =
606 bt_ctf_field_type_structure_create();
607 struct bt_ctf_field_type *uuid_array_type =
608 bt_ctf_field_type_array_create(_uint8_t, 16);
609
610 if (!trace_packet_header_type || !uuid_array_type) {
611 ret = -1;
612 goto end;
613 }
614
615 ret = bt_ctf_field_type_set_byte_order(_uint32_t,
616 (writer->byte_order == LITTLE_ENDIAN ?
617 BT_CTF_BYTE_ORDER_LITTLE_ENDIAN :
618 BT_CTF_BYTE_ORDER_BIG_ENDIAN));
619 if (ret) {
620 goto end;
621 }
622
623 ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type,
624 _uint32_t, "magic");
625 if (ret) {
626 goto end;
627 }
628
629 ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type,
630 uuid_array_type, "uuid");
631 if (ret) {
632 goto end;
633 }
634
635 ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type,
636 _uint32_t, "stream_id");
637 if (ret) {
638 goto end;
639 }
640
641 trace_packet_header = bt_ctf_field_create(trace_packet_header_type);
642 if (!trace_packet_header) {
643 ret = -1;
644 goto end;
645 }
646
647 magic = bt_ctf_field_structure_get_field(trace_packet_header, "magic");
648 ret = bt_ctf_field_unsigned_integer_set_value(magic, 0xC1FC1FC1);
649 if (ret) {
650 goto end;
651 }
652
653 uuid_array = bt_ctf_field_structure_get_field(trace_packet_header,
654 "uuid");
655 for (i = 0; i < 16; i++) {
656 struct bt_ctf_field *uuid_element =
657 bt_ctf_field_array_get_field(uuid_array, i);
658 ret = bt_ctf_field_unsigned_integer_set_value(uuid_element,
659 writer->uuid[i]);
660 bt_ctf_field_put(uuid_element);
661 if (ret) {
662 goto end;
663 }
664 }
665
666 bt_ctf_field_type_put(writer->trace_packet_header_type);
667 bt_ctf_field_put(writer->trace_packet_header);
668 writer->trace_packet_header_type = trace_packet_header_type;
669 writer->trace_packet_header = trace_packet_header;
670 end:
671 bt_ctf_field_type_put(uuid_array_type);
672 bt_ctf_field_type_put(_uint32_t);
673 bt_ctf_field_type_put(_uint8_t);
674 bt_ctf_field_put(magic);
675 bt_ctf_field_put(uuid_array);
676 if (ret) {
677 bt_ctf_field_type_put(trace_packet_header_type);
678 bt_ctf_field_put(trace_packet_header);
679 }
680
681 return ret;
682 }
683
684 static
685 void environment_variable_destroy(struct environment_variable *var)
686 {
687 g_string_free(var->name, TRUE);
688 g_string_free(var->value, TRUE);
689 g_free(var);
690 }
691
692 static
693 int create_stream_file(struct bt_ctf_writer *writer,
694 struct bt_ctf_stream *stream)
695 {
696 int fd;
697 GString *filename = g_string_new(stream->stream_class->name->str);
698
699 g_string_append_printf(filename, "_%" PRIu32, stream->id);
700 fd = openat(writer->trace_dir_fd, filename->str,
701 O_RDWR | O_CREAT | O_TRUNC,
702 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
703 g_string_free(filename, TRUE);
704 return fd;
705 }
706
707 static
708 void stream_flush_cb(struct bt_ctf_stream *stream, struct bt_ctf_writer *writer)
709 {
710 struct bt_ctf_field *stream_id;
711
712 /* Start a new packet in the stream */
713 if (stream->flushed_packet_count) {
714 /* ctf_init_pos has already initialized the first packet */
715 ctf_packet_seek(&stream->pos.parent, 0, SEEK_CUR);
716 }
717
718 stream_id = bt_ctf_field_structure_get_field(
719 writer->trace_packet_header, "stream_id");
720 bt_ctf_field_unsigned_integer_set_value(stream_id, stream->id);
721 bt_ctf_field_put(stream_id);
722
723 /* Write the trace_packet_header */
724 bt_ctf_field_serialize(writer->trace_packet_header, &stream->pos);
725 }
726
727 static __attribute__((constructor))
728 void writer_init(void)
729 {
730 size_t i;
731 const size_t reserved_keywords_count =
732 sizeof(reserved_keywords_str) / sizeof(char *);
733
734 global_data_refcount++;
735 if (init_done) {
736 return;
737 }
738
739 reserved_keywords_set = g_hash_table_new(g_direct_hash, g_direct_equal);
740 for (i = 0; i < reserved_keywords_count; i++) {
741 g_hash_table_add(reserved_keywords_set,
742 GINT_TO_POINTER(g_quark_from_string(reserved_keywords_str[i])));
743 }
744
745 init_done = 1;
746 }
747
748 static __attribute__((destructor))
749 void writer_finalize(void)
750 {
751 if (--global_data_refcount == 0) {
752 g_hash_table_destroy(reserved_keywords_set);
753 }
754 }
This page took 0.044748 seconds and 5 git commands to generate.