LIRC libraries
LinuxInfraredRemoteControl
config_file.c
Go to the documentation of this file.
1 /****************************************************************************
2 ** config_file.c ***********************************************************
3 ****************************************************************************
4 *
5 *
6 * Copyright (C) 1998 Pablo d'Angelo <pablo@ag-trek.allgaeu.org>
7 *
8 */
9 
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 
21 #include <dirent.h>
22 #include <errno.h>
23 #include <glob.h>
24 #include <limits.h>
25 #include <unistd.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <libgen.h>
30 #include <sys/socket.h>
31 #include <sys/un.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <fcntl.h>
35 #include <ctype.h>
36 
37 #include "include/media/lirc.h"
38 #include "lirc/lirc_log.h"
39 #include "lirc/lirc_options.h"
40 #include "lirc/ir_remote.h"
41 #include "lirc/config_file.h"
42 #include "lirc/transmit.h"
43 #include "lirc/config_flags.h"
44 
45 
46 enum directive { ID_none, ID_remote, ID_codes, ID_raw_codes, ID_raw_name };
47 
48 struct ptr_array {
49  void** ptr;
50  size_t nr_items;
51  size_t chunk_size;
52 };
53 
54 struct void_array {
55  void* ptr;
56  size_t item_size;
57  size_t nr_items;
58  size_t chunk_size;
59 };
60 
61 
63 typedef void* (*array_guest_func)(void* item, void* arg);
64 
65 
66 #define LINE_LEN 1024
67 #define MAX_INCLUDES 10
68 
69 const char* whitespace = " \t";
70 
71 static int line;
72 static int parse_error;
73 
74 static struct ir_remote* read_config_recursive(FILE* f, const char* name, int depth);
75 static void calculate_signal_lengths(struct ir_remote* remote);
76 
77 void** init_void_array(struct void_array* ar, size_t chunk_size, size_t item_size)
78 {
79  ar->chunk_size = chunk_size;
80  ar->item_size = item_size;
81  ar->nr_items = 0;
82  ar->ptr = calloc(chunk_size, ar->item_size);
83  if (!ar->ptr) {
84  logprintf(LIRC_ERROR, "out of memory");
85  parse_error = 1;
86  return NULL;
87  }
88  return ar->ptr;
89 }
90 
91 const struct flaglist all_flags[] = {
92  { "RAW_CODES", RAW_CODES },
93  { "RC5", RC5 },
94  { "SHIFT_ENC", SHIFT_ENC }, /* obsolete */
95  { "RC6", RC6 },
96  { "RCMM", RCMM },
97  { "SPACE_ENC", SPACE_ENC },
98  { "SPACE_FIRST", SPACE_FIRST },
99  { "GOLDSTAR", GOLDSTAR },
100  { "GRUNDIG", GRUNDIG },
101  { "BO", BO },
102  { "SERIAL", SERIAL },
103  { "XMP", XMP },
104 
105  { "REVERSE", REVERSE },
106  { "NO_HEAD_REP", NO_HEAD_REP },
107  { "NO_FOOT_REP", NO_FOOT_REP },
108  { "CONST_LENGTH", CONST_LENGTH }, /* remember to adapt warning
109  * message when changing this */
110  { "REPEAT_HEADER", REPEAT_HEADER },
111  { NULL, 0 },
112 };
113 
114 
116 int add_void_array(struct void_array* ar, void* dataptr)
117 {
118  void* ptr;
119 
120  if ((ar->nr_items % ar->chunk_size) == (ar->chunk_size) - 1) {
121  /* I hope this works with the right alignment,
122  * if not we're screwed */
123  ptr = realloc(ar->ptr,
124  ar->item_size *
125  (ar->nr_items + ar->chunk_size + 1));
126  if (!ptr) {
127  logprintf(LIRC_ERROR, "out of memory");
128  parse_error = 1;
129  return 0;
130  }
131  ar->ptr = ptr;
132  }
133  memcpy((ar->ptr) + (ar->item_size * ar->nr_items), dataptr, ar->item_size);
134  ar->nr_items = (ar->nr_items) + 1;
135  memset((ar->ptr) + (ar->item_size * ar->nr_items), 0, ar->item_size);
136  return 1;
137 }
138 
139 
141 void* get_void_array(struct void_array* ar)
142 {
143  return ar->ptr;
144 }
145 
146 
151 static void*
152 foreach_void_array(struct void_array* ar, array_guest_func func, void* arg)
153 {
154  void* r;
155  int i;
156 
157  for (i = 0; i < ar->nr_items; i += 1) {
158  r = func(ar->ptr + (i * ar->item_size), arg);
159  if (r != NULL)
160  return r;
161  }
162  return NULL;
163 }
164 
165 
166 static int
167 ir_code_node_equals(struct ir_code_node* node1, struct ir_code_node* node2)
168 {
169  if (node1 == NULL || node2 == NULL)
170  return node1 == node2;
171  return node1->code == node2->code;
172 }
173 
174 
179 static void* array_guest_code_equals(void* arg1, void* arg2)
180 {
181 
182  struct ir_ncode* code1 = (struct ir_ncode*) arg1;
183  struct ir_ncode* code2 = (struct ir_ncode*) arg2;
184  struct ir_code_node* next1;
185  struct ir_code_node* next2;
186 
187  if (code1 == NULL || code2 == NULL)
188  return code1 == code2 ? arg1 : NULL;
189  if (code1->code != code2->code)
190  return NULL;
191  next1 = code1->next;
192  next2 = code2->next;
193  while (code1->next != NULL) {
194  if (!ir_code_node_equals(next1, next2))
195  return NULL;
196  next1 = code1->next;
197  next2 = code2->next;
198  }
199  return arg1;
200 }
201 
202 
207 static void* array_guest_ncode_cmp(void* item, void* arg)
208 {
209 
210  struct ir_ncode* code1 = (struct ir_ncode*) item;
211  struct ir_ncode* code2 = (struct ir_ncode*) arg;
212 
213  if (strcmp(code1->name, code2->name) == 0)
214  return item;
215  return NULL;
216 }
217 
218 
219 void* s_malloc(size_t size)
220 {
221  void* ptr;
222 
223  ptr = malloc(size);
224  if (ptr == NULL) {
225  logprintf(LIRC_ERROR, "out of memory");
226  parse_error = 1;
227  return NULL;
228  }
229  memset(ptr, 0, size);
230  return ptr;
231 }
232 
233 char* s_strdup(char* string)
234 {
235  char* ptr;
236 
237  ptr = strdup(string);
238  if (!ptr) {
239  logprintf(LIRC_ERROR, "out of memory");
240  parse_error = 1;
241  return NULL;
242  }
243  return ptr;
244 }
245 
246 ir_code s_strtocode(const char* val)
247 {
248  ir_code code = 0;
249  char* endptr;
250 
251  errno = 0;
252  code = strtoull(val, &endptr, 0);
253  if ((code == (__u64) -1 && errno == ERANGE) || strlen(endptr) != 0 || strlen(val) == 0) {
254  logprintf(LIRC_ERROR, "error in configfile line %d:", line);
255  logprintf(LIRC_ERROR, "\"%s\": must be a valid (__u64) number", val);
256  parse_error = 1;
257  return 0;
258  }
259  return code;
260 }
261 
262 __u32 s_strtou32(char* val)
263 {
264  __u32 n;
265  char* endptr;
266 
267  n = strtoul(val, &endptr, 0);
268  if (!*val || *endptr) {
269  logprintf(LIRC_ERROR, "error in configfile line %d:", line);
270  logprintf(LIRC_ERROR, "\"%s\": must be a valid (__u32) number", val);
271  parse_error = 1;
272  return 0;
273  }
274  return n;
275 }
276 
277 int s_strtoi(char* val)
278 {
279  char* endptr;
280  long n;
281  int h;
282 
283  n = strtol(val, &endptr, 0);
284  h = (int)n;
285  if (!*val || *endptr || n != ((long)h)) {
286  logprintf(LIRC_ERROR, "error in configfile line %d:", line);
287  logprintf(LIRC_ERROR, "\"%s\": must be a valid (int) number", val);
288  parse_error = 1;
289  return 0;
290  }
291  return h;
292 }
293 
294 unsigned int s_strtoui(char* val)
295 {
296  char* endptr;
297  __u32 n;
298  unsigned int h;
299 
300  n = strtoul(val, &endptr, 0);
301  h = (unsigned int)n;
302  if (!*val || *endptr || n != ((__u32)h)) {
303  logprintf(LIRC_ERROR, "error in configfile line %d:", line);
304  logprintf(LIRC_ERROR, "\"%s\": must be a valid (unsigned int) number", val);
305  parse_error = 1;
306  return 0;
307  }
308  return h;
309 }
310 
311 lirc_t s_strtolirc_t(char* val)
312 {
313  __u32 n;
314  lirc_t h;
315  char* endptr;
316 
317  n = strtoul(val, &endptr, 0);
318  h = (lirc_t)n;
319  if (!*val || *endptr || n != ((__u32)h)) {
320  logprintf(LIRC_ERROR, "error in configfile line %d:", line);
321  logprintf(LIRC_ERROR, "\"%s\": must be a valid (lirc_t) number", val);
322  parse_error = 1;
323  return 0;
324  }
325  if (h < 0) {
326  logprintf(LIRC_WARNING, "error in configfile line %d:", line);
327  logprintf(LIRC_WARNING, "\"%s\" is out of range", val);
328  }
329  return h;
330 }
331 
332 int checkMode(int is_mode, int c_mode, char* error)
333 {
334  if (is_mode != c_mode) {
335  logprintf(LIRC_ERROR, "fatal error in configfile line %d:", line);
336  logprintf(LIRC_ERROR, "\"%s\" isn't valid at this position", error);
337  parse_error = 1;
338  return 0;
339  }
340  return 1;
341 }
342 
343 int addSignal(struct void_array* signals, char* val)
344 {
345  unsigned int t;
346 
347  t = s_strtoui(val);
348  if (parse_error)
349  return 0;
350  if (!add_void_array(signals, &t))
351  return 0;
352 
353  return 1;
354 }
355 
356 struct ir_ncode* defineCode(char* key, char* val, struct ir_ncode* code)
357 {
358  memset(code, 0, sizeof(*code));
359  code->name = s_strdup(key);
360  code->code = s_strtocode(val);
361  LOGPRINTF(3, " %-20s 0x%016llX", code->name, code->code);
362  return code;
363 }
364 
365 struct ir_code_node* defineNode(struct ir_ncode* code, const char* val)
366 {
367  struct ir_code_node* node;
368 
369  node = s_malloc(sizeof(*node));
370  if (node == NULL)
371  return NULL;
372 
373  node->code = s_strtocode(val);
374  node->next = NULL;
375 
376  LOGPRINTF(3, " 0x%016llX", node->code);
377 
378  if (code->current == NULL) {
379  code->next = node;
380  code->current = node;
381  } else {
382  code->current->next = node;
383  code->current = node;
384  }
385  return node;
386 }
387 
388 int parseFlags(char* val)
389 {
390  const struct flaglist* flaglptr;
391  int flags = 0;
392  char* flag;
393  char* help;
394 
395  flag = help = val;
396  while (flag != NULL) {
397  while (*help != '|' && *help != 0)
398  help++;
399  if (*help == '|') {
400  *help = 0;
401  help++;
402  } else {
403  help = NULL;
404  }
405 
406  flaglptr = all_flags;
407  while (flaglptr->name != NULL) {
408  if (strcasecmp(flaglptr->name, flag) == 0) {
409  if (flaglptr->flag & IR_PROTOCOL_MASK && flags & IR_PROTOCOL_MASK) {
410  logprintf(LIRC_ERROR, "error in configfile line %d:", line);
411  logprintf(LIRC_ERROR, "multiple protocols given in flags: \"%s\"", flag);
412  parse_error = 1;
413  return 0;
414  }
415  flags = flags | flaglptr->flag;
416  LOGPRINTF(3, "flag %s recognized", flaglptr->name);
417  break;
418  }
419  flaglptr++;
420  }
421  if (flaglptr->name == NULL) {
422  logprintf(LIRC_ERROR, "error in configfile line %d:", line);
423  logprintf(LIRC_ERROR, "unknown flag: \"%s\"", flag);
424  parse_error = 1;
425  return 0;
426  }
427  flag = help;
428  }
429  LOGPRINTF(2, "flags value: %d", flags);
430 
431  return flags;
432 }
433 
434 int defineRemote(char* key, char* val, char* val2, struct ir_remote* rem)
435 {
436  if ((strcasecmp("name", key)) == 0) {
437  if (rem->name != NULL)
438  free((void*)(rem->name));
439  rem->name = s_strdup(val);
440  logprintf(LIRC_INFO, "Using remote: %s.", val);
441  return 1;
442  }
443  if (options_getboolean("lircd:dynamic-codes")) {
444  if ((strcasecmp("dyncodes_name", key)) == 0) {
445  if (rem->dyncodes_name != NULL)
446  free(rem->dyncodes_name);
447  rem->dyncodes_name = s_strdup(val);
448  return 1;
449  }
450  } else if (strcasecmp("driver", key) == 0) {
451  if (rem->driver != NULL)
452  free((void*)(rem->driver));
453  rem->driver = s_strdup(val);
454  return 1;
455  } else if ((strcasecmp("bits", key)) == 0) {
456  rem->bits = s_strtoi(val);
457  return 1;
458  } else if (strcasecmp("flags", key) == 0) {
459  rem->flags |= parseFlags(val);
460  return 1;
461  } else if (strcasecmp("eps", key) == 0) {
462  rem->eps = s_strtoi(val);
463  return 1;
464  } else if (strcasecmp("aeps", key) == 0) {
465  rem->aeps = s_strtoi(val);
466  return 1;
467  } else if (strcasecmp("plead", key) == 0) {
468  rem->plead = s_strtolirc_t(val);
469  return 1;
470  } else if (strcasecmp("ptrail", key) == 0) {
471  rem->ptrail = s_strtolirc_t(val);
472  return 1;
473  } else if (strcasecmp("pre_data_bits", key) == 0) {
474  rem->pre_data_bits = s_strtoi(val);
475  return 1;
476  } else if (strcasecmp("pre_data", key) == 0) {
477  rem->pre_data = s_strtocode(val);
478  return 1;
479  } else if (strcasecmp("post_data_bits", key) == 0) {
480  rem->post_data_bits = s_strtoi(val);
481  return 1;
482  } else if (strcasecmp("post_data", key) == 0) {
483  rem->post_data = s_strtocode(val);
484  return 1;
485  } else if (strcasecmp("gap", key) == 0) {
486  if (val2 != NULL)
487  rem->gap2 = s_strtou32(val2);
488  rem->gap = s_strtou32(val);
489  return val2 != NULL ? 2 : 1;
490  } else if (strcasecmp("repeat_gap", key) == 0) {
491  rem->repeat_gap = s_strtou32(val);
492  return 1;
493  } else if (strcasecmp("repeat_mask", key) == 0) {
494  rem->repeat_mask = s_strtocode(val);
495  return 1;
496  }
497  /* obsolete: use toggle_bit_mask instead */
498  else if (strcasecmp("toggle_bit", key) == 0) {
499  rem->toggle_bit = s_strtoi(val);
500  return 1;
501  } else if (strcasecmp("toggle_bit_mask", key) == 0) {
502  rem->toggle_bit_mask = s_strtocode(val);
503  return 1;
504  } else if (strcasecmp("toggle_mask", key) == 0) {
505  rem->toggle_mask = s_strtocode(val);
506  return 1;
507  } else if (strcasecmp("rc6_mask", key) == 0) {
508  rem->rc6_mask = s_strtocode(val);
509  return 1;
510  } else if (strcasecmp("ignore_mask", key) == 0) {
511  rem->ignore_mask = s_strtocode(val);
512  return 1;
513  } else if (strcasecmp("manual_sort", key) == 0) {
514  rem->manual_sort = s_strtoi(val);
515  return 1;
516  }
517  /* obsolete name */
518  else if (strcasecmp("repeat_bit", key) == 0) {
519  rem->toggle_bit = s_strtoi(val);
520  return 1;
521  } else if (strcasecmp("suppress_repeat", key) == 0) {
522  rem->suppress_repeat = s_strtoi(val);
523  return 1;
524  } else if (strcasecmp("min_repeat", key) == 0) {
525  rem->min_repeat = s_strtoi(val);
526  return 1;
527  } else if (strcasecmp("min_code_repeat", key) == 0) {
528  rem->min_code_repeat = s_strtoi(val);
529  return 1;
530  } else if (strcasecmp("frequency", key) == 0) {
531  rem->freq = s_strtoui(val);
532  return 1;
533  } else if (strcasecmp("duty_cycle", key) == 0) {
534  rem->duty_cycle = s_strtoui(val);
535  return 1;
536  } else if (strcasecmp("baud", key) == 0) {
537  rem->baud = s_strtoui(val);
538  return 1;
539  } else if (strcasecmp("serial_mode", key) == 0) {
540  if (val[0] < '5' || val[0] > '9') {
541  logprintf(LIRC_ERROR, "error in configfile line %d:", line);
542  logprintf(LIRC_ERROR, "bad bit count");
543  parse_error = 1;
544  return 0;
545  }
546  rem->bits_in_byte = val[0] - '0';
547  switch (toupper(val[1])) {
548  case 'N':
549  rem->parity = IR_PARITY_NONE;
550  break;
551  case 'E':
552  rem->parity = IR_PARITY_EVEN;
553  break;
554  case 'O':
555  rem->parity = IR_PARITY_ODD;
556  break;
557  default:
558  logprintf(LIRC_ERROR, "error in configfile line %d:", line);
559  logprintf(LIRC_ERROR, "unsupported parity mode");
560  parse_error = 1;
561  return 0;
562  }
563  if (strcmp(val + 2, "1.5") == 0)
564  rem->stop_bits = 3;
565  else
566  rem->stop_bits = s_strtoui(val + 2) * 2;
567  return 1;
568  } else if (val2 != NULL) {
569  if (strcasecmp("header", key) == 0) {
570  rem->phead = s_strtolirc_t(val);
571  rem->shead = s_strtolirc_t(val2);
572  return 2;
573  } else if (strcasecmp("three", key) == 0) {
574  rem->pthree = s_strtolirc_t(val);
575  rem->sthree = s_strtolirc_t(val2);
576  return 2;
577  } else if (strcasecmp("two", key) == 0) {
578  rem->ptwo = s_strtolirc_t(val);
579  rem->stwo = s_strtolirc_t(val2);
580  return 2;
581  } else if (strcasecmp("one", key) == 0) {
582  rem->pone = s_strtolirc_t(val);
583  rem->sone = s_strtolirc_t(val2);
584  return 2;
585  } else if (strcasecmp("zero", key) == 0) {
586  rem->pzero = s_strtolirc_t(val);
587  rem->szero = s_strtolirc_t(val2);
588  return 2;
589  } else if (strcasecmp("foot", key) == 0) {
590  rem->pfoot = s_strtolirc_t(val);
591  rem->sfoot = s_strtolirc_t(val2);
592  return 2;
593  } else if (strcasecmp("repeat", key) == 0) {
594  rem->prepeat = s_strtolirc_t(val);
595  rem->srepeat = s_strtolirc_t(val2);
596  return 2;
597  } else if (strcasecmp("pre", key) == 0) {
598  rem->pre_p = s_strtolirc_t(val);
599  rem->pre_s = s_strtolirc_t(val2);
600  return 2;
601  } else if (strcasecmp("post", key) == 0) {
602  rem->post_p = s_strtolirc_t(val);
603  rem->post_s = s_strtolirc_t(val2);
604  return 2;
605  }
606  }
607  if (val2) {
608  logprintf(LIRC_ERROR, "error in configfile line %d:", line);
609  logprintf(LIRC_ERROR, "unknown definiton: \"%s %s %s\"", key, val, val2);
610  } else {
611  logprintf(LIRC_ERROR, "error in configfile line %d:", line);
612  logprintf(LIRC_ERROR, "unknown definiton or too few arguments: \"%s %s\"", key, val);
613  }
614  parse_error = 1;
615  return 0;
616 }
617 
618 static int sanityChecks(struct ir_remote* rem, const char* path)
619 {
620  struct ir_ncode* codes;
621  struct ir_code_node* node;
622 
623  path = path != NULL ? path : "unknown file";
624 
625  if (!rem->name) {
626  logprintf(LIRC_ERROR,
627  "%s: %s: Missing remote name", path, rem);
628  return 0;
629  }
630  if (rem->gap == 0) {
631  logprintf(LIRC_WARNING, "%s: %s: Gap value missing or invalid",
632  path, rem->name);
633  }
634  if (has_repeat_gap(rem) && is_const(rem)) {
635  logprintf(LIRC_WARNING,
636  "%s: %s: Repeat_gap ignored (CONST_LENGTH is set)",
637  path, rem->name);
638  }
639 
640  if (is_raw(rem))
641  return 1;
642 
643  if ((rem->pre_data & gen_mask(rem->pre_data_bits)) != rem->pre_data) {
644  logprintf(LIRC_WARNING,
645  "%s: %s: Invalid pre_data", path, rem->name);
646  rem->pre_data &= gen_mask(rem->pre_data_bits);
647  }
648  if ((rem->post_data & gen_mask(rem->post_data_bits)) != rem->post_data) {
649  logprintf(LIRC_WARNING, "%s: %s: Invalid post_data",
650  path, rem->name);
651  rem->post_data &= gen_mask(rem->post_data_bits);
652  }
653  for (codes = rem->codes; codes->name != NULL; codes++) {
654  if ((codes->code & gen_mask(rem->bits)) != codes->code) {
655  logprintf(LIRC_WARNING, "%s: %s: Invalid code : %s",
656  path, rem->name, codes->name);
657  codes->code &= gen_mask(rem->bits);
658  }
659  for (node = codes->next; node != NULL; node = node->next) {
660  if ((node->code & gen_mask(rem->bits)) != node->code) {
661  logprintf(LIRC_WARNING, "%s: %s: Invalid code %s: %s",
662  path, rem->name, codes->name);
663  node->code &= gen_mask(rem->bits);
664  }
665  }
666  }
667  return 1;
668 }
669 
676 static int remote_bits_cmp(struct ir_remote* r1, struct ir_remote* r2)
677 {
678  int r1_size;
679  int r2_size;
680  struct ir_ncode* c;
681 
682  int r1_is_raw = is_raw(r1);
683  int r2_is_raw = is_raw(r2);
684 
685  if (!r1_is_raw && r2_is_raw)
686  return -1;
687  if (r1_is_raw && !r2_is_raw)
688  return 1;
689 
690  if (r1_is_raw && r2_is_raw) {
691  for (c = r1->codes, r1_size = 0; c->name != NULL; c++)
692  r1_size += 1;
693  for (c = r2->codes, r2_size = 0; c->name != NULL; c++)
694  r2_size += 1;
695  } else {
696  r1_size = bit_count(r1);
697  r2_size = bit_count(r2);
698  }
699  if (r1_size == r2_size)
700  return 0;
701  return r1_size < r2_size ? -1 : 1;
702 }
703 
704 
709 static struct ir_remote* sort_by_bit_count(struct ir_remote* remotes)
710 {
711  struct ir_remote* top;
712  struct ir_remote* rem;
713  struct ir_remote* next;
714  struct ir_remote* prev;
715  struct ir_remote* scan;
716  struct ir_remote* r;
717 
718  for (r = remotes; r != NULL && r != (void*)-1; r = r->next)
719  if (r->manual_sort)
720  return remotes;
721  rem = remotes;
722  top = NULL;
723  while (rem != NULL && rem != (void*)-1) {
724  next = rem->next;
725 
726  scan = top;
727  prev = NULL;
728  while (scan && remote_bits_cmp(scan, rem) <= 0) {
729  prev = scan;
730  scan = scan->next;
731  }
732  if (prev)
733  prev->next = rem;
734  else
735  top = rem;
736  if (scan)
737  rem->next = scan;
738  else
739  rem->next = NULL;
740 
741  rem = next;
742  }
743 
744  return top;
745 }
746 
747 static const char* lirc_parse_include(char* s)
748 {
749  char* last;
750  size_t len;
751 
752  len = strlen(s);
753  if (len < 2)
754  return NULL;
755  last = s + len - 1;
756  while (last > s && strchr(whitespace, *last) != NULL)
757  last--;
758  if (last <= s)
759  return NULL;
760  if (*s != '"' && *s != '<')
761  return NULL;
762  if (*s == '"' && *last != '"')
763  return NULL;
764  else if (*s == '<' && *last != '>')
765  return NULL;
766  *last = 0;
767  memmove(s, s + 1, len - 2 + 1); /* terminating 0 is copied, and
768  * maybe more, but we don't care */
769  return s;
770 }
771 
772 
774 static const char* lirc_parse_relative(char* dst,
775  size_t dst_size,
776  const char* child,
777  const char* current)
778 {
779  char* dir;
780  size_t dirlen;
781 
782  if (!current)
783  return child;
784 
785  /* Not a relative path */
786  if (*child == '/')
787  return child;
788 
789  if (strlen(current) >= dst_size)
790  return NULL;
791  strcpy(dst, current);
792  dir = dirname(dst);
793  dirlen = strlen(dir);
794  if (dir != dst)
795  memmove(dst, dir, dirlen + 1);
796 
797  if (dirlen + 1 + strlen(child) + 1 > dst_size)
798  return NULL;
799  strcat(dst, "/");
800  strcat(dst, child);
801 
802  return dst;
803 }
804 
805 
807 static struct ir_remote*
808 ir_remotes_append(struct ir_remote* root, struct ir_remote* what)
809 {
810  struct ir_remote* r;
811 
812  if (root == (struct ir_remote*)-1)
813  root = NULL;
814  if (what == (struct ir_remote*)-1)
815  what = NULL;
816  if (root == NULL && what != NULL)
817  return what;
818  if (what == NULL)
819  return root;
820  for (r = root; r->next != NULL; r = r->next)
821  ;
822  r->next = what;
823  return root;
824 }
825 
826 
827 struct ir_remote* read_config(FILE* f, const char* name)
828 {
829  struct ir_remote* head;
830 
831  head = read_config_recursive(f, name, 0);
832  head = sort_by_bit_count(head);
833  return head;
834 }
835 
836 
847 static struct ir_remote*
848 read_included(const char* name, int depth, char* val, struct ir_remote* top_rem)
849 {
850  FILE* childFile;
851  const char* childName;
852  struct ir_remote* rem = NULL;
853 
854  if (depth > MAX_INCLUDES) {
855  logprintf(LIRC_ERROR, "error opening child file defined at %s:%d", name, line);
856  logprintf(LIRC_ERROR, "too many files included");
857  return top_rem;
858  }
859  childName = lirc_parse_include(val);
860  if (!childName) {
861  logprintf(LIRC_ERROR, "error parsing child file value defined at line %d:", line);
862  logprintf(LIRC_ERROR, "invalid quoting");
863  return top_rem;
864  }
865  childFile = fopen(childName, "r");
866  if (childFile == NULL) {
867  logprintf(LIRC_ERROR, "error opening child file '%s' defined at line %d:",
868  childName, line);
869  logprintf(LIRC_ERROR, "ignoring this child file for now.");
870  return NULL;
871  }
872  rem = read_config_recursive(childFile, childName, depth + 1);
873  top_rem = ir_remotes_append(top_rem, rem);
874  fclose(childFile);
875  return top_rem;
876 }
877 
878 
889 static struct ir_remote* read_all_included(const char* name,
890  int depth,
891  char* val,
892  struct ir_remote* top_rem)
893 {
894  int i;
895  glob_t globbuf;
896  char buff[256] = { '\0' };
897 
898  memset(&globbuf, 0, sizeof(globbuf));
899  val = val + 1; // Strip quotes
900  val[strlen(val) - 1] = '\0';
901  lirc_parse_relative(buff, sizeof(buff), val, name);
902  glob(buff, 0, NULL, &globbuf);
903  for (i = 0; i < globbuf.gl_pathc; i += 1) {
904  snprintf(buff, sizeof(buff), "\"%s\"", globbuf.gl_pathv[i]);
905  top_rem = read_included(name, depth, buff, top_rem);
906  }
907  globfree(&globbuf);
908  return top_rem;
909 }
910 
911 
912 static void check_ncode_dups(const char* path,
913  const char* name,
914  struct void_array* ar,
915  struct ir_ncode* code)
916 {
917  if (foreach_void_array(ar, array_guest_ncode_cmp, code) != NULL) {
918  logprintf(LIRC_WARNING,
919  "%s: %s: Duplicate codes: %s",
920  path, name, code->name);
921  }
922  if (foreach_void_array(ar, array_guest_code_equals, code) != NULL) {
923  logprintf(LIRC_WARNING,
924  "%s: %s: Duplicate values: %s",
925  path, name, code->name);
926  }
927 }
928 
929 
930 static struct ir_remote*
931 read_config_recursive(FILE* f, const char* name, int depth)
932 {
933  char buf[LINE_LEN + 1];
934  char* key;
935  char* val;
936  char* val2;
937  int len, argc;
938  struct ir_remote* top_rem = NULL;
939  struct ir_remote* rem = NULL;
940  struct void_array codes_list, raw_codes, signals;
941  struct ir_ncode raw_code = { NULL, 0, 0, NULL };
942  struct ir_ncode name_code = { NULL, 0, 0, NULL };
943  struct ir_ncode* code;
944  int mode = ID_none;
945 
946  line = 0;
947  parse_error = 0;
948  LOGPRINTF(2, "parsing '%s'", name);
949 
950  while (fgets(buf, LINE_LEN, f) != NULL) {
951  line++;
952  len = strlen(buf);
953  if (len == LINE_LEN && buf[len - 1] != '\n') {
954  logprintf(LIRC_ERROR, "line %d too long in config file", line);
955  parse_error = 1;
956  break;
957  }
958 
959  if (len > 0) {
960  len--;
961  if (buf[len] == '\n')
962  buf[len] = 0;
963  }
964  if (len > 0) {
965  len--;
966  if (buf[len] == '\r')
967  buf[len] = 0;
968  }
969  /* ignore comments */
970  if (buf[0] == '#')
971  continue;
972  key = strtok(buf, whitespace);
973  /* ignore empty lines */
974  if (key == NULL)
975  continue;
976  val = strtok(NULL, whitespace);
977  if (val != NULL) {
978  val2 = strtok(NULL, whitespace);
979  LOGPRINTF(3, "Tokens: \"%s\" \"%s\" \"%s\"", key, val, (val2 == NULL ? "(null)" : val));
980  if (strcasecmp("include", key) == 0) {
981  int save_line = line;
982 
983  top_rem = read_all_included(name,
984  depth,
985  val,
986  top_rem);
987  line = save_line;
988  } else if (strcasecmp("begin", key) == 0) {
989  if (strcasecmp("codes", val) == 0) {
990  /* init codes mode */
991  LOGPRINTF(2, " begin codes");
992  if (!checkMode(mode, ID_remote, "begin codes"))
993  break;
994  if (rem->codes) {
995  logprintf(LIRC_ERROR, "error in configfile line %d:", line);
996  logprintf(LIRC_ERROR, "codes are already defined");
997  parse_error = 1;
998  break;
999  }
1000 
1001  init_void_array(&codes_list, 30, sizeof(struct ir_ncode));
1002  mode = ID_codes;
1003  } else if (strcasecmp("raw_codes", val) == 0) {
1004  /* init raw_codes mode */
1005  LOGPRINTF(2, " begin raw_codes");
1006  if (!checkMode(mode, ID_remote, "begin raw_codes"))
1007  break;
1008  if (rem->codes) {
1009  logprintf(LIRC_ERROR, "error in configfile line %d:", line);
1010  logprintf(LIRC_ERROR, "codes are already defined");
1011  parse_error = 1;
1012  break;
1013  }
1014  set_protocol(rem, RAW_CODES);
1015  raw_code.code = 0;
1016  init_void_array(&raw_codes, 30, sizeof(struct ir_ncode));
1017  mode = ID_raw_codes;
1018  } else if (strcasecmp("remote", val) == 0) {
1019  /* create new remote */
1020  LOGPRINTF(1, "parsing remote");
1021  if (!checkMode(mode, ID_none, "begin remote"))
1022  break;
1023  mode = ID_remote;
1024  if (!top_rem) {
1025  /* create first remote */
1026  LOGPRINTF(2, "creating first remote");
1027  rem = top_rem = s_malloc(sizeof(struct ir_remote));
1028  } else {
1029  /* create new remote */
1030  LOGPRINTF(2, "creating next remote");
1031  rem = s_malloc(sizeof(struct ir_remote));
1032  ir_remotes_append(top_rem, rem);
1033  }
1034  } else if (mode == ID_codes) {
1035  code = defineCode(key, val, &name_code);
1036  while (!parse_error && val2 != NULL) {
1037  if (val2[0] == '#')
1038  break; /* comment */
1039  defineNode(code, val2);
1040  val2 = strtok(NULL, whitespace);
1041  }
1042  code->current = NULL;
1043  check_ncode_dups(name, rem->name, &codes_list, code);
1044  add_void_array(&codes_list, code);
1045  } else {
1046  logprintf(LIRC_ERROR, "error in configfile line %d:", line);
1047  logprintf(LIRC_ERROR, "unknown section \"%s\"", val);
1048  parse_error = 1;
1049  }
1050  if (!parse_error && val2 != NULL) {
1051  logprintf(LIRC_WARNING,
1052  "%s: garbage after '%s' token "
1053  "in line %d ignored",
1054  rem->name, val, line);
1055  }
1056  } else if (strcasecmp("end", key) == 0) {
1057  if (strcasecmp("codes", val) == 0) {
1058  /* end Codes mode */
1059  LOGPRINTF(2, " end codes");
1060  if (!checkMode(mode, ID_codes, "end codes"))
1061  break;
1062  rem->codes = get_void_array(&codes_list);
1063  mode = ID_remote; /* switch back */
1064  } else if (strcasecmp("raw_codes", val) == 0) {
1065  /* end raw codes mode */
1066  LOGPRINTF(2, " end raw_codes");
1067 
1068  if (mode == ID_raw_name) {
1069  raw_code.signals = get_void_array(&signals);
1070  raw_code.length = signals.nr_items;
1071  if (raw_code.length % 2 == 0) {
1072  logprintf(LIRC_ERROR, "error in configfile line %d:", line);
1073  logprintf(LIRC_ERROR, "bad signal length");
1074  parse_error = 1;
1075  }
1076  if (!add_void_array(&raw_codes, &raw_code))
1077  break;
1078  mode = ID_raw_codes;
1079  }
1080  if (!checkMode(mode, ID_raw_codes, "end raw_codes"))
1081  break;
1082  rem->codes = get_void_array(&raw_codes);
1083  mode = ID_remote; /* switch back */
1084  } else if (strcasecmp("remote", val) == 0) {
1085  /* end remote mode */
1086  LOGPRINTF(2, "end remote");
1087  /* print_remote(rem); */
1088  if (!checkMode(mode, ID_remote, "end remote"))
1089  break;
1090  if (!sanityChecks(rem, name)) {
1091  parse_error = 1;
1092  break;
1093  }
1094  if (options_getboolean("lircd:dynamic-codes")) {
1095  if (rem->dyncodes_name == NULL)
1096  rem->dyncodes_name = s_strdup("unknown");
1097  rem->dyncodes[0].name = rem->dyncodes_name;
1098  rem->dyncodes[1].name = rem->dyncodes_name;
1099  }
1100  /* not really necessary because we
1101  * clear the alloced memory */
1102  rem->next = NULL;
1103  rem->last_code = NULL;
1104  mode = ID_none; /* switch back */
1105  } else if (mode == ID_codes) {
1106  code = defineCode(key, val, &name_code);
1107  while (!parse_error && val2 != NULL) {
1108  if (val2[0] == '#')
1109  break; /* comment */
1110  defineNode(code, val2);
1111  val2 = strtok(NULL, whitespace);
1112  }
1113  code->current = NULL;
1114  add_void_array(&codes_list, code);
1115  } else {
1116  logprintf(LIRC_ERROR, "error in configfile line %d:", line);
1117  logprintf(LIRC_ERROR, "unknown section %s", val);
1118  parse_error = 1;
1119  }
1120  if (!parse_error && val2 != NULL) {
1121  logprintf(LIRC_WARNING,
1122  "%s: garbage after '%s'"
1123  " token in line %d ignored",
1124  rem->name, val, line);
1125  }
1126  } else {
1127  switch (mode) {
1128  case ID_remote:
1129  argc = defineRemote(key, val, val2, rem);
1130  if (!parse_error
1131  && ((argc == 1 && val2 != NULL)
1132  || (argc == 2 && val2 != NULL && strtok(NULL, whitespace) != NULL))) {
1133  logprintf(LIRC_WARNING,
1134  "%s: garbage after '%s'"
1135  " token in line %d ignored",
1136  rem->name, key, line);
1137  }
1138  break;
1139  case ID_codes:
1140  code = defineCode(key, val, &name_code);
1141  while (!parse_error && val2 != NULL) {
1142  if (val2[0] == '#')
1143  break; /* comment */
1144  defineNode(code, val2);
1145  val2 = strtok(NULL, whitespace);
1146  }
1147  code->current = NULL;
1148  check_ncode_dups(name,
1149  rem->name,
1150  &codes_list,
1151  code);
1152  add_void_array(&codes_list, code);
1153  break;
1154  case ID_raw_codes:
1155  case ID_raw_name:
1156  if (strcasecmp("name", key) == 0) {
1157  LOGPRINTF(3, "Button: \"%s\"", val);
1158  if (mode == ID_raw_name) {
1159  raw_code.signals = get_void_array(&signals);
1160  raw_code.length = signals.nr_items;
1161  if (raw_code.length % 2 == 0) {
1162  logprintf(LIRC_ERROR, "error in configfile line %d:",
1163  line);
1164  logprintf(LIRC_ERROR, "bad signal length");
1165  parse_error = 1;
1166  }
1167  if (!add_void_array(&raw_codes, &raw_code))
1168  break;
1169  }
1170  raw_code.name = s_strdup(val);
1171  if (!raw_code.name)
1172  break;
1173  raw_code.code++;
1174  init_void_array(&signals, 50, sizeof(lirc_t));
1175  mode = ID_raw_name;
1176  if (!parse_error && val2 != NULL) {
1177  logprintf(LIRC_WARNING,
1178  "%s: garbage after '%s'"
1179  " token in line %d ignored",
1180  rem->name, key, line);
1181  }
1182  } else {
1183  if (mode == ID_raw_codes) {
1184  logprintf(LIRC_ERROR, "no name for signal defined at line %d",
1185  line);
1186  parse_error = 1;
1187  break;
1188  }
1189  if (!addSignal(&signals, key))
1190  break;
1191  if (!addSignal(&signals, val))
1192  break;
1193  if (val2)
1194  if (!addSignal(&signals, val2))
1195  break;
1196  while ((val = strtok(NULL, whitespace)))
1197  if (!addSignal(&signals, val))
1198  break;
1199  }
1200  break;
1201  }
1202  }
1203  } else if (mode == ID_raw_name) {
1204  if (!addSignal(&signals, key))
1205  break;
1206  } else {
1207  logprintf(LIRC_ERROR, "error in configfile line %d", line);
1208  parse_error = 1;
1209  break;
1210  }
1211  if (parse_error)
1212  break;
1213  }
1214  if (mode != ID_none) {
1215  switch (mode) {
1216  case ID_raw_name:
1217  if (raw_code.name != NULL) {
1218  free(raw_code.name);
1219  if (get_void_array(&signals) != NULL)
1220  free(get_void_array(&signals));
1221  }
1222  case ID_raw_codes:
1223  rem->codes = get_void_array(&raw_codes);
1224  break;
1225  case ID_codes:
1226  rem->codes = get_void_array(&codes_list);
1227  break;
1228  }
1229  if (!parse_error) {
1230  logprintf(LIRC_ERROR, "unexpected end of file");
1231  parse_error = 1;
1232  }
1233  }
1234  if (parse_error) {
1235  static int print_error = 1;
1236 
1237  if (print_error) {
1238  logprintf(LIRC_ERROR, "reading of file '%s' failed", name);
1239  print_error = 0;
1240  }
1241  free_config(top_rem);
1242  if (depth == 0)
1243  print_error = 1;
1244  return (void*)-1;
1245  }
1246  /* kick reverse flag */
1247  /* handle RC6 flag to be backwards compatible: previous RC-6
1248  * config files did not set rc6_mask */
1249  rem = top_rem;
1250  while (rem != NULL) {
1251  if ((!is_raw(rem)) && rem->flags & REVERSE) {
1252  struct ir_ncode* codes;
1253 
1254  if (has_pre(rem))
1255  rem->pre_data = reverse(rem->pre_data, rem->pre_data_bits);
1256  if (has_post(rem))
1257  rem->post_data = reverse(rem->post_data, rem->post_data_bits);
1258  codes = rem->codes;
1259  while (codes->name != NULL) {
1260  codes->code = reverse(codes->code, rem->bits);
1261  codes++;
1262  }
1263  rem->flags = rem->flags & (~REVERSE);
1264  rem->flags = rem->flags | COMPAT_REVERSE;
1265  /* don't delete the flag because we still need
1266  * it to remain compatible with older versions
1267  */
1268  }
1269  if (rem->flags & RC6 && rem->rc6_mask == 0 && rem->toggle_bit > 0) {
1270  int all_bits = bit_count(rem);
1271 
1272  rem->rc6_mask = ((ir_code)1) << (all_bits - rem->toggle_bit);
1273  }
1274  if (rem->toggle_bit > 0) {
1275  int all_bits = bit_count(rem);
1276 
1277  if (has_toggle_bit_mask(rem))
1278  logprintf(LIRC_WARNING, "%s uses both toggle_bit and toggle_bit_mask", rem->name);
1279  else
1280  rem->toggle_bit_mask = ((ir_code)1) << (all_bits - rem->toggle_bit);
1281  rem->toggle_bit = 0;
1282  }
1283  if (has_toggle_bit_mask(rem)) {
1284  if (!is_raw(rem) && rem->codes) {
1285  rem->toggle_bit_mask_state = (rem->codes->code & rem->toggle_bit_mask);
1286  if (rem->toggle_bit_mask_state)
1287  /* start with state set to 0 for backwards compatibility */
1288  rem->toggle_bit_mask_state ^= rem->toggle_bit_mask;
1289  }
1290  }
1291  if (is_serial(rem)) {
1292  lirc_t base;
1293 
1294  if (rem->baud > 0) {
1295  base = 1000000 / rem->baud;
1296  if (rem->pzero == 0 && rem->szero == 0)
1297  rem->pzero = base;
1298  if (rem->pone == 0 && rem->sone == 0)
1299  rem->sone = base;
1300  }
1301  if (rem->bits_in_byte == 0)
1302  rem->bits_in_byte = 8;
1303  }
1304  if (rem->min_code_repeat > 0) {
1305  if (!has_repeat(rem) || rem->min_code_repeat > rem->min_repeat) {
1306  logprintf(LIRC_WARNING, "invalid min_code_repeat value");
1307  rem->min_code_repeat = 0;
1308  }
1309  }
1310  calculate_signal_lengths(rem);
1311  rem = rem->next;
1312  }
1313 
1314  return top_rem;
1315 }
1316 
1317 void calculate_signal_lengths(struct ir_remote* remote)
1318 {
1319  if (is_const(remote)) {
1320  remote->min_total_signal_length = min_gap(remote);
1321  remote->max_total_signal_length = max_gap(remote);
1322  } else {
1323  remote->min_gap_length = min_gap(remote);
1324  remote->max_gap_length = max_gap(remote);
1325  }
1326 
1327  lirc_t min_signal_length = 0, max_signal_length = 0;
1328  lirc_t max_pulse = 0, max_space = 0;
1329  int first_sum = 1;
1330  struct ir_ncode* c = remote->codes;
1331  int i;
1332 
1333  while (c->name) {
1334  struct ir_ncode code = *c;
1335  struct ir_code_node* next = code.next;
1336  int first = 1;
1337  int repeat = 0;
1338 
1339  do {
1340  if (first) {
1341  first = 0;
1342  } else {
1343  code.code = next->code;
1344  next = next->next;
1345  }
1346  for (repeat = 0; repeat < 2; repeat++) {
1347  if (init_sim(remote, &code, repeat)) {
1348  lirc_t sum = send_buffer_sum();
1349 
1350  if (sum) {
1351  if (first_sum || sum < min_signal_length)
1352  min_signal_length = sum;
1353  if (first_sum || sum > max_signal_length)
1354  max_signal_length = sum;
1355  first_sum = 0;
1356  }
1357  for (i = 0; i < send_buffer_length(); i++) {
1358  if (i & 1) { /* space */
1359  if (send_buffer_data()[i] > max_space)
1360  max_space = send_buffer_data()[i];
1361  } else { /* pulse */
1362  if (send_buffer_data()[i] > max_pulse)
1363  max_pulse = send_buffer_data()[i];
1364  }
1365  }
1366  }
1367  }
1368  } while (next);
1369  c++;
1370  }
1371  if (first_sum) {
1372  /* no timing data, so assume gap is the actual total
1373  * length */
1374  remote->min_total_signal_length = min_gap(remote);
1375  remote->max_total_signal_length = max_gap(remote);
1376  remote->min_gap_length = min_gap(remote);
1377  remote->max_gap_length = max_gap(remote);
1378  } else if (is_const(remote)) {
1379  if (remote->min_total_signal_length > max_signal_length) {
1380  remote->min_gap_length = remote->min_total_signal_length - max_signal_length;
1381  } else {
1382  logprintf(LIRC_WARNING,
1383  "min_gap_length is 0 for '%s' remote",
1384  remote->name);
1385  remote->min_gap_length = 0;
1386  }
1387  if (remote->max_total_signal_length > min_signal_length) {
1388  remote->max_gap_length = remote->max_total_signal_length - min_signal_length;
1389  } else {
1390  logprintf(LIRC_WARNING, "max_gap_length is 0 for '%s' remote", remote->name);
1391  remote->max_gap_length = 0;
1392  }
1393  } else {
1394  remote->min_total_signal_length = min_signal_length + remote->min_gap_length;
1395  remote->max_total_signal_length = max_signal_length + remote->max_gap_length;
1396  }
1397  LOGPRINTF(1, "lengths: %lu %lu %lu %lu", remote->min_total_signal_length, remote->max_total_signal_length,
1398  remote->min_gap_length, remote->max_gap_length);
1399 }
1400 
1401 void free_config(struct ir_remote* remotes)
1402 {
1403  struct ir_remote* next;
1404  struct ir_ncode* codes;
1405 
1406  while (remotes != NULL) {
1407  next = remotes->next;
1408 
1409  if (remotes->dyncodes_name != NULL)
1410  free(remotes->dyncodes_name);
1411  if (remotes->name != NULL)
1412  free((void*)(remotes->name));
1413  if (remotes->codes != NULL) {
1414  codes = remotes->codes;
1415  while (codes->name != NULL) {
1416  struct ir_code_node* node;
1417  struct ir_code_node* next_node;
1418 
1419  free(codes->name);
1420  if (codes->signals != NULL)
1421  free(codes->signals);
1422  node = codes->next;
1423  while (node) {
1424  next_node = node->next;
1425  free(node);
1426  node = next_node;
1427  }
1428  codes++;
1429  }
1430  free(remotes->codes);
1431  }
1432  free(remotes);
1433  remotes = next;
1434  }
1435 }
const lirc_t * send_buffer_data(void)
Definition: transmit.c:372
lirc_t min_total_signal_length
#define NO_FOOT_REP
#define RC6
unsigned int freq
lirc_t max_gap_length
void *(* array_guest_func)(void *item, void *arg)
Definition: config_file.c:63
#define GOLDSTAR
lirc_t max_total_signal_length
ir_code post_data
ir_code repeat_mask
struct ir_code_node * next
unsigned int baud
const char * name
#define SPACE_ENC
void free_config(struct ir_remote *remotes)
Definition: config_file.c:1401
lirc_t * signals
#define COMPAT_REVERSE
struct ir_ncode * last_code
unsigned int parity
__u64 ir_code
lirc_t min_gap_length
char * name
struct ir_code_node * current
unsigned int duty_cycle
#define RCMM
ir_code toggle_mask
ir_code pre_data
lirc_t send_buffer_sum(void)
Definition: transmit.c:377
#define RC5
char * dyncodes_name
#define REPEAT_HEADER
#define SPACE_FIRST
__u32 repeat_gap
#define CONST_LENGTH
#define NO_HEAD_REP
unsigned int stop_bits
#define GRUNDIG
void * get_void_array(struct void_array *ar)
Definition: config_file.c:141
#define LOGPRINTF(level, fmt, args...)
Definition: lirc_log.h:75
unsigned int aeps
lirc_t srepeat
#define SERIAL
#define SHIFT_ENC
#define BO
ir_code code
const char * driver
unsigned int min_code_repeat
const struct flaglist all_flags[]
Definition: config_file.c:91
struct ir_ncode dyncodes[2]
char * name
Definition: config_flags.h:14
ir_code rc6_mask
int send_buffer_length(void)
Definition: transmit.c:366
ir_code toggle_bit_mask
#define RAW_CODES
struct ir_remote * read_config(FILE *f, const char *name)
Definition: config_file.c:827
ir_code ignore_mask
#define XMP
unsigned int bits_in_byte
int add_void_array(struct void_array *ar, void *dataptr)
Definition: config_file.c:116