1 /*
2 INI LIBRARY
3
4 Object to handle comments
5
6 Copyright (C) Dmitri Pal <dpal@redhat.com> 2010
7
8 INI Library is free software: you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 INI Library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with INI Library. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "config.h"
23 #include <errno.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include "trace.h"
27 #include "ref_array.h"
28 #include "simplebuffer.h"
29 #include "ini_comment.h"
30 #include "ini_defines.h"
31
32 /* The lines will increment in this number */
33 #define INI_COMMENT_BLOCK 10
34 /* Default comment length */
35 #define INI_COMMENT_LEN 100
36
37
38 /***************************/
39 /* Internal comment states */
40 /***************************/
41 /* Empty - initial */
42 #define INI_COMMENT_EMPTY 0
43 /* Read - read from file */
44 #define INI_COMMENT_READ 1
45 /* Comment was altered */
46 #define INI_COMMENT_CHANGED 2
47
48
49 /*********************************/
50 /* Modes to wrap ref array calls */
51 /*********************************/
52 #define INI_COMMENT_MODE_BUILD 1
53 #define INI_COMMENT_MODE_APPEND 2
54 #define INI_COMMENT_MODE_INSERT 3
55 #define INI_COMMENT_MODE_REPLACE 4
56 #define INI_COMMENT_MODE_REMOVE 5
57 #define INI_COMMENT_MODE_CLEAR 6
58
59 /****************************************/
60 /* Internal structure to hold a comment */
61 /****************************************/
62 struct ini_comment {
63 struct ref_array *ra;
64 uint32_t state;
65 };
66
67
68 /****************************************/
69
70 /* Destroy the comment object */
ini_comment_destroy(struct ini_comment * ic)71 void ini_comment_destroy(struct ini_comment *ic)
72 {
73
74 TRACE_FLOW_ENTRY();
75 if (ic) {
76 /* Function will check for NULL */
77 ref_array_destroy(ic->ra);
78 free(ic);
79 }
80 TRACE_FLOW_EXIT();
81 }
82
83
84 /* Cleanup callback */
ini_comment_cb(void * elem,ref_array_del_enum type,void * data)85 static void ini_comment_cb(void *elem,
86 ref_array_del_enum type,
87 void *data)
88 {
89
90 TRACE_FLOW_ENTRY();
91 simplebuffer_free(*((struct simplebuffer **)elem));
92 TRACE_FLOW_EXIT();
93 }
94
95
96 /* Create a comment object */
ini_comment_create(struct ini_comment ** ic)97 int ini_comment_create(struct ini_comment **ic)
98 {
99 int error = EOK;
100 struct ref_array *ra = NULL;
101 struct ini_comment *ic_new = NULL;
102
103 TRACE_FLOW_ENTRY();
104
105 error = ref_array_create(&ra,
106 sizeof(struct simplebuffer *),
107 INI_COMMENT_BLOCK,
108 ini_comment_cb,
109 NULL);
110 if (error) {
111 TRACE_ERROR_NUMBER("Error creating ref array", error);
112 return error;
113 }
114
115 ic_new = malloc(sizeof(struct ini_comment));
116 if (!ic_new) {
117 TRACE_ERROR_NUMBER("Memory allocation error", ENOMEM);
118 ref_array_destroy(ra);
119 return ENOMEM;
120 }
121
122 /* Initialize members here */
123 ic_new->ra = ra;
124 ic_new->state = INI_COMMENT_EMPTY;
125
126 *ic = ic_new;
127
128 TRACE_FLOW_EXIT();
129 return error;
130 }
131
132 /* Callback to copy comment */
ini_comment_copy_cb(void * elem,void * new_elem)133 static int ini_comment_copy_cb(void *elem,
134 void *new_elem)
135 {
136 int error = EOK;
137 struct simplebuffer *sb = NULL;
138 struct simplebuffer *sb_new = NULL;
139
140 TRACE_FLOW_ENTRY();
141
142 error = simplebuffer_alloc(&sb_new);
143 if (error) {
144 TRACE_ERROR_NUMBER("Failed to allocate buffer", error);
145 return error;
146 }
147
148 sb = *((struct simplebuffer **)elem);
149 error = simplebuffer_add_str(sb_new,
150 (const char *)simplebuffer_get_buf(sb),
151 simplebuffer_get_len(sb),
152 INI_COMMENT_LEN);
153 if (error) {
154 TRACE_ERROR_NUMBER("Failed to allocate buffer", error);
155 simplebuffer_free(sb_new);
156 return error;
157 }
158
159 *((struct simplebuffer **)new_elem) = sb_new;
160
161 TRACE_FLOW_EXIT();
162 return error;
163 }
164
165
166 /* Create a copy of the comment object */
ini_comment_copy(struct ini_comment * ic,struct ini_comment ** ic_copy)167 int ini_comment_copy(struct ini_comment *ic,
168 struct ini_comment **ic_copy)
169 {
170 int error = EOK;
171 struct ref_array *ra = NULL;
172 struct ini_comment *ic_new = NULL;
173
174 TRACE_FLOW_ENTRY();
175
176 error = ref_array_copy(ic->ra,
177 ini_comment_copy_cb,
178 ini_comment_cb,
179 NULL,
180 &ra);
181 if (error) {
182 TRACE_ERROR_NUMBER("Error creating a copy of ref array", error);
183 return error;
184 }
185
186 ic_new = malloc(sizeof(struct ini_comment));
187 if (!ic_new) {
188 TRACE_ERROR_NUMBER("Memory allocation error", ENOMEM);
189 ref_array_destroy(ra);
190 return ENOMEM;
191 }
192
193 /* Initialize members here */
194 ic_new->ra = ra;
195 ic_new->state = ic->state;
196
197 *ic_copy = ic_new;
198
199 TRACE_FLOW_EXIT();
200 return error;
201 }
202
203 /*
204 * Modify the comment object
205 */
ini_comment_modify(struct ini_comment * ic,int mode,uint32_t idx,const char * line,uint32_t length)206 static int ini_comment_modify(struct ini_comment *ic,
207 int mode,
208 uint32_t idx,
209 const char *line,
210 uint32_t length)
211 {
212 int error = EOK;
213 struct simplebuffer *elem = NULL;
214 struct simplebuffer *empty = NULL;
215 char *input = NULL;
216
217 uint32_t i, len = 0;
218 uint32_t input_len = 0;
219
220 TRACE_FLOW_ENTRY();
221
222 if (!ic) {
223 TRACE_ERROR_NUMBER("Invalid comment object", EINVAL);
224 return EINVAL;
225 }
226
227
228 if (mode == INI_COMMENT_MODE_BUILD) {
229 /*
230 * Can use this function only if object is empty or
231 * reading from the file.
232 */
233 if ((ic->state != INI_COMMENT_EMPTY) &&
234 (ic->state != INI_COMMENT_READ)) {
235 TRACE_ERROR_NUMBER("Invalid use of the function", EINVAL);
236 return EINVAL;
237 }
238 }
239
240 /* Make sure that we ignore "line" in reset case */
241 if (mode != INI_COMMENT_MODE_CLEAR)
242 memcpy(&input, &line, sizeof(char *));
243
244 if (mode != INI_COMMENT_MODE_REMOVE) {
245
246 error = simplebuffer_alloc(&elem);
247 if (error) {
248 TRACE_ERROR_NUMBER("Allocate buffer for the comment", error);
249 return error;
250 }
251
252 if (input) {
253 if (length == 0) input_len = strlen(input);
254 else input_len = length;
255
256 error = simplebuffer_add_str(elem,
257 input,
258 input_len,
259 INI_COMMENT_LEN);
260 }
261 else {
262 error = simplebuffer_add_str(elem,
263 "",
264 0,
265 INI_COMMENT_LEN);
266 }
267
268 if (error) {
269 TRACE_ERROR_NUMBER("Allocate buffer for the comment", error);
270 simplebuffer_free(elem);
271 return error;
272 }
273 }
274
275 /* Do action depending on mode */
276 switch (mode) {
277 case INI_COMMENT_MODE_BUILD:
278
279 TRACE_INFO_STRING("BUILD mode", "");
280 error = ref_array_append(ic->ra, (void *)&elem);
281 if (error) {
282 TRACE_ERROR_NUMBER("Failed to append line to an array", error);
283 simplebuffer_free(elem);
284 return error;
285 }
286
287 break;
288
289 case INI_COMMENT_MODE_APPEND:
290
291 TRACE_INFO_STRING("Append mode", "");
292 error = ref_array_append(ic->ra, (void *)&elem);
293 if (error) {
294 TRACE_ERROR_NUMBER("Failed to append line to an array", error);
295 simplebuffer_free(elem);
296 return error;
297 }
298
299 break;
300
301 case INI_COMMENT_MODE_INSERT:
302
303 TRACE_INFO_STRING("Insert mode", "");
304 len = ref_array_len(ic->ra);
305 if (idx > len) {
306 /* Fill in empty lines */
307 for (i = 0; i < (idx-len); i++) {
308 error = simplebuffer_alloc(&empty);
309 if (error) {
310 TRACE_ERROR_NUMBER("Allocate buffer for the comment", error);
311 simplebuffer_free(elem);
312 return error;
313 }
314 error = simplebuffer_add_str(elem,
315 NULL,
316 0,
317 INI_COMMENT_LEN);
318 if (error) {
319 TRACE_ERROR_NUMBER("Make comment empty", error);
320 simplebuffer_free(empty);
321 simplebuffer_free(elem);
322 return error;
323 }
324 error = ref_array_append(ic->ra, (void *)&empty);
325 if (error) {
326 TRACE_ERROR_NUMBER("Append problem", error);
327 simplebuffer_free(empty);
328 simplebuffer_free(elem);
329 return error;
330 }
331 }
332 /* Append last line */
333 error = ref_array_append(ic->ra, (void *)&elem);
334 if (error) {
335 TRACE_ERROR_NUMBER("Failed to append last line", error);
336 simplebuffer_free(elem);
337 return error;
338 }
339 }
340 else {
341 /* Insert inside the array */
342 error = ref_array_insert(ic->ra, idx, (void *)&elem);
343 if (error) {
344 TRACE_ERROR_NUMBER("Failed to append last line", error);
345 simplebuffer_free(elem);
346 return error;
347 }
348
349 }
350 break;
351
352
353 case INI_COMMENT_MODE_REPLACE:
354
355 TRACE_INFO_STRING("Replace mode", "");
356 error = ref_array_replace(ic->ra, idx, (void *)&elem);
357 if (error) {
358 TRACE_ERROR_NUMBER("Failed to replace", error);
359 simplebuffer_free(elem);
360 return error;
361 }
362 break;
363
364 case INI_COMMENT_MODE_REMOVE:
365
366 TRACE_INFO_STRING("Remove mode", "");
367 error = ref_array_remove(ic->ra, idx);
368 if (error) {
369 TRACE_ERROR_NUMBER("Failed to remove", error);
370 return error;
371 }
372
373 break;
374
375 case INI_COMMENT_MODE_CLEAR:
376
377 TRACE_INFO_STRING("Clear mode", "");
378 error = ref_array_replace(ic->ra, idx, (void *)&elem);
379 if (error) {
380 TRACE_ERROR_NUMBER("Failed to replace", error);
381 simplebuffer_free(elem);
382 return error;
383 }
384 break;
385
386 default :
387
388 TRACE_ERROR_STRING("Coding error", "");
389 simplebuffer_free(elem);
390 return EINVAL;
391
392 }
393
394
395 /* Change state */
396 if (INI_COMMENT_MODE_BUILD) ic->state = INI_COMMENT_READ;
397 else ic->state = INI_COMMENT_CHANGED;
398
399
400 TRACE_FLOW_EXIT();
401 return error;
402 }
403
404 /*
405 * Build up a comment object - use this when reading
406 * comments from a file.
407 */
ini_comment_build(struct ini_comment * ic,const char * line)408 int ini_comment_build(struct ini_comment *ic, const char *line)
409 {
410 int error = EOK;
411
412 TRACE_FLOW_ENTRY();
413
414 error = ini_comment_modify(ic, INI_COMMENT_MODE_BUILD, 0, line, 0);
415
416 TRACE_FLOW_NUMBER("ini_comment_build - Returning", error);
417 return error;
418 }
419
420 /*
421 * Build up a comment object - use this when reading
422 * comments from a file.
423 */
ini_comment_build_wl(struct ini_comment * ic,const char * line,uint32_t length)424 int ini_comment_build_wl(struct ini_comment *ic,
425 const char *line,
426 uint32_t length)
427 {
428 int error = EOK;
429
430 TRACE_FLOW_ENTRY();
431
432 error = ini_comment_modify(ic, INI_COMMENT_MODE_BUILD, 0, line, length);
433
434 TRACE_FLOW_NUMBER("ini_comment_build - Returning", error);
435 return error;
436 }
437
438 /*
439 * Modify comment by instering a line.
440 */
ini_comment_insert(struct ini_comment * ic,uint32_t idx,const char * line)441 int ini_comment_insert(struct ini_comment *ic,
442 uint32_t idx,
443 const char *line)
444 {
445 int error = EOK;
446
447 TRACE_FLOW_ENTRY();
448
449 error = ini_comment_modify(ic, INI_COMMENT_MODE_INSERT, idx, line, 0);
450
451 TRACE_FLOW_NUMBER("ini_comment_insert - Returning", error);
452 return error;
453 }
454
455 /* Modify comment by appending a line. */
ini_comment_append(struct ini_comment * ic,const char * line)456 int ini_comment_append(struct ini_comment *ic, const char *line)
457 {
458 int error = EOK;
459
460 TRACE_FLOW_ENTRY();
461
462 error = ini_comment_modify(ic, INI_COMMENT_MODE_APPEND, 0, line, 0);
463
464 TRACE_FLOW_NUMBER("ini_comment_append - Returning", error);
465 return error;
466 }
467
468 /* Remove line from the comment.*/
ini_comment_remove(struct ini_comment * ic,uint32_t idx)469 int ini_comment_remove(struct ini_comment *ic, uint32_t idx)
470 {
471 int error = EOK;
472
473 TRACE_FLOW_ENTRY();
474
475 error = ini_comment_modify(ic, INI_COMMENT_MODE_REMOVE, idx, NULL, 0);
476
477 TRACE_FLOW_NUMBER("ini_comment_remove - Returning", error);
478 return error;
479 }
480
481 /* Clear line in the comment. Line is replaced with an empty line */
ini_comment_clear(struct ini_comment * ic,uint32_t idx)482 int ini_comment_clear(struct ini_comment *ic, uint32_t idx)
483 {
484 int error = EOK;
485
486 TRACE_FLOW_ENTRY();
487
488 error = ini_comment_modify(ic, INI_COMMENT_MODE_CLEAR, idx, NULL, 0);
489
490 TRACE_FLOW_NUMBER("ini_comment_clear - Returning", error);
491 return error;
492
493 }
494
495 /* Replace a line in the comment */
ini_comment_replace(struct ini_comment * ic,uint32_t idx,const char * line)496 int ini_comment_replace(struct ini_comment *ic,
497 uint32_t idx,
498 const char *line)
499 {
500 int error = EOK;
501
502 TRACE_FLOW_ENTRY();
503
504 error = ini_comment_modify(ic, INI_COMMENT_MODE_REPLACE, idx, line, 0);
505
506 TRACE_FLOW_NUMBER("ini_comment_replace - Returning", error);
507 return error;
508 }
509
510
511 /* Reset the comment - clean all lines.*/
ini_comment_reset(struct ini_comment * ic)512 int ini_comment_reset(struct ini_comment *ic)
513 {
514 int error = EOK;
515
516 TRACE_FLOW_ENTRY();
517
518 if (!ic) {
519 TRACE_ERROR_NUMBER("Invalid comment object", EINVAL);
520 return EINVAL;
521 }
522
523 /* Reset comment if it is not empty */
524 if (ic->state != INI_COMMENT_EMPTY) {
525 ref_array_reset(ic->ra);
526 ic->state = INI_COMMENT_CHANGED;
527 }
528
529 TRACE_FLOW_EXIT();
530 return error;
531 }
532
533 /* Get number of lines */
ini_comment_get_numlines(struct ini_comment * ic,uint32_t * num)534 int ini_comment_get_numlines(struct ini_comment *ic, uint32_t *num)
535 {
536 int error = EOK;
537
538 TRACE_FLOW_ENTRY();
539
540 if ((!ic) || (!num)) {
541 TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
542 return EINVAL;
543 }
544
545 error = ref_array_getlen(ic->ra, num);
546
547 TRACE_FLOW_NUMBER("ini_comment_get_numlines - Returning", error);
548 return error;
549
550 }
551
552 /* Get line */
ini_comment_get_line(struct ini_comment * ic,uint32_t idx,char ** line,uint32_t * line_len)553 int ini_comment_get_line(struct ini_comment *ic, uint32_t idx,
554 char **line, uint32_t *line_len)
555 {
556 int error = EOK;
557 void *res = NULL;
558 struct simplebuffer *sb = NULL;
559 const unsigned char *ln;
560
561 TRACE_FLOW_ENTRY();
562
563 if ((!ic) || (!line)) {
564 TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
565 return EINVAL;
566 }
567
568 res = ref_array_get(ic->ra, idx, (void *)&sb);
569 if (!res) {
570 error = EINVAL;
571 *line = NULL;
572 if (line_len) line_len = 0;
573 }
574 else {
575 ln = simplebuffer_get_buf(sb);
576 memcpy(line, &ln, sizeof(char *));
577 if (line_len) *line_len = simplebuffer_get_len(sb);
578 }
579
580 TRACE_FLOW_NUMBER("ini_comment_get_line - Returning", error);
581 return error;
582 }
583
584 /* Swap lines */
ini_comment_swap(struct ini_comment * ic,uint32_t idx1,uint32_t idx2)585 int ini_comment_swap(struct ini_comment *ic,
586 uint32_t idx1,
587 uint32_t idx2)
588 {
589 int error = EOK;
590
591 TRACE_FLOW_ENTRY();
592
593 if (!ic) {
594 TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
595 return EINVAL;
596 }
597
598 if (idx1 != idx2) {
599 if ((error = ref_array_swap(ic->ra, idx1, idx2))) {
600 TRACE_ERROR_NUMBER("Failed to swap", error);
601 return error;
602 }
603 ic->state = INI_COMMENT_CHANGED;
604 }
605
606 TRACE_FLOW_EXIT();
607 return error;
608 }
609
610 /* Add one comment to another */
ini_comment_add(struct ini_comment * ic_to_add,struct ini_comment * ic)611 int ini_comment_add(struct ini_comment *ic_to_add,
612 struct ini_comment *ic)
613 {
614 int error = EOK;
615 struct simplebuffer *sb = NULL;
616 struct simplebuffer *sb_new = NULL;
617 void *res = NULL;
618 uint32_t len = 0;
619 int i;
620
621 TRACE_FLOW_ENTRY();
622
623 len = ref_array_len(ic_to_add->ra);
624
625 for (i = 0; i< len; i++) {
626
627 /* Get data element */
628 res = ref_array_get(ic_to_add->ra, i, (void *)(&sb));
629 if (!res) {
630 TRACE_ERROR_NUMBER("Failed to get comment element", error);
631 return error;
632 }
633
634 /* Create a storage a for a copy */
635 error = simplebuffer_alloc(&sb_new);
636 if (error) {
637 TRACE_ERROR_NUMBER("Allocate buffer for the comment", error);
638 return error;
639 }
640
641 /* Copy actual data */
642 error = simplebuffer_add_str(sb_new,
643 (const char *)simplebuffer_get_buf(sb),
644 simplebuffer_get_len(sb),
645 INI_COMMENT_LEN);
646 if (error) {
647 TRACE_ERROR_NUMBER("Failed to append line to an array", error);
648 simplebuffer_free(sb_new);
649 return error;
650 }
651
652 /* Append it to the array */
653 error = ref_array_append(ic->ra, (void *)&sb_new);
654 if (error) {
655 TRACE_ERROR_NUMBER("Failed to append element to an array", error);
656 simplebuffer_free(sb_new);
657 return error;
658 }
659 }
660
661 TRACE_FLOW_EXIT();
662 return error;
663 }
664
665 /* Serialize comment */
ini_comment_serialize(struct ini_comment * ic,struct simplebuffer * sbobj)666 int ini_comment_serialize (struct ini_comment *ic,
667 struct simplebuffer *sbobj)
668 {
669 int error = EOK;
670 uint32_t num = 0;
671 uint32_t i = 0;
672 uint32_t len = 0;
673 char *commentline = NULL;
674
675 TRACE_FLOW_ENTRY();
676
677 /* Get number of lines in the comment */
678 error = ini_comment_get_numlines(ic, &num);
679 if (error) {
680 TRACE_ERROR_NUMBER("Failed to get number of lines", error);
681 return error;
682 }
683
684 for (i = 0; i < num; i++) {
685
686 len = 0;
687 commentline = NULL;
688
689 error = ini_comment_get_line(ic, i, &commentline, &len);
690 if (error) {
691 TRACE_ERROR_NUMBER("Failed to get line", error);
692 return error;
693 }
694
695 error = simplebuffer_add_raw(sbobj,
696 commentline,
697 len,
698 INI_VALUE_BLOCK);
699 if (error) {
700 TRACE_ERROR_NUMBER("Failed to add comment", error);
701 return error;
702 }
703
704 error = simplebuffer_add_cr(sbobj);
705 if (error) {
706 TRACE_ERROR_NUMBER("Failed to add CR", error);
707 return error;
708 }
709 }
710
711 TRACE_FLOW_EXIT();
712 return error;
713 }
714
715 /* Internal function to print comment */
ini_comment_print(struct ini_comment * ic,FILE * file)716 void ini_comment_print(struct ini_comment *ic, FILE *file)
717 {
718 int len;
719 int i;
720 struct simplebuffer *sb = NULL;
721
722 TRACE_FLOW_ENTRY();
723
724 if (!file) {
725 TRACE_ERROR_NUMBER("Invalid file argument", EINVAL);
726 return;
727 }
728
729 if (ic) {
730 len = ref_array_len(ic->ra);
731 for (i = 0; i < len; i++) {
732 ref_array_get(ic->ra, i, (void *)(&sb));
733 fprintf(file, "%s\n", simplebuffer_get_buf(sb));
734 }
735 }
736
737 TRACE_FLOW_EXIT();
738 }
739
740 /* Construct a comment out of array or strings. */
ini_comment_construct(const char * comments[],size_t count_comment,struct ini_comment ** ic)741 int ini_comment_construct(const char *comments[],
742 size_t count_comment,
743 struct ini_comment **ic)
744 {
745 int error = EOK;
746 size_t cnt = 0;
747 struct ini_comment *new_ic = NULL;
748
749 TRACE_FLOW_ENTRY();
750 if (!ic) {
751 TRACE_ERROR_STRING("Invalid argument","comment object");
752 return EINVAL;
753 }
754
755 if (comments) {
756 error = ini_comment_create(&new_ic);
757 if (error) {
758 TRACE_ERROR_NUMBER("Failed to create comment object",
759 error);
760 return error;
761 }
762
763 if (count_comment) {
764 /* Base the number of lines on count */
765 for (cnt = 0; cnt < count_comment; cnt++) {
766 error = ini_comment_append(new_ic, comments[cnt]);
767 if (error) {
768 TRACE_ERROR_NUMBER("Failed to append comment in for loop.",
769 error);
770 ini_comment_destroy(new_ic);
771 return error;
772 }
773 }
774 }
775 else {
776 /* Assume that the list is NULL terminated */
777 while (comments[cnt]) {
778 error = ini_comment_append(new_ic, comments[cnt]);
779 if (error) {
780 TRACE_ERROR_NUMBER("Failed to append comment in for loop.",
781 error);
782 ini_comment_destroy(new_ic);
783 return error;
784 }
785 cnt++;
786 }
787 }
788 *ic = new_ic;
789 }
790 else {
791 /* No comments! */
792 *ic = NULL;
793 }
794
795 TRACE_FLOW_EXIT();
796 return EOK;
797 }
798