1 #include "libini2.h"
2
3 #include "libini/ini.h"
4
5 #include <errno.h>
6 #include <iio.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <glib.h>
10
11 #ifdef _WIN32
12 #define LONG_LONG_FORMAT "%I64d"
13 #else
14 #define LONG_LONG_FORMAT "%lld"
15 #endif
16
17 struct load_store_params {
18 const struct iio_device *dev;
19 const char * const *whitelist;
20 size_t list_len;
21 bool is_debug;
22 FILE *f;
23 struct INI *ini;
24 const void *section_top;
25 };
26
attr_matches(const char * dev_name,size_t dev_len,const char * attr,size_t attr_len,const char * key,size_t len,bool debug)27 static bool attr_matches(const char *dev_name, size_t dev_len,
28 const char *attr, size_t attr_len,
29 const char *key, size_t len, bool debug)
30 {
31 return (!debug && (len == dev_len + 1 + attr_len) &&
32 !strncmp(key, dev_name, dev_len) &&
33 !strncmp(key + dev_len + 1, attr, attr_len)) ||
34 (debug && (len == dev_len + sizeof("debug.") + attr_len) &&
35 !strncmp(key, "debug.", sizeof("debug.") - 1) &&
36 !strncmp(key + sizeof("debug.") - 1, dev_name, dev_len) &&
37 !strncmp(key + sizeof("debug.") + dev_len, attr, attr_len));
38 }
39
attr_in_whitelist(const char * attr,const char * dev_name,size_t dev_len,bool is_debug,const char * const * whitelist,size_t list_len)40 static bool attr_in_whitelist(const char *attr,
41 const char *dev_name, size_t dev_len, bool is_debug,
42 const char * const *whitelist, size_t list_len)
43 {
44 unsigned int i;
45 for (i = 0; i < list_len && whitelist[i]; i++)
46 if ((!is_debug && !strncmp(whitelist[i], dev_name, dev_len) &&
47 !strcmp(whitelist[i] + dev_len + 1, attr)) ||
48 (is_debug && !strncmp(whitelist[i], "debug.", sizeof("debug.") - 1) &&
49 !strncmp(whitelist[i] + sizeof("debug.") - 1, dev_name, dev_len) &&
50 !strcmp(whitelist[i] + sizeof("debug.") + dev_len, attr)))
51 return true;
52 return false;
53 }
54
read_from_ini(struct load_store_params * params,const char * dev_name,size_t name_len,const char * attr,void * buf,size_t len)55 static ssize_t read_from_ini(struct load_store_params *params,
56 const char *dev_name, size_t name_len,
57 const char *attr, void *buf, size_t len)
58 {
59 bool found = false;
60 const char *key, *value;
61 size_t klen, vlen;
62
63 if (!len)
64 return 0;
65
66 /* Rewind to the beginning of the section */
67 ini_set_read_pointer(params->ini, params->section_top);
68
69 while (!found && ini_read_pair(params->ini,
70 &key, &klen, &value, &vlen) > 0)
71 found = attr_matches(dev_name, name_len, attr, strlen(attr),
72 key, klen, params->is_debug);
73 if (!found)
74 return 0;
75
76 if (len > vlen)
77 len = vlen;
78 memcpy(buf, value, len);
79 return (ssize_t) len;
80 }
81
update_from_ini_dev_cb(struct iio_device * dev,const char * attr,void * buf,size_t len,void * d)82 static ssize_t update_from_ini_dev_cb(struct iio_device *dev,
83 const char *attr, void *buf, size_t len, void *d)
84 {
85 struct load_store_params *params = (struct load_store_params *) d;
86 const char *dev_name = iio_device_get_name(dev);
87 size_t name_len = dev_name ? strlen(dev_name) : 0;
88
89 if (attr_in_whitelist(attr, dev_name, name_len, params->is_debug,
90 params->whitelist, params->list_len))
91 return read_from_ini(params,
92 dev_name, name_len, attr, buf, len);
93 return 0;
94 }
95
update_from_ini_chn_cb(struct iio_channel * chn,const char * attr,void * buf,size_t len,void * d)96 static ssize_t update_from_ini_chn_cb(struct iio_channel *chn,
97 const char *attr, void *buf, size_t len, void *d)
98 {
99 struct load_store_params *params = (struct load_store_params *) d;
100 const char *dev_name = iio_device_get_name(params->dev);
101 size_t name_len = dev_name ? strlen(dev_name) : 0;
102 bool is_hardwaregain = !strncmp(attr, "hardwaregain", len);
103
104 attr = iio_channel_attr_get_filename(chn, attr);
105 if (attr_in_whitelist(attr, dev_name, name_len, false,
106 params->whitelist, params->list_len)) {
107 ssize_t ret = read_from_ini(params,
108 dev_name, name_len, attr, buf, len);
109
110 /* Dirty workaround that strips the "dB" suffix of
111 * hardwaregain value. Fix me when possible. */
112 if (is_hardwaregain) {
113 char *tmp = strstr((char *) buf, " dB");
114 if (tmp)
115 *tmp = '\0';
116 }
117 return ret;
118 }
119 return 0;
120 }
121
update_from_ini(const char * ini_file,const char * driver_name,struct iio_device * dev,const char * const * whitelist,size_t list_len)122 void update_from_ini(const char *ini_file,
123 const char *driver_name, struct iio_device *dev,
124 const char * const *whitelist, size_t list_len)
125 {
126 bool found = false;
127 const char *name;
128 size_t nlen, dlen;
129 unsigned int i;
130 struct INI *ini = ini_open(ini_file);
131 struct load_store_params params = {
132 .dev = dev,
133 .whitelist = whitelist,
134 .list_len = list_len,
135 .is_debug = false,
136 .ini = ini,
137 };
138
139 if (!ini) {
140 fprintf(stderr, "ERROR: Cannot open INI file %s\n", ini_file);
141 return;
142 }
143
144 dlen = strlen(driver_name);
145
146 while (!found && ini_next_section(ini, &name, &nlen) > 0) {
147 if (nlen == dlen)
148 found = !strncmp(name, driver_name, nlen);
149 }
150
151 if (!found) {
152 fprintf(stderr, "error parsing %s file: Could not find %s\n",
153 ini_file, driver_name);
154 ini_close(ini);
155 return;
156 }
157
158 params.section_top = name + nlen + 1;
159
160 for (i = 0; i < iio_device_get_channels_count(dev); i++)
161 iio_channel_attr_write_all(iio_device_get_channel(dev, i),
162 update_from_ini_chn_cb, ¶ms);
163
164 if (iio_device_get_attrs_count(dev))
165 iio_device_attr_write_all(dev, update_from_ini_dev_cb, ¶ms);
166
167 params.is_debug = true;
168 iio_device_debug_attr_write_all(dev, update_from_ini_dev_cb, ¶ms);
169
170 ini_close(ini);
171 }
172
read_token_from_ini(const char * ini_file,const char * driver_name,const char * token)173 char * read_token_from_ini(const char *ini_file,
174 const char *driver_name, const char *token)
175 {
176 char *dup;
177 const char *name, *key, *value;
178 size_t nlen, klen, vlen, tlen = strlen(token);
179 bool found = false;
180 struct INI *ini = ini_open(ini_file);
181 if (!ini)
182 return NULL;
183
184 while (!found && ini_next_section(ini, &name, &nlen) > 0)
185 found = !strncmp(name, driver_name, nlen);
186 if (!found)
187 return NULL;
188
189 found = false;
190 while (!found && ini_read_pair(ini, &key, &klen, &value, &vlen) > 0)
191 found = (tlen == klen) && !strncmp(token, key, klen);
192 if (!found)
193 return NULL;
194
195 dup = malloc(vlen + 1);
196 snprintf(dup, vlen + 1, "%.*s", (int) vlen, value);
197
198 ini_close(ini);
199 return dup;
200 }
201
write_to_ini(struct load_store_params * params,const char * dev_name,size_t name_len,const char * attr,const char * val,size_t len)202 static void write_to_ini(struct load_store_params *params, const char *dev_name,
203 size_t name_len, const char *attr, const char *val, size_t len)
204 {
205 FILE *f = params->f;
206
207 if (params->is_debug)
208 fwrite("debug.", 1, sizeof("debug.") - 1, f);
209 fwrite(dev_name, 1, name_len, f);
210 fwrite(".", 1, 1, f);
211 fwrite(attr, 1, strlen(attr), f);
212 fwrite(" = ", 1, sizeof(" = ") - 1, f);
213 fwrite(val, 1, len - 1, f);
214 fwrite("\n", 1, 1, f);
215 }
216
save_to_ini_dev_cb(struct iio_device * dev,const char * attr,const char * val,size_t len,void * d)217 static int save_to_ini_dev_cb(struct iio_device *dev,
218 const char *attr, const char *val, size_t len, void *d)
219 {
220 struct load_store_params *params = (struct load_store_params *) d;
221 const char *dev_name = iio_device_get_name(dev);
222 size_t name_len = dev_name ? strlen(dev_name) : 0;
223
224 if (attr_in_whitelist(attr, dev_name, name_len, params->is_debug,
225 params->whitelist, params->list_len))
226 write_to_ini(params, dev_name, name_len, attr, val, len);
227 return 0;
228 }
229
save_to_ini_chn_cb(struct iio_channel * chn,const char * attr,const char * val,size_t len,void * d)230 static int save_to_ini_chn_cb(struct iio_channel *chn,
231 const char *attr, const char *val, size_t len, void *d)
232 {
233 struct load_store_params *params = (struct load_store_params *) d;
234 const char *dev_name = iio_device_get_name(params->dev);
235 size_t name_len = dev_name ? strlen(dev_name) : 0;
236
237 attr = iio_channel_attr_get_filename(chn, attr);
238 if (attr_in_whitelist(attr, dev_name, name_len, false,
239 params->whitelist, params->list_len))
240 write_to_ini(params, dev_name, name_len, attr, val, len);
241 return 0;
242 }
243
save_to_ini(FILE * f,const char * driver_name,struct iio_device * dev,const char * const * whitelist,size_t list_len)244 void save_to_ini(FILE *f, const char *driver_name, struct iio_device *dev,
245 const char * const *whitelist, size_t list_len)
246 {
247 unsigned int i;
248 struct load_store_params params = {
249 .dev = dev,
250 .whitelist = whitelist,
251 .list_len = list_len,
252 .is_debug = false,
253 .f = f,
254 };
255
256 write_driver_name_to_ini(f, driver_name);
257
258 for (i = 0; i < iio_device_get_channels_count(dev); i++)
259 iio_channel_attr_read_all(iio_device_get_channel(dev, i),
260 save_to_ini_chn_cb, ¶ms);
261 iio_device_attr_read_all(dev, save_to_ini_dev_cb, ¶ms);
262
263 params.is_debug = true;
264 iio_device_debug_attr_read_all(dev, save_to_ini_dev_cb, ¶ms);
265 }
266
foreach_in_ini(const char * ini_file,int (* cb)(int,const char *,const char *,const char *))267 int foreach_in_ini(const char *ini_file,
268 int (*cb)(int, const char *, const char *, const char *))
269 {
270 int ret = 0;
271 const char *name, *key, *value;
272 size_t nlen, klen, vlen;
273 struct INI *ini = ini_open(ini_file);
274 if (!ini)
275 return -1;
276
277 while (ini_next_section(ini, &name, &nlen) > 0) {
278 char *n = malloc(nlen + 1);
279 if (!n)
280 goto err_ini_close;
281
282 snprintf(n, nlen + 1, "%.*s", (int) nlen, name);
283
284 while (ini_read_pair(ini, &key, &klen, &value, &vlen) > 0) {
285 int line = ini_get_line_number(ini, key);
286 char *v, *k = malloc(klen + 1);
287 if (!k) {
288 free(n);
289 goto err_ini_close;
290 }
291
292 v = malloc(vlen + 1);
293 if (!v) {
294 free(k);
295 free(n);
296 goto err_ini_close;
297 }
298
299 snprintf(k, klen + 1, "%.*s", (int) klen, key);
300 snprintf(v, vlen + 1, "%.*s", (int) vlen, value);
301
302 ret = cb(line, n, k, v);
303
304 /* only needed when debugging - this should be done in each section
305 if (ret < 0) {
306 fprintf(stderr, "issue in '%s' file: Section:'%s' key:'%s' value:'%s'\n",
307 ini_file, n, k, v);
308 }
309 */
310
311 free(k);
312 free(v);
313
314 if (ret < 0) {
315 free(n);
316 goto err_ini_close;
317 }
318
319 if (ret > 0) {
320 ret = 0;
321 break;
322 }
323 }
324
325 free(n);
326 }
327
328 err_ini_close:
329 ini_close(ini);
330 return ret;
331 }
332
333 /*
334 * Types of loops that can be handled while parsing .ini files
335 * INI_LOOP_SEQ - Loops through a sequence of numbers. Ini syntax:
336 * <SEQ> [var] [first] [increment] [last]
337 * </SEQ>
338 *
339 * INI_LOOP_FOR - Loops through a given list of values. Ini syntax:
340 * <FOR> [var] in {space-separated values}
341 * </FOR>
342 *
343 */
344 enum ini_loop_types {
345 INI_LOOP_SEQ,
346 INI_LOOP_FOR,
347 };
348
349 /*
350 * Structure of a ini loop that stores loop properties and attributes.
351 * type - The type of the loop. See enum ini_loop_types.
352 * var - The variable of the loop that is used as an iterator.
353 * end_loop - The keyword used to mark the end of the loop.
354 * first - first iteration to start from.
355 * inc - incrementation step.
356 * last - last iteration.
357 * i - Holds the current iteration of the loop.
358 * for_values - Use only by the <FOR> loop to store the list of values that the
359 * loop will iterate through.
360 */
361 struct ini_loop {
362 int type;
363 char var[128];
364 char end_loop[128];
365 long long first;
366 long long inc;
367 long long last;
368 long long i;
369 gchar **for_values;
370 };
371
372 /*
373 * Structure of a set of parameters that are used when parsing and expanding
374 * an ini loop structure and all its inner loops.
375 * in_file - input ini file
376 * out_file - output (expanded) ini file
377 * ini_loops - list of loop and its inner loops
378 * unclosed_loops - flag is set when one of the loops is left unclosed in the
379 * input ini file.
380 */
381 struct loops_parse_params {
382 FILE *in_file;
383 FILE *out_file;
384 GSList *ini_loops;
385 bool unclosed_loops;
386 };
387
loops_params_init(struct loops_parse_params * p,FILE * i,FILE * o)388 static void loops_params_init(struct loops_parse_params *p, FILE *i, FILE *o)
389 {
390 if (p) {
391 p->in_file = i;
392 p->out_file = o;
393 p->ini_loops = NULL;
394 p->unclosed_loops = false;
395 } else {
396 printf("Structure to init in %s is invalid\n", __func__);
397 }
398 }
399
400 /*
401 * Check if a char array starts with a keyword and extracts the keyword.
402 * A keyword is any char array between '<' and '>' characters and is found at
403 * the beginning of the array and does not start with '/' character.
404 */
ini_line_begins_with_keyword(char * line,char * extracted_keyword)405 static bool ini_line_begins_with_keyword(char *line, char *extracted_keyword)
406 {
407 char *keyword_start = strstr(line, "<");
408 char *keyword_end = strstr(line, ">");
409 bool ret = false;
410
411 if (keyword_start && (keyword_start - line == 0) && keyword_end &&
412 *(++keyword_start) != '/') {
413 strncpy(extracted_keyword, keyword_start,
414 keyword_end - keyword_start);
415 extracted_keyword[keyword_end - keyword_start] = 0;
416 ret = true;
417 }
418
419 return ret;
420 }
421
422 /*
423 * Create and initialize a new ini_loop structure base on the supplied data.
424 * buf_with_loop - array containing loop instruction and all its parameters.
425 * loop_name - keyword used to identify the type of loop.
426 */
ini_loop_new(char * buf_with_loop,char * loop_name)427 static struct ini_loop * ini_loop_new(char *buf_with_loop, char *loop_name)
428 {
429 struct ini_loop *loop = NULL;
430 long long first, inc, last;
431 int loop_type;
432 char var[128];
433 gchar *for_raw_list = NULL;
434 gchar **for_values = NULL;
435 int ret;
436
437 /* Check loop type. Base on that gather up all of it's parameters */
438 if (!strncmp(loop_name, "SEQ", sizeof("SEQ") - 1)) {
439 loop_type = INI_LOOP_SEQ;
440
441 ret = sscanf(buf_with_loop, "<SEQ> %s %lli %lli %lli",
442 var, &first, &inc, &last);
443 if (ret != 4) {
444 ret = -EINVAL;
445 fprintf(stderr, "Unrecognized SEQ line\n");
446 goto err_close;
447 }
448 } else if (!strncmp(loop_name, "FOR", sizeof("FOR") - 1)) {
449 loop_type = INI_LOOP_FOR;
450
451 ret = sscanf(buf_with_loop, "<FOR> %s in {", var);
452 for_raw_list = g_strstr_len(buf_with_loop, -1, "{");
453 if (!for_raw_list) {
454 ret = -EINVAL;
455 fprintf(stderr, "Unrecognized FOR line\n");
456 goto err_close;
457 }
458 char *s = g_strdup(for_raw_list);
459 s = g_strstrip(g_strdelimit(s, "{}\n", ' '));
460 for_values = g_strsplit_set(s, " {}\n", -1);
461 g_free(s);
462 first = 0;
463 inc = 1;
464 last = g_strv_length(for_values) - 1;
465 } else {
466 ret = -EINVAL;
467 fprintf(stderr, "Unrecognized %s loop keyword\n", loop_name);
468 goto err_close;
469 }
470
471 /* Store all necessary parameters of a loop */
472 loop = calloc(sizeof(struct ini_loop), 1);
473 if (!loop) {
474 fprintf(stderr, "%s is %s", strerror(errno), __func__);
475 goto err_close;
476 }
477 loop->type = loop_type;
478 loop->first = first;
479 loop->inc = inc;
480 loop->last = last;
481 if (snprintf(loop->var, sizeof(loop->var), "<%s>", var) == sizeof(loop->var))
482 goto err_close;
483
484 if (snprintf(loop->end_loop, sizeof(loop->end_loop), "</%s>", loop_name) == sizeof(loop->end_loop))
485 goto err_close;
486
487 loop->for_values = for_values;
488
489 err_close:
490
491 return loop;
492 }
493
494 /*
495 * Get the current iteration that the loop is at.
496 */
ini_loop_get_iteration(struct ini_loop * loop)497 static char * ini_loop_get_iteration(struct ini_loop *loop)
498 {
499 char *iteration;
500
501 switch (loop->type) {
502 case INI_LOOP_SEQ:
503 iteration = g_strdup_printf(LONG_LONG_FORMAT, loop->i);
504 break;
505 case INI_LOOP_FOR:
506 iteration = g_strdup(loop->for_values[loop->i]);
507 break;
508 default:
509 iteration = NULL;
510 }
511
512 return iteration;
513 }
514
515 /*
516 * Expand a <loop>..</loop> structure and any other inner loop structures.
517 */
loop_expand(struct loops_parse_params * parse_params,char * buf_with_loop,char * loop_name)518 static int loop_expand(struct loops_parse_params *parse_params,
519 char *buf_with_loop, char *loop_name)
520 {
521 char *replace, *eol;
522 char buf[1024];
523 char inner_loop[128];
524 size_t tmplen;
525 fpos_t pos;
526 long long i, first, inc, last;
527 int ret = 0;
528 struct ini_loop *iniloop;
529 bool unclosed_loop = true;
530
531 iniloop = ini_loop_new(buf_with_loop, loop_name);
532 if (!iniloop)
533 goto err_close;
534
535 parse_params->ini_loops = g_slist_prepend(parse_params->ini_loops,
536 iniloop);
537
538 first = iniloop->first;
539 inc = iniloop->inc;
540 last = iniloop->last;
541 fgetpos(parse_params->in_file, &pos);
542 for (i = first; inc > 0 ? i <= last : i >= last; i = i + inc) {
543 fsetpos(parse_params->in_file, &pos);
544 iniloop->i = i;
545
546 while (fgets(buf, sizeof(buf), parse_params->in_file) != NULL) {
547 if (ini_line_begins_with_keyword(buf, inner_loop)) {
548 ret = loop_expand(parse_params, buf,
549 inner_loop);
550 if (ret < 0) {
551 goto err_close;
552 }
553 } else {
554 if (!strncmp(buf, iniloop->end_loop,
555 strlen(iniloop->end_loop))) {
556 unclosed_loop = false;
557 break;
558 }
559
560 GSList *list, *node;
561 struct ini_loop *loop;
562
563 list = parse_params->ini_loops;
564 replace = NULL;
565 for (node = list; node;
566 node = g_slist_next(node)) {
567 loop = (struct ini_loop *)node->data;
568 replace = strstr(buf, loop->var);
569 if (replace)
570 break;
571 }
572 if (!replace) {
573 fprintf(parse_params->out_file, "%s",
574 buf);
575 continue;
576 }
577
578 tmplen = strlen(iniloop->var);
579 eol = strchr(buf, '\0');
580 gchar *it = ini_loop_get_iteration(loop);
581 fprintf(parse_params->out_file, "%.*s%s%.*s",
582 (int) (long) (replace - buf), buf,
583 it, (int) (eol - replace - tmplen),
584 (char *)((uintptr_t) replace + tmplen));
585 if (it)
586 g_free(it);
587 }
588 }
589 }
590
591 err_close:
592
593 if (iniloop) {
594 parse_params->ini_loops = g_slist_remove(
595 parse_params->ini_loops, iniloop);
596 if (iniloop->for_values)
597 g_strfreev(iniloop->for_values);
598 free(iniloop);
599 }
600
601 if (unclosed_loop) {
602 parse_params->unclosed_loops = true;
603 ret = -EINVAL;
604 }
605
606 return ret;
607 }
608
ini_unroll(const char * input,const char * output)609 int ini_unroll(const char *input, const char *output)
610 {
611 FILE *in, *out;
612 char buf[1024];
613 char loop_name[128];
614 int ret = 0;
615 struct loops_parse_params loops_params;
616
617 in = fopen(input, "r");
618 out = fopen (output, "w");
619
620 if (!in) {
621 ret = -errno;
622 fprintf(stderr, "Failed to open %s : %s\n", input,
623 strerror(-ret));
624 goto err_close;
625 }
626
627 if (!out) {
628 ret = -errno;
629 fprintf(stderr, "Failed to open %s : %s\n", output,
630 strerror(-ret));
631 goto err_close;
632 }
633
634 loops_params_init(&loops_params, in, out);
635 while (fgets(buf, sizeof(buf), in) != NULL) {
636 if (!buf[0])
637 continue;
638
639 if (!ini_line_begins_with_keyword(buf, loop_name)) {
640 fprintf(out, "%s", buf);
641 continue;
642 }
643 if (!strcmp(loop_name, "COMMENT")) {
644 while (fgets(buf, sizeof(buf), in) != NULL) {
645 if (!strncmp(buf, "</COMMENT>", strlen("</COMMENT>")))
646 break;
647 }
648 continue;
649 }
650
651 ret = loop_expand(&loops_params, buf, loop_name);
652 if (ret < 0) {
653 if (loops_params.unclosed_loops)
654 printf("loop isn't closed in %s\n", input);
655 break;
656 }
657 }
658
659 err_close:
660 fclose(in);
661 fclose(out);
662 return ret;
663 }
664
write_driver_name_to_ini(FILE * f,const char * driver_name)665 void write_driver_name_to_ini(FILE *f, const char *driver_name)
666 {
667 if (driver_name) {
668 fwrite("\n[", 1, 2, f);
669 fwrite(driver_name, 1, strlen(driver_name), f);
670 fwrite("]\n", 1, 2, f);
671 }
672 }