libs/alsa-lib-1.0.13/src/conf.c

Go to the documentation of this file.
00001 
00013 /*
00014  *  Configuration helper functions
00015  *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>,
00016  *                        Jaroslav Kysela <perex@suse.cz>
00017  *
00018  *
00019  *   This library is free software; you can redistribute it and/or modify
00020  *   it under the terms of the GNU Lesser General Public License as
00021  *   published by the Free Software Foundation; either version 2.1 of
00022  *   the License, or (at your option) any later version.
00023  *
00024  *   This program is distributed in the hope that it will be useful,
00025  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00026  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00027  *   GNU Lesser General Public License for more details.
00028  *
00029  *   You should have received a copy of the GNU Lesser General Public
00030  *   License along with this library; if not, write to the Free Software
00031  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
00032  *
00033  */
00034 
00417 #include <stdarg.h>
00418 #include <dlfcn.h>
00419 #include <limits.h>
00420 #include <sys/stat.h>
00421 #include <pthread.h>
00422 #include <locale.h>
00423 #include "local.h"
00424 
00425 #ifndef DOC_HIDDEN
00426 
00427 struct _snd_config {
00428         char *id;
00429         snd_config_type_t type;
00430         union {
00431                 long integer;
00432                 long long integer64;
00433                 char *string;
00434                 double real;
00435                 const void *ptr;
00436                 struct {
00437                         struct list_head fields;
00438                         int join;
00439                 } compound;
00440         } u;
00441         struct list_head list;
00442         snd_config_t *father;
00443         int hop;
00444 };
00445 
00446 struct filedesc {
00447         char *name;
00448         snd_input_t *in;
00449         unsigned int line, column;
00450         struct filedesc *next;
00451 };
00452 
00453 #define LOCAL_ERROR                     (-0x68000000)
00454 
00455 #define LOCAL_UNTERMINATED_STRING       (LOCAL_ERROR - 0)
00456 #define LOCAL_UNTERMINATED_QUOTE        (LOCAL_ERROR - 1)
00457 #define LOCAL_UNEXPECTED_CHAR           (LOCAL_ERROR - 2)
00458 #define LOCAL_UNEXPECTED_EOF            (LOCAL_ERROR - 3)
00459 
00460 typedef struct {
00461         struct filedesc *current;
00462         int unget;
00463         int ch;
00464 } input_t;
00465 
00466 static int safe_strtoll(const char *str, long long *val)
00467 {
00468         long long v;
00469         int endidx;
00470         if (!*str)
00471                 return -EINVAL;
00472         errno = 0;
00473         if (sscanf(str, "%Li%n", &v, &endidx) < 1)
00474                 return -EINVAL;
00475         if (str[endidx])
00476                 return -EINVAL;
00477         *val = v;
00478         return 0;
00479 }
00480 
00481 int safe_strtol(const char *str, long *val)
00482 {
00483         char *end;
00484         long v;
00485         if (!*str)
00486                 return -EINVAL;
00487         errno = 0;
00488         v = strtol(str, &end, 0);
00489         if (errno)
00490                 return -errno;
00491         if (*end)
00492                 return -EINVAL;
00493         *val = v;
00494         return 0;
00495 }
00496 
00497 static int safe_strtod(const char *str, double *val)
00498 {
00499         char *end;
00500         double v;
00501         char *saved_locale;
00502         char locstr[64]; /* enough? */
00503         int err;
00504 
00505         if (!*str)
00506                 return -EINVAL;
00507         saved_locale = setlocale(LC_NUMERIC, NULL);
00508         if (saved_locale) {
00509                 snprintf(locstr, sizeof(locstr), "%s", saved_locale);
00510                 setlocale(LC_NUMERIC, "C");
00511         }
00512         errno = 0;
00513         v = strtod(str, &end);
00514         err = -errno;
00515         if (saved_locale)
00516                 setlocale(LC_NUMERIC, locstr);
00517         if (err)
00518                 return err;
00519         if (*end)
00520                 return -EINVAL;
00521         *val = v;
00522         return 0;
00523 }
00524 
00525 static int get_char(input_t *input)
00526 {
00527         int c;
00528         struct filedesc *fd;
00529         if (input->unget) {
00530                 input->unget = 0;
00531                 return input->ch;
00532         }
00533  again:
00534         fd = input->current;
00535         c = snd_input_getc(fd->in);
00536         switch (c) {
00537         case '\n':
00538                 fd->column = 0;
00539                 fd->line++;
00540                 break;
00541         case '\t':
00542                 fd->column += 8 - fd->column % 8;
00543                 break;
00544         case EOF:
00545                 if (fd->next) {
00546                         snd_input_close(fd->in);
00547                         free(fd->name);
00548                         input->current = fd->next;
00549                         free(fd);
00550                         goto again;
00551                 }
00552                 return LOCAL_UNEXPECTED_EOF;
00553         default:
00554                 fd->column++;
00555                 break;
00556         }
00557         return (unsigned char)c;
00558 }
00559 
00560 static void unget_char(int c, input_t *input)
00561 {
00562         assert(!input->unget);
00563         input->ch = c;
00564         input->unget = 1;
00565 }
00566 
00567 static int get_delimstring(char **string, int delim, input_t *input);
00568 
00569 static int get_char_skip_comments(input_t *input)
00570 {
00571         int c;
00572         while (1) {
00573                 c = get_char(input);
00574                 if (c == '<') {
00575                         char *str;
00576                         snd_input_t *in;
00577                         struct filedesc *fd;
00578                         int err = get_delimstring(&str, '>', input);
00579                         if (err < 0)
00580                                 return err;
00581                         if (!strncmp(str, "confdir:", 8)) {
00582                                 char *tmp = malloc(strlen("/share" "/alsa") + 1 + strlen(str + 8) + 1);
00583                                 if (tmp == NULL) {
00584                                         free(str);
00585                                         return -ENOMEM;
00586                                 }
00587                                 sprintf(tmp, "/share" "/alsa/%s", str + 8);
00588                                 free(str);
00589                                 str = tmp;
00590                         }
00591                         err = snd_input_stdio_open(&in, str, "r");
00592                         if (err < 0) {
00593                                 free(str);
00594                                 return err;
00595                         }
00596                         fd = malloc(sizeof(*fd));
00597                         if (!fd) {
00598                                 free(str);
00599                                 return -ENOMEM;
00600                         }
00601                         fd->name = str;
00602                         fd->in = in;
00603                         fd->next = input->current;
00604                         fd->line = 1;
00605                         fd->column = 0;
00606                         input->current = fd;
00607                         continue;
00608                 }
00609                 if (c != '#')
00610                         break;
00611                 while (1) {
00612                         c = get_char(input);
00613                         if (c < 0)
00614                                 return c;
00615                         if (c == '\n')
00616                                 break;
00617                 }
00618         }
00619                 
00620         return c;
00621 }
00622                         
00623 
00624 static int get_nonwhite(input_t *input)
00625 {
00626         int c;
00627         while (1) {
00628                 c = get_char_skip_comments(input);
00629                 switch (c) {
00630                 case ' ':
00631                 case '\f':
00632                 case '\t':
00633                 case '\n':
00634                 case '\r':
00635                         break;
00636                 default:
00637                         return c;
00638                 }
00639         }
00640 }
00641 
00642 static int get_quotedchar(input_t *input)
00643 {
00644         int c;
00645         c = get_char(input);
00646         switch (c) {
00647         case 'n':
00648                 return '\n';
00649         case 't':
00650                 return '\t';
00651         case 'v':
00652                 return '\v';
00653         case 'b':
00654                 return '\b';
00655         case 'r':
00656                 return '\r';
00657         case 'f':
00658                 return '\f';
00659         case '0' ... '7':
00660         {
00661                 int num = c - '0';
00662                 int i = 1;
00663                 do {
00664                         c = get_char(input);
00665                         if (c < '0' || c > '7') {
00666                                 unget_char(c, input);
00667                                 break;
00668                         }
00669                         num = num * 8 + c - '0';
00670                         i++;
00671                 } while (i < 3);
00672                 return num;
00673         }
00674         default:
00675                 return c;
00676         }
00677 }
00678 
00679 #define LOCAL_STR_BUFSIZE       64
00680 struct local_string {
00681         char *buf;
00682         size_t alloc;
00683         size_t idx;
00684         char tmpbuf[LOCAL_STR_BUFSIZE];
00685 };
00686 
00687 static void init_local_string(struct local_string *s)
00688 {
00689         memset(s, 0, sizeof(*s));
00690         s->buf = s->tmpbuf;
00691         s->alloc = LOCAL_STR_BUFSIZE;
00692 }
00693 
00694 static void free_local_string(struct local_string *s)
00695 {
00696         if (s->buf != s->tmpbuf)
00697                 free(s->buf);
00698 }
00699 
00700 static int add_char_local_string(struct local_string *s, int c)
00701 {
00702         if (s->idx >= s->alloc) {
00703                 size_t nalloc = s->alloc * 2;
00704                 if (s->buf == s->tmpbuf) {
00705                         s->buf = malloc(nalloc);
00706                         if (s->buf == NULL)
00707                                 return -ENOMEM;
00708                         memcpy(s->buf, s->tmpbuf, s->alloc);
00709                 } else {
00710                         char *ptr = realloc(s->buf, nalloc);
00711                         if (ptr == NULL)
00712                                 return -ENOMEM;
00713                         s->buf = ptr;
00714                 }
00715                 s->alloc = nalloc;
00716         }
00717         s->buf[s->idx++] = c;
00718         return 0;
00719 }
00720 
00721 static char *copy_local_string(struct local_string *s)
00722 {
00723         char *dst = malloc(s->idx + 1);
00724         if (dst) {
00725                 memcpy(dst, s->buf, s->idx);
00726                 dst[s->idx] = '\0';
00727         }
00728         return dst;
00729 }
00730 
00731 static int get_freestring(char **string, int id, input_t *input)
00732 {
00733         struct local_string str;
00734         int c;
00735 
00736         init_local_string(&str);
00737         while (1) {
00738                 c = get_char(input);
00739                 if (c < 0) {
00740                         if (c == LOCAL_UNEXPECTED_EOF) {
00741                                 *string = copy_local_string(&str);
00742                                 if (! *string)
00743                                         c = -ENOMEM;
00744                                 else
00745                                         c = 0;
00746                         }
00747                         break;
00748                 }
00749                 switch (c) {
00750                 case '.':
00751                         if (!id)
00752                                 break;
00753                 case ' ':
00754                 case '\f':
00755                 case '\t':
00756                 case '\n':
00757                 case '\r':
00758                 case '=':
00759                 case ',':
00760                 case ';':
00761                 case '{':
00762                 case '}':
00763                 case '[':
00764                 case ']':
00765                 case '\'':
00766                 case '"':
00767                 case '\\':
00768                 case '#':
00769                         *string = copy_local_string(&str);
00770                         if (! *string)
00771                                 c = -ENOMEM;
00772                         else {
00773                                 unget_char(c, input);
00774                                 c = 0;
00775                         }
00776                         goto _out;
00777                 default:
00778                         break;
00779                 }
00780                 if (add_char_local_string(&str, c) < 0) {
00781                         c = -ENOMEM;
00782                         break;
00783                 }
00784         }
00785  _out:
00786         free_local_string(&str);
00787         return c;
00788 }
00789                         
00790 static int get_delimstring(char **string, int delim, input_t *input)
00791 {
00792         struct local_string str;
00793         int c;
00794 
00795         init_local_string(&str);
00796         while (1) {
00797                 c = get_char(input);
00798                 if (c < 0)
00799                         break;
00800                 if (c == '\\') {
00801                         c = get_quotedchar(input);
00802                         if (c < 0)
00803                                 break;
00804                         if (c == '\n')
00805                                 continue;
00806                 } else if (c == delim) {
00807                         *string = copy_local_string(&str);
00808                         if (! *string)
00809                                 c = -ENOMEM;
00810                         else
00811                                 c = 0;
00812                         break;
00813                 }
00814                 if (add_char_local_string(&str, c) < 0) {
00815                         c = -ENOMEM;
00816                         break;
00817                 }
00818         }
00819          free_local_string(&str);
00820          return c;
00821 }
00822 
00823 /* Return 0 for free string, 1 for delimited string */
00824 static int get_string(char **string, int id, input_t *input)
00825 {
00826         int c = get_nonwhite(input), err;
00827         if (c < 0)
00828                 return c;
00829         switch (c) {
00830         case '=':
00831         case ',':
00832         case ';':
00833         case '.':
00834         case '{':
00835         case '}':
00836         case '[':
00837         case ']':
00838         case '\\':
00839                 return LOCAL_UNEXPECTED_CHAR;
00840         case '\'':
00841         case '"':
00842                 err = get_delimstring(string, c, input);
00843                 if (err < 0)
00844                         return err;
00845                 return 1;
00846         default:
00847                 unget_char(c, input);
00848                 err = get_freestring(string, id, input);
00849                 if (err < 0)
00850                         return err;
00851                 return 0;
00852         }
00853 }
00854 
00855 static int _snd_config_make(snd_config_t **config, char **id, snd_config_type_t type)
00856 {
00857         snd_config_t *n;
00858         assert(config);
00859         n = calloc(1, sizeof(*n));
00860         if (n == NULL) {
00861                 if (*id) {
00862                         free(*id);
00863                         *id = NULL;
00864                 }
00865                 return -ENOMEM;
00866         }
00867         if (id) {
00868                 n->id = *id;
00869                 *id = NULL;
00870         }
00871         n->type = type;
00872         if (type == SND_CONFIG_TYPE_COMPOUND)
00873                 INIT_LIST_HEAD(&n->u.compound.fields);
00874         *config = n;
00875         return 0;
00876 }
00877         
00878 
00879 static int _snd_config_make_add(snd_config_t **config, char **id,
00880                                 snd_config_type_t type, snd_config_t *father)
00881 {
00882         snd_config_t *n;
00883         int err;
00884         assert(father->type == SND_CONFIG_TYPE_COMPOUND);
00885         err = _snd_config_make(&n, id, type);
00886         if (err < 0)
00887                 return err;
00888         n->father = father;
00889         list_add_tail(&n->list, &father->u.compound.fields);
00890         *config = n;
00891         return 0;
00892 }
00893 
00894 static int _snd_config_search(snd_config_t *config, 
00895                               const char *id, int len, snd_config_t **result)
00896 {
00897         snd_config_iterator_t i, next;
00898         snd_config_for_each(i, next, config) {
00899                 snd_config_t *n = snd_config_iterator_entry(i);
00900                 if (len < 0) {
00901                         if (strcmp(n->id, id) != 0)
00902                                 continue;
00903                 } else if (strlen(n->id) != (size_t) len ||
00904                            memcmp(n->id, id, (size_t) len) != 0)
00905                                 continue;
00906                 if (result)
00907                         *result = n;
00908                 return 0;
00909         }
00910         return -ENOENT;
00911 }
00912 
00913 static int parse_value(snd_config_t **_n, snd_config_t *father, input_t *input, char **id, int skip)
00914 {
00915         snd_config_t *n = *_n;
00916         char *s;
00917         int err;
00918 
00919         err = get_string(&s, 0, input);
00920         if (err < 0)
00921                 return err;
00922         if (skip) {
00923                 free(s);
00924                 return 0;
00925         }
00926         if (err == 0 && ((s[0] >= '0' && s[0] <= '9') || s[0] == '-')) {
00927                 long long i;
00928                 errno = 0;
00929                 err = safe_strtoll(s, &i);
00930                 if (err < 0) {
00931                         double r;
00932                         err = safe_strtod(s, &r);
00933                         if (err >= 0) {
00934                                 free(s);
00935                                 if (n) {
00936                                         if (n->type != SND_CONFIG_TYPE_REAL) {
00937                                                 SNDERR("%s is not a real", *id);
00938                                                 return -EINVAL;
00939                                         }
00940                                 } else {
00941                                         err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_REAL, father);
00942                                         if (err < 0)
00943                                                 return err;
00944                                 }
00945                                 n->u.real = r;
00946                                 *_n = n;
00947                                 return 0;
00948                         }
00949                 } else {
00950                         free(s);
00951                         if (n) {
00952                                 if (n->type != SND_CONFIG_TYPE_INTEGER && n->type != SND_CONFIG_TYPE_INTEGER64) {
00953                                         SNDERR("%s is not an integer", *id);
00954                                         return -EINVAL;
00955                                 }
00956                         } else {
00957                                 if (i <= INT_MAX) 
00958                                         err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_INTEGER, father);
00959                                 else
00960                                         err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_INTEGER64, father);
00961                                 if (err < 0)
00962                                         return err;
00963                         }
00964                         if (n->type == SND_CONFIG_TYPE_INTEGER) 
00965                                 n->u.integer = (long) i;
00966                         else 
00967                                 n->u.integer64 = i;
00968                         *_n = n;
00969                         return 0;
00970                 }
00971         }
00972         if (n) {
00973                 if (n->type != SND_CONFIG_TYPE_STRING) {
00974                         SNDERR("%s is not a string", *id);
00975                         free(s);
00976                         return -EINVAL;
00977                 }
00978         } else {
00979                 err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_STRING, father);
00980                 if (err < 0)
00981                         return err;
00982         }
00983         free(n->u.string);
00984         n->u.string = s;
00985         *_n = n;
00986         return 0;
00987 }
00988 
00989 static int parse_defs(snd_config_t *father, input_t *input, int skip, int override);
00990 static int parse_array_defs(snd_config_t *farther, input_t *input, int skip, int override);
00991 
00992 static int parse_array_def(snd_config_t *father, input_t *input, int idx, int skip, int override)
00993 {
00994         char *id = NULL;
00995         int c;
00996         int err;
00997         snd_config_t *n = NULL;
00998 
00999         if (!skip) {
01000                 char static_id[12];
01001                 snprintf(static_id, sizeof(static_id), "%i", idx);
01002                 id = strdup(static_id);
01003                 if (id == NULL)
01004                         return -ENOMEM;
01005         }
01006         c = get_nonwhite(input);
01007         if (c < 0) {
01008                 err = c;
01009                 goto __end;
01010         }
01011         switch (c) {
01012         case '{':
01013         case '[':
01014         {
01015                 char endchr;
01016                 if (!skip) {
01017                         if (n) {
01018                                 if (n->type != SND_CONFIG_TYPE_COMPOUND) {
01019                                         SNDERR("%s is not a compound", id);
01020                                         err = -EINVAL;
01021                                         goto __end;
01022                                 }
01023                         } else {
01024                                 err = _snd_config_make_add(&n, &id, SND_CONFIG_TYPE_COMPOUND, father);
01025                                 if (err < 0)
01026                                         goto __end;
01027                         }
01028                 }
01029                 if (c == '{') {
01030                         err = parse_defs(n, input, skip, override);
01031                         endchr = '}';
01032                 } else {
01033                         err = parse_array_defs(n, input, skip, override);
01034                         endchr = ']';
01035                 }
01036                 c = get_nonwhite(input);
01037                 if (c < 0) {
01038                         err = c;
01039                         goto __end;
01040                 }
01041                 if (c != endchr) {
01042                         if (n)
01043                                 snd_config_delete(n);
01044                         err = LOCAL_UNEXPECTED_CHAR;
01045                         goto __end;
01046                 }
01047                 break;
01048         }
01049         default:
01050                 unget_char(c, input);
01051                 err = parse_value(&n, father, input, &id, skip);
01052                 if (err < 0)
01053                         goto __end;
01054                 break;
01055         }
01056         err = 0;
01057       __end:
01058         free(id);
01059         return err;
01060 }
01061 
01062 static int parse_array_defs(snd_config_t *father, input_t *input, int skip, int override)
01063 {
01064         int idx = 0;
01065         while (1) {
01066                 int c = get_nonwhite(input), err;
01067                 if (c < 0)
01068                         return c;
01069                 unget_char(c, input);
01070                 if (c == ']')
01071                         return 0;
01072                 err = parse_array_def(father, input, idx++, skip, override);
01073                 if (err < 0)
01074                         return err;
01075         }
01076         return 0;
01077 }
01078 
01079 static int parse_def(snd_config_t *father, input_t *input, int skip, int override)
01080 {
01081         char *id = NULL;
01082         int c;
01083         int err;
01084         snd_config_t *n;
01085         enum {MERGE_CREATE, MERGE, OVERRIDE, DONT_OVERRIDE} mode;
01086         while (1) {
01087                 c = get_nonwhite(input);
01088                 if (c < 0)
01089                         return c;
01090                 switch (c) {
01091                 case '+':
01092                         mode = MERGE_CREATE;
01093                         break;
01094                 case '-':
01095                         mode = MERGE;
01096                         break;
01097                 case '?':
01098                         mode = DONT_OVERRIDE;
01099                         break;
01100                 case '!':
01101                         mode = OVERRIDE;
01102                         break;
01103                 default:
01104                         mode = !override ? MERGE_CREATE : OVERRIDE;
01105                         unget_char(c, input);
01106                 }
01107                 err = get_string(&id, 1, input);
01108                 if (err < 0)
01109                         return err;
01110                 c = get_nonwhite(input);
01111                 if (c != '.')
01112                         break;
01113                 if (skip) {
01114                         free(id);
01115                         continue;
01116                 }
01117                 if (_snd_config_search(father, id, -1, &n) == 0) {
01118                         if (mode == DONT_OVERRIDE) {
01119                                 skip = 1;
01120                                 free(id);
01121                                 continue;
01122                         }
01123                         if (mode != OVERRIDE) {
01124                                 if (n->type != SND_CONFIG_TYPE_COMPOUND) {
01125                                         SNDERR("%s is not a compound", id);
01126                                         return -EINVAL;
01127                                 }
01128                                 n->u.compound.join = 1;
01129                                 father = n;
01130                                 free(id);
01131                                 continue;
01132                         }
01133                         snd_config_delete(n);
01134                 }
01135                 if (mode == MERGE) {
01136                         SNDERR("%s does not exists", id);
01137                         err = -ENOENT;
01138                         goto __end;
01139                 }
01140                 err = _snd_config_make_add(&n, &id, SND_CONFIG_TYPE_COMPOUND, father);
01141                 if (err < 0)
01142                         goto __end;
01143                 n->u.compound.join = 1;
01144                 father = n;
01145         }
01146         if (c == '=') {
01147                 c = get_nonwhite(input);
01148                 if (c < 0)
01149                         return c;
01150         }
01151         if (!skip) {
01152                 if (_snd_config_search(father, id, -1, &n) == 0) {
01153                         if (mode == DONT_OVERRIDE) {
01154                                 skip = 1;
01155                                 n = NULL;
01156                         } else if (mode == OVERRIDE) {
01157                                 snd_config_delete(n);
01158                                 n = NULL;
01159                         }
01160                 } else {
01161                         n = NULL;
01162                         if (mode == MERGE) {
01163                                 SNDERR("%s does not exists", id);
01164                                 err = -ENOENT;
01165                                 goto __end;
01166                         }
01167                 }
01168         }
01169         switch (c) {
01170         case '{':
01171         case '[':
01172         {
01173                 char endchr;
01174                 if (!skip) {
01175                         if (n) {
01176                                 if (n->type != SND_CONFIG_TYPE_COMPOUND) {
01177                                         SNDERR("%s is not a compound", id);
01178                                         err = -EINVAL;
01179                                         goto __end;
01180                                 }
01181                         } else {
01182                                 err = _snd_config_make_add(&n, &id, SND_CONFIG_TYPE_COMPOUND, father);
01183                                 if (err < 0)
01184                                         goto __end;
01185                         }
01186                 }
01187                 if (c == '{') {
01188                         err = parse_defs(n, input, skip, override);
01189                         endchr = '}';
01190                 } else {
01191                         err = parse_array_defs(n, input, skip, override);
01192                         endchr = ']';
01193                 }
01194                 c = get_nonwhite(input);
01195                 if (c != endchr) {
01196                         if (n)
01197                                 snd_config_delete(n);
01198                         err = LOCAL_UNEXPECTED_CHAR;
01199                         goto __end;
01200                 }
01201                 break;
01202         }
01203         default:
01204                 unget_char(c, input);
01205                 err = parse_value(&n, father, input, &id, skip);
01206                 if (err < 0)
01207                         goto __end;
01208                 break;
01209         }
01210         c = get_nonwhite(input);
01211         switch (c) {
01212         case ';':
01213         case ',':
01214                 break;
01215         default:
01216                 unget_char(c, input);
01217         }
01218       __end:
01219         free(id);
01220         return err;
01221 }
01222                 
01223 static int parse_defs(snd_config_t *father, input_t *input, int skip, int override)
01224 {
01225         int c, err;
01226         while (1) {
01227                 c = get_nonwhite(input);
01228                 if (c < 0)
01229                         return c == LOCAL_UNEXPECTED_EOF ? 0 : c;
01230                 unget_char(c, input);
01231                 if (c == '}')
01232                         return 0;
01233                 err = parse_def(father, input, skip, override);
01234                 if (err < 0)
01235                         return err;
01236         }
01237         return 0;
01238 }
01239 
01240 static void string_print(char *str, int id, snd_output_t *out)
01241 {
01242         unsigned char *p = (unsigned char *)str;
01243         if (!id) {
01244                 switch (*p) {
01245                 case 0:
01246                         assert(0);
01247                         break;
01248                 case '0' ... '9':
01249                 case '-':
01250                         goto quoted;
01251                 }
01252         }
01253         if (!*p) {
01254                 snd_output_puts(out, "''");
01255                 return;
01256         }
01257  loop:
01258         switch (*p) {
01259         case 0:
01260                 goto nonquoted;
01261         case 1 ... 31:
01262         case 127 ... 255:
01263         case ' ':
01264         case '=':
01265         case ';':
01266         case ',':
01267         case '.':
01268         case '{':
01269         case '}':
01270         case '\'':
01271         case '"':
01272                 goto quoted;
01273         default:
01274                 p++;
01275                 goto loop;
01276         }
01277  nonquoted:
01278         snd_output_puts(out, str);
01279         return;
01280  quoted:
01281         snd_output_putc(out, '\'');
01282         p = (unsigned char *)str;
01283         while (*p) {
01284                 int c;
01285                 c = *p;
01286                 switch (c) {
01287                 case '\n':
01288                         snd_output_putc(out, '\\');
01289                         snd_output_putc(out, 'n');
01290                         break;
01291                 case '\t':
01292                         snd_output_putc(out, '\\');
01293                         snd_output_putc(out, 't');
01294                         break;
01295                 case '\v':
01296                         snd_output_putc(out, '\\');
01297                         snd_output_putc(out, 'v');
01298                         break;
01299                 case '\b':
01300                         snd_output_putc(out, '\\');
01301                         snd_output_putc(out, 'b');
01302                         break;
01303                 case '\r':
01304                         snd_output_putc(out, '\\');
01305                         snd_output_putc(out, 'r');
01306                         break;
01307                 case '\f':
01308                         snd_output_putc(out, '\\');
01309                         snd_output_putc(out, 'f');
01310                         break;
01311                 case '\'':
01312                         snd_output_putc(out, '\\');
01313                         snd_output_putc(out, c);
01314                         break;
01315                 case 32 ... '\'' - 1:
01316                 case '\'' + 1 ... 126:
01317                         snd_output_putc(out, c);
01318                         break;
01319                 default:
01320                         snd_output_printf(out, "\\%04o", c);
01321                         break;
01322                 }
01323                 p++;
01324         }
01325         snd_output_putc(out, '\'');
01326 }
01327 
01328 static int _snd_config_save_leaves(snd_config_t *config, snd_output_t *out, unsigned int level, unsigned int joins);
01329 
01330 static int _snd_config_save_leaf(snd_config_t *n, snd_output_t *out, 
01331                                  unsigned int level)
01332 {
01333         int err;
01334         unsigned int k;
01335         switch (n->type) {
01336         case SND_CONFIG_TYPE_INTEGER:
01337                 snd_output_printf(out, "%ld", n->u.integer);
01338                 break;
01339         case SND_CONFIG_TYPE_INTEGER64:
01340                 snd_output_printf(out, "%Ld", n->u.integer64);
01341                 break;
01342         case SND_CONFIG_TYPE_REAL:
01343                 snd_output_printf(out, "%-16g", n->u.real);
01344                 break;
01345         case SND_CONFIG_TYPE_STRING:
01346                 string_print(n->u.string, 0, out);
01347                 break;
01348         case SND_CONFIG_TYPE_POINTER:
01349                 SNDERR("cannot save runtime pointer type");
01350                 return -EINVAL;
01351         case SND_CONFIG_TYPE_COMPOUND:
01352                 snd_output_putc(out, '{');
01353                 snd_output_putc(out, '\n');
01354                 err = _snd_config_save_leaves(n, out, level + 1, 0);
01355                 if (err < 0)
01356                         return err;
01357                 for (k = 0; k < level; ++k) {
01358                         snd_output_putc(out, '\t');
01359                 }
01360                 snd_output_putc(out, '}');
01361                 break;
01362         }
01363         return 0;
01364 }
01365 
01366 static void id_print(snd_config_t *n, snd_output_t *out, unsigned int joins)
01367 {
01368         if (joins > 0) {
01369                 assert(n->father);
01370                 id_print(n->father, out, joins - 1);
01371                 snd_output_putc(out, '.');
01372         }
01373         string_print(n->id, 1, out);
01374 }
01375 
01376 static int _snd_config_save_leaves(snd_config_t *config, snd_output_t *out, unsigned int level, unsigned int joins)
01377 {
01378         unsigned int k;
01379         int err;
01380         snd_config_iterator_t i, next;
01381         assert(config && out);
01382         snd_config_for_each(i, next, config) {
01383                 snd_config_t *n = snd_config_iterator_entry(i);
01384                 if (n->type == SND_CONFIG_TYPE_COMPOUND &&
01385                     n->u.compound.join) {
01386                         err = _snd_config_save_leaves(n, out, level, joins + 1);
01387                         if (err < 0)
01388                                 return err;
01389                         continue;
01390                 }
01391                 for (k = 0; k < level; ++k) {
01392                         snd_output_putc(out, '\t');
01393                 }
01394                 id_print(n, out, joins);
01395 #if 0
01396                 snd_output_putc(out, ' ');
01397                 snd_output_putc(out, '=');
01398 #endif
01399                 snd_output_putc(out, ' ');
01400                 err = _snd_config_save_leaf(n, out, level);
01401                 if (err < 0)
01402                         return err;
01403 #if 0
01404                 snd_output_putc(out, ';');
01405 #endif
01406                 snd_output_putc(out, '\n');
01407         }
01408         return 0;
01409 }
01410 #endif
01411 
01412 
01428 int snd_config_substitute(snd_config_t *dst, snd_config_t *src)
01429 {
01430         assert(dst && src);
01431         if (dst->type == SND_CONFIG_TYPE_COMPOUND &&
01432             src->type == SND_CONFIG_TYPE_COMPOUND) {    /* append */
01433                 snd_config_iterator_t i, next;
01434                 snd_config_for_each(i, next, src) {
01435                         snd_config_t *n = snd_config_iterator_entry(i);
01436                         n->father = dst;
01437                 }
01438                 src->u.compound.fields.next->prev = &dst->u.compound.fields;
01439                 src->u.compound.fields.prev->next = &dst->u.compound.fields;
01440         } else if (dst->type == SND_CONFIG_TYPE_COMPOUND) {
01441                 int err;
01442                 err = snd_config_delete_compound_members(dst);
01443                 if (err < 0)
01444                         return err;
01445         }
01446         free(dst->id);
01447         dst->id = src->id;
01448         dst->type = src->type;
01449         dst->u = src->u;
01450         free(src);
01451         return 0;
01452 }
01453 
01461 int snd_config_get_type_ascii(const char *ascii, snd_config_type_t *type)
01462 {
01463         assert(ascii && type);
01464         if (!strcmp(ascii, "integer")) {
01465                 *type = SND_CONFIG_TYPE_INTEGER;
01466                 return 0;
01467         }
01468         if (!strcmp(ascii, "integer64")) {
01469                 *type = SND_CONFIG_TYPE_INTEGER64;
01470                 return 0;
01471         }
01472         if (!strcmp(ascii, "real")) {
01473                 *type = SND_CONFIG_TYPE_REAL;
01474                 return 0;
01475         }
01476         if (!strcmp(ascii, "string")) {
01477                 *type = SND_CONFIG_TYPE_STRING;
01478                 return 0;
01479         }
01480         if (!strcmp(ascii, "compound")) {
01481                 *type = SND_CONFIG_TYPE_COMPOUND;
01482                 return 0;
01483         }
01484         return -EINVAL;
01485 }
01486 
01492 snd_config_type_t snd_config_get_type(const snd_config_t *config)
01493 {
01494         return config->type;
01495 }
01496 
01507 int snd_config_get_id(const snd_config_t *config, const char **id)
01508 {
01509         assert(config && id);
01510         *id = config->id;
01511         return 0;
01512 }
01513 
01520 int snd_config_set_id(snd_config_t *config, const char *id)
01521 {
01522         char *new_id;
01523         assert(config && id);
01524         new_id = strdup(id);
01525         if (!new_id)
01526                 return -ENOMEM;
01527         free(config->id);
01528         config->id = new_id;
01529         return 0;
01530 }
01531 
01540 int snd_config_top(snd_config_t **config)
01541 {
01542         assert(config);
01543         return _snd_config_make(config, 0, SND_CONFIG_TYPE_COMPOUND);
01544 }
01545 
01546 static int snd_config_load1(snd_config_t *config, snd_input_t *in, int override)
01547 {
01548         int err;
01549         input_t input;
01550         struct filedesc *fd, *fd_next;
01551         assert(config && in);
01552         fd = malloc(sizeof(*fd));
01553         if (!fd)
01554                 return -ENOMEM;
01555         fd->name = NULL;
01556         fd->in = in;
01557         fd->line = 1;
01558         fd->column = 0;
01559         fd->next = NULL;
01560         input.current = fd;
01561         input.unget = 0;
01562         err = parse_defs(config, &input, 0, override);
01563         fd = input.current;
01564         if (err < 0) {
01565                 const char *str;
01566                 switch (err) {
01567                 case LOCAL_UNTERMINATED_STRING:
01568                         str = "Unterminated string";
01569                         err = -EINVAL;
01570                         break;
01571                 case LOCAL_UNTERMINATED_QUOTE:
01572                         str = "Unterminated quote";
01573                         err = -EINVAL;
01574                         break;
01575                 case LOCAL_UNEXPECTED_CHAR:
01576                         str = "Unexpected char";
01577                         err = -EINVAL;
01578                         break;
01579                 case LOCAL_UNEXPECTED_EOF:
01580                         str = "Unexpected end of file";
01581                         err = -EINVAL;
01582                         break;
01583                 default:
01584                         str = strerror(-err);
01585                         break;
01586                 }
01587                 SNDERR("%s:%d:%d:%s", fd->name ? fd->name : "_toplevel_", fd->line, fd->column, str);
01588                 goto _end;
01589         }
01590         if (get_char(&input) != LOCAL_UNEXPECTED_EOF) {
01591                 SNDERR("%s:%d:%d:Unexpected }", fd->name ? fd->name : "", fd->line, fd->column);
01592                 err = -EINVAL;
01593                 goto _end;
01594         }
01595  _end:
01596         while (fd->next) {
01597                 fd_next = fd->next;
01598                 snd_input_close(fd->in);
01599                 free(fd->name);
01600                 free(fd);
01601                 fd = fd_next;
01602         }
01603         free(fd);
01604         return err;
01605 }
01606 
01613 int snd_config_load(snd_config_t *config, snd_input_t *in)
01614 {
01615         return snd_config_load1(config, in, 0);
01616 }
01617 
01624 int snd_config_load_override(snd_config_t *config, snd_input_t *in)
01625 {
01626         return snd_config_load1(config, in, 1);
01627 }
01628 
01635 int snd_config_add(snd_config_t *father, snd_config_t *leaf)
01636 {
01637         snd_config_iterator_t i, next;
01638         assert(father && leaf);
01639         snd_config_for_each(i, next, father) {
01640                 snd_config_t *n = snd_config_iterator_entry(i);
01641                 if (strcmp(leaf->id, n->id) == 0)
01642                         return -EEXIST;
01643         }
01644         leaf->father = father;
01645         list_add_tail(&leaf->list, &father->u.compound.fields);
01646         return 0;
01647 }
01648 
01656 int snd_config_remove(snd_config_t *config)
01657 {
01658         assert(config);
01659         if (config->father)
01660                 list_del(&config->list);
01661         config->father = NULL;
01662         return 0;
01663 }
01664 
01674 int snd_config_delete(snd_config_t *config)
01675 {
01676         assert(config);
01677         switch (config->type) {
01678         case SND_CONFIG_TYPE_COMPOUND:
01679         {
01680                 int err;
01681                 struct list_head *i;
01682                 i = config->u.compound.fields.next;
01683                 while (i != &config->u.compound.fields) {
01684                         struct list_head *nexti = i->next;
01685                         snd_config_t *leaf = snd_config_iterator_entry(i);
01686                         err = snd_config_delete(leaf);
01687                         if (err < 0)
01688                                 return err;
01689                         i = nexti;
01690                 }
01691                 break;
01692         }
01693         case SND_CONFIG_TYPE_STRING:
01694                 free(config->u.string);
01695                 break;
01696         default:
01697                 break;
01698         }
01699         if (config->father)
01700                 list_del(&config->list);
01701         free(config->id);
01702         free(config);
01703         return 0;
01704 }
01705 
01713 int snd_config_delete_compound_members(const snd_config_t *config)
01714 {
01715         int err;
01716         struct list_head *i;
01717 
01718         assert(config);
01719         if (config->type != SND_CONFIG_TYPE_COMPOUND)
01720                 return -EINVAL;
01721         i = config->u.compound.fields.next;
01722         while (i != &config->u.compound.fields) {
01723                 struct list_head *nexti = i->next;
01724                 snd_config_t *leaf = snd_config_iterator_entry(i);
01725                 err = snd_config_delete(leaf);
01726                 if (err < 0)
01727                         return err;
01728                 i = nexti;
01729         }
01730         return 0;
01731 }
01732 
01741 int snd_config_make(snd_config_t **config, const char *id,
01742                     snd_config_type_t type)
01743 {
01744         char *id1;
01745         assert(config);
01746         if (id) {
01747                 id1 = strdup(id);
01748                 if (!id1)
01749                         return -ENOMEM;
01750         } else
01751                 id1 = NULL;
01752         return _snd_config_make(config, &id1, type);
01753 }
01754 
01764 int snd_config_make_integer(snd_config_t **config, const char *id)
01765 {
01766         return snd_config_make(config, id, SND_CONFIG_TYPE_INTEGER);
01767 }
01768 
01778 int snd_config_make_integer64(snd_config_t **config, const char *id)
01779 {
01780         return snd_config_make(config, id, SND_CONFIG_TYPE_INTEGER64);
01781 }
01782 
01792 int snd_config_make_real(snd_config_t **config, const char *id)
01793 {
01794         return snd_config_make(config, id, SND_CONFIG_TYPE_REAL);
01795 }
01796 
01806 int snd_config_make_string(snd_config_t **config, const char *id)
01807 {
01808         return snd_config_make(config, id, SND_CONFIG_TYPE_STRING);
01809 }
01810 
01820 int snd_config_make_pointer(snd_config_t **config, const char *id)
01821 {
01822         return snd_config_make(config, id, SND_CONFIG_TYPE_POINTER);
01823 }
01824 
01834 int snd_config_make_compound(snd_config_t **config, const char *id,
01835                              int join)
01836 {
01837         int err;
01838         err = snd_config_make(config, id, SND_CONFIG_TYPE_COMPOUND);
01839         if (err < 0)
01840                 return err;
01841         (*config)->u.compound.join = join;
01842         return 0;
01843 }
01844 
01853 int snd_config_imake_integer(snd_config_t **config, const char *id, const long value)
01854 {
01855         int err;
01856         
01857         err = snd_config_make(config, id, SND_CONFIG_TYPE_INTEGER);
01858         if (err < 0)
01859                 return err;
01860         (*config)->u.integer = value;
01861         return 0;
01862 }
01863 
01872 int snd_config_imake_integer64(snd_config_t **config, const char *id, const long long value)
01873 {
01874         int err;
01875         
01876         err = snd_config_make(config, id, SND_CONFIG_TYPE_INTEGER64);
01877         if (err < 0)
01878                 return err;
01879         (*config)->u.integer64 = value;
01880         return 0;
01881 }
01882 
01891 int snd_config_imake_real(snd_config_t **config, const char *id, const double value)
01892 {
01893         int err;
01894         
01895         err = snd_config_make(config, id, SND_CONFIG_TYPE_REAL);
01896         if (err < 0)
01897                 return err;
01898         (*config)->u.real = value;
01899         return 0;
01900 }
01901 
01912 int snd_config_imake_string(snd_config_t **config, const char *id, const char *value)
01913 {
01914         int err;
01915         snd_config_t *tmp;
01916         
01917         err = snd_config_make(&tmp, id, SND_CONFIG_TYPE_STRING);
01918         if (err < 0)
01919                 return err;
01920         if (value) {
01921                 tmp->u.string = strdup(value);
01922                 if (!tmp->u.string) {
01923                         snd_config_delete(tmp);
01924                         return -ENOMEM;
01925                 }
01926         } else {
01927                 tmp->u.string = NULL;
01928         }
01929         *config = tmp;
01930         return 0;
01931 }
01932 
01941 int snd_config_imake_pointer(snd_config_t **config, const char *id, const void *value)
01942 {
01943         int err;
01944         
01945         err = snd_config_make(config, id, SND_CONFIG_TYPE_POINTER);
01946         if (err < 0)
01947                 return err;
01948         (*config)->u.ptr = value;
01949         return 0;
01950 }
01951 
01958 int snd_config_set_integer(snd_config_t *config, long value)
01959 {
01960         assert(config);
01961         if (config->type != SND_CONFIG_TYPE_INTEGER)
01962                 return -EINVAL;
01963         config->u.integer = value;
01964         return 0;
01965 }
01966 
01973 int snd_config_set_integer64(snd_config_t *config, long long value)
01974 {
01975         assert(config);
01976         if (config->type != SND_CONFIG_TYPE_INTEGER64)
01977                 return -EINVAL;
01978         config->u.integer64 = value;
01979         return 0;
01980 }
01981 
01988 int snd_config_set_real(snd_config_t *config, double value)
01989 {
01990         assert(config);
01991         if (config->type != SND_CONFIG_TYPE_REAL)
01992                 return -EINVAL;
01993         config->u.real = value;
01994         return 0;
01995 }
01996 
02006 int snd_config_set_string(snd_config_t *config, const char *value)
02007 {
02008         char *new_string;
02009         assert(config);
02010         if (config->type != SND_CONFIG_TYPE_STRING)
02011                 return -EINVAL;
02012         if (value) {
02013                 new_string = strdup(value);
02014                 if (!new_string)
02015                         return -ENOMEM;
02016         } else {
02017                 new_string = NULL;
02018         }
02019         free(config->u.string);
02020         config->u.string = new_string;
02021         return 0;
02022 }
02023 
02032 int snd_config_set_pointer(snd_config_t *config, const void *value)
02033 {
02034         assert(config);
02035         if (config->type != SND_CONFIG_TYPE_POINTER)
02036                 return -EINVAL;
02037         config->u.ptr = value;
02038         return 0;
02039 }
02040 
02050 int snd_config_set_ascii(snd_config_t *config, const char *ascii)
02051 {
02052         assert(config && ascii);
02053         switch (config->type) {
02054         case SND_CONFIG_TYPE_INTEGER:
02055                 {
02056                         long i;
02057                         int err = safe_strtol(ascii, &i);
02058                         if (err < 0)
02059                                 return err;
02060                         config->u.integer = i;
02061                 }
02062                 break;
02063         case SND_CONFIG_TYPE_INTEGER64:
02064                 {
02065                         long long i;
02066                         int err = safe_strtoll(ascii, &i);
02067                         if (err < 0)
02068                                 return err;
02069                         config->u.integer64 = i;
02070                 }
02071                 break;
02072         case SND_CONFIG_TYPE_REAL:
02073                 {
02074                         double d;
02075                         int err = safe_strtod(ascii, &d);
02076                         if (err < 0)
02077                                 return err;
02078                         config->u.real = d;
02079                         break;
02080                 }
02081         case SND_CONFIG_TYPE_STRING:
02082                 {
02083                         char *ptr = strdup(ascii);
02084                         if (ptr == NULL)
02085                                 return -ENOMEM;
02086                         free(config->u.string);
02087                         config->u.string = ptr;
02088                 }
02089                 break;
02090         default:
02091                 return -EINVAL;
02092         }
02093         return 0;
02094 }
02095 
02103 int snd_config_get_integer(const snd_config_t *config, long *ptr)
02104 {
02105         assert(config && ptr);
02106         if (config->type != SND_CONFIG_TYPE_INTEGER)
02107                 return -EINVAL;
02108         *ptr = config->u.integer;
02109         return 0;
02110 }
02111 
02119 int snd_config_get_integer64(const snd_config_t *config, long long *ptr)
02120 {
02121         assert(config && ptr);
02122         if (config->type != SND_CONFIG_TYPE_INTEGER64)
02123                 return -EINVAL;
02124         *ptr = config->u.integer64;
02125         return 0;
02126 }
02127 
02135 int snd_config_get_real(const snd_config_t *config, double *ptr)
02136 {
02137         assert(config && ptr);
02138         if (config->type != SND_CONFIG_TYPE_REAL)
02139                 return -EINVAL;
02140         *ptr = config->u.real;
02141         return 0;
02142 }
02143 
02154 int snd_config_get_ireal(const snd_config_t *config, double *ptr)
02155 {
02156         assert(config && ptr);
02157         if (config->type == SND_CONFIG_TYPE_REAL)
02158                 *ptr = config->u.real;
02159         else if (config->type == SND_CONFIG_TYPE_INTEGER)
02160                 *ptr = config->u.integer;
02161         else if (config->type == SND_CONFIG_TYPE_INTEGER64)
02162                 *ptr = config->u.integer64;
02163         else
02164                 return -EINVAL;
02165         return 0;
02166 }
02167 
02178 int snd_config_get_string(const snd_config_t *config, const char **ptr)
02179 {
02180         assert(config && ptr);
02181         if (config->type != SND_CONFIG_TYPE_STRING)
02182                 return -EINVAL;
02183         *ptr = config->u.string;
02184         return 0;
02185 }
02186 
02194 int snd_config_get_pointer(const snd_config_t *config, const void **ptr)
02195 {
02196         assert(config && ptr);
02197         if (config->type != SND_CONFIG_TYPE_POINTER)
02198                 return -EINVAL;
02199         *ptr = config->u.ptr;
02200         return 0;
02201 }
02202 
02213 int snd_config_get_ascii(const snd_config_t *config, char **ascii)
02214 {
02215         assert(config && ascii);
02216         switch (config->type) {
02217         case SND_CONFIG_TYPE_INTEGER:
02218                 {
02219                         char res[12];
02220                         int err;
02221                         err = snprintf(res, sizeof(res), "%li", config->u.integer);
02222                         if (err < 0 || err == sizeof(res)) {
02223                                 assert(0);
02224                                 return -ENOMEM;
02225                         }
02226                         *ascii = strdup(res);
02227                 }
02228                 break;
02229         case SND_CONFIG_TYPE_INTEGER64:
02230                 {
02231                         char res[32];
02232                         int err;
02233                         err = snprintf(res, sizeof(res), "%Li", config->u.integer64);
02234                         if (err < 0 || err == sizeof(res)) {
02235                                 assert(0);
02236                                 return -ENOMEM;
02237                         }
02238                         *ascii = strdup(res);
02239                 }
02240                 break;
02241         case SND_CONFIG_TYPE_REAL:
02242                 {
02243                         char res[32];
02244                         int err;
02245                         err = snprintf(res, sizeof(res), "%-16g", config->u.real);
02246                         if (err < 0 || err == sizeof(res)) {
02247                                 assert(0);
02248                                 return -ENOMEM;
02249                         }
02250                         if (res[0]) {           /* trim the string */
02251                                 char *ptr;
02252                                 ptr = res + strlen(res) - 1;
02253                                 while (ptr != res && *ptr == ' ')
02254                                         ptr--;
02255                                 if (*ptr != ' ')
02256                                         ptr++;
02257                                 *ptr = '\0';
02258                         }
02259                         *ascii = strdup(res);
02260                 }
02261                 break;
02262         case SND_CONFIG_TYPE_STRING:
02263                 if (config->u.string)
02264                         *ascii = strdup(config->u.string);
02265                 else {
02266                         *ascii = NULL;
02267                         return 0;
02268                 }
02269                 break;
02270         default:
02271                 return -EINVAL;
02272         }
02273         if (*ascii == NULL)
02274                 return -ENOMEM;
02275         return 0;
02276 }
02277 
02284 int snd_config_test_id(const snd_config_t *config, const char *id)
02285 {
02286         assert(config && id);
02287         return strcmp(config->id, id);
02288 }
02289 
02296 int snd_config_save(snd_config_t *config, snd_output_t *out)
02297 {
02298         assert(config && out);
02299         if (config->type == SND_CONFIG_TYPE_COMPOUND)
02300                 return _snd_config_save_leaves(config, out, 0, 0);
02301         else
02302                 return _snd_config_save_leaf(config, out, 0);
02303 }
02304 
02305 /*
02306  *  *** search macros ***
02307  */
02308 
02309 #ifndef DOC_HIDDEN
02310 
02311 #define SND_CONFIG_SEARCH(config, key, result, extra_code) \
02312 { \
02313         snd_config_t *n; \
02314         int err; \
02315         const char *p; \
02316         assert(config && key); \
02317         while (1) { \
02318                 if (config->type != SND_CONFIG_TYPE_COMPOUND) \
02319                         return -ENOENT; \
02320                 { extra_code ; } \
02321                 p = strchr(key, '.'); \
02322                 if (p) { \
02323                         err = _snd_config_search(config, key, p - key, &n); \
02324                         if (err < 0) \
02325                                 return err; \
02326                         config = n; \
02327                         key = p + 1; \
02328                 } else \
02329                         return _snd_config_search(config, key, -1, result); \
02330         } \
02331 }
02332 
02333 #define SND_CONFIG_SEARCHA(root, config, key, result, fcn, extra_code) \
02334 { \
02335         snd_config_t *n; \
02336         int err; \
02337         const char *p; \
02338         assert(config && key); \
02339         while (1) { \
02340                 if (config->type != SND_CONFIG_TYPE_COMPOUND) { \
02341                         if (snd_config_get_string(config, &p) < 0) \
02342                                 return -ENOENT; \
02343                         err = fcn(root, root, p, &config); \
02344                         if (err < 0) \
02345                                 return err; \
02346                 } \
02347                 { extra_code ; } \
02348                 p = strchr(key, '.'); \
02349                 if (p) { \
02350                         err = _snd_config_search(config, key, p - key, &n); \
02351                         if (err < 0) \
02352                                 return err; \
02353                         config = n; \
02354                         key = p + 1; \
02355                 } else \
02356                         return _snd_config_search(config, key, -1, result); \
02357         } \
02358 }
02359 
02360 #define SND_CONFIG_SEARCHV(config, result, fcn) \
02361 { \
02362         snd_config_t *n; \
02363         va_list arg; \
02364         assert(config); \
02365         va_start(arg, result); \
02366         while (1) { \
02367                 const char *k = va_arg(arg, const char *); \
02368                 int err; \
02369                 if (!k) \
02370                         break; \
02371                 err = fcn(config, k, &n); \
02372                 if (err < 0) \
02373                         return err; \
02374                 config = n; \
02375         } \
02376         va_end(arg); \
02377         if (result) \
02378                 *result = n; \
02379         return 0; \
02380 }
02381 
02382 #define SND_CONFIG_SEARCHVA(root, config, result, fcn) \
02383 { \
02384         snd_config_t *n; \
02385         va_list arg; \
02386         assert(config); \
02387         va_start(arg, result); \
02388         while (1) { \
02389                 const char *k = va_arg(arg, const char *); \
02390                 int err; \
02391                 if (!k) \
02392                         break; \
02393                 err = fcn(root, config, k, &n); \
02394                 if (err < 0) \
02395                         return err; \
02396                 config = n; \
02397         } \
02398         va_end(arg); \
02399         if (result) \
02400                 *result = n; \
02401         return 0; \
02402 }
02403 
02404 #define SND_CONFIG_SEARCH_ALIAS(config, base, key, result, fcn1, fcn2) \
02405 { \
02406         snd_config_t *res = NULL; \
02407         char *old_key; \
02408         int err, first = 1, maxloop = 1000; \
02409         assert(config && key); \
02410         while (1) { \
02411                 old_key = strdup(key); \
02412                 if (old_key == NULL) { \
02413                         err = -ENOMEM; \
02414                         res = NULL; \
02415                         break; \
02416                 } \
02417                 err = first && base ? -EIO : fcn1(config, config, key, &res); \
02418                 if (err < 0) { \
02419                         if (!base) \
02420                                 break; \
02421                         err = fcn2(config, config, &res, base, key, NULL); \
02422                         if (err < 0) \
02423                                 break; \
02424                 } \
02425                 if (snd_config_get_string(res, &key) < 0) \
02426                         break; \
02427                 if (!first && (strcmp(key, old_key) == 0 || maxloop <= 0)) { \
02428                         if (maxloop == 0) \
02429                                 SNDERR("maximum loop count reached (circular configuration?)"); \
02430                         else \
02431                                 SNDERR("key %s refers to itself", key); \
02432                         err = -EINVAL; \
02433                         res = NULL; \
02434                         break; \
02435                 } \
02436                 free(old_key); \
02437                 first = 0; \
02438                 maxloop--; \
02439         } \
02440         free(old_key); \
02441         if (!res) \
02442                 return err; \
02443         if (result) \
02444                 *result = res; \
02445         return 0; \
02446 }
02447 
02448 #endif /* DOC_HIDDEN */
02449 
02458 int snd_config_search(snd_config_t *config, const char *key, snd_config_t **result)
02459 {
02460         SND_CONFIG_SEARCH(config, key, result, );
02461 }
02462 
02473 int snd_config_searcha(snd_config_t *root, snd_config_t *config, const char *key, snd_config_t **result)
02474 {
02475         SND_CONFIG_SEARCHA(root, config, key, result, snd_config_searcha, );
02476 }
02477 
02486 int snd_config_searchv(snd_config_t *config, snd_config_t **result, ...)
02487 {
02488         SND_CONFIG_SEARCHV(config, result, snd_config_search);
02489 }
02490 
02501 int snd_config_searchva(snd_config_t *root, snd_config_t *config, snd_config_t **result, ...)
02502 {
02503         SND_CONFIG_SEARCHVA(root, config, result, snd_config_searcha);
02504 }
02505 
02519 int snd_config_search_alias(snd_config_t *config,
02520                             const char *base, const char *key,
02521                             snd_config_t **result)
02522 {
02523         SND_CONFIG_SEARCH_ALIAS(config, base, key, result,
02524                                 snd_config_searcha, snd_config_searchva);
02525 }
02526 
02527 static int snd_config_hooks(snd_config_t *config, snd_config_t *private_data);
02528 
02537 int snd_config_search_hooks(snd_config_t *config, const char *key, snd_config_t **result)
02538 {
02539         SND_CONFIG_SEARCH(config, key, result, \
02540                                         err = snd_config_hooks(config, NULL); \
02541                                         if (err < 0) \
02542                                                 return err; \
02543                          );
02544 }
02545 
02556 int snd_config_searcha_hooks(snd_config_t *root, snd_config_t *config, const char *key, snd_config_t **result)
02557 {
02558         SND_CONFIG_SEARCHA(root, config, key, result,
02559                                         snd_config_searcha_hooks,
02560                                         err = snd_config_hooks(config, NULL); \
02561                                         if (err < 0) \
02562                                                 return err; \
02563                          );
02564 }
02565 
02576 int snd_config_searchva_hooks(snd_config_t *root, snd_config_t *config,
02577                               snd_config_t **result, ...)
02578 {
02579         SND_CONFIG_SEARCHVA(root, config, result, snd_config_searcha_hooks);
02580 }
02581 
02595 int snd_config_search_alias_hooks(snd_config_t *config,
02596                                   const char *base, const char *key,
02597                                   snd_config_t **result)
02598 {
02599         SND_CONFIG_SEARCH_ALIAS(config, base, key, result,
02600                                 snd_config_searcha_hooks,
02601                                 snd_config_searchva_hooks);
02602 }
02603 
02605 #define ALSA_CONFIG_PATH_VAR "ALSA_CONFIG_PATH"
02606 
02608 #define ALSA_CONFIG_PATH_DEFAULT "/share/alsa/alsa.conf"
02609 
02614 snd_config_t *snd_config = NULL;
02615 
02616 #ifndef DOC_HIDDEN
02617 struct finfo {
02618         char *name;
02619         dev_t dev;
02620         ino_t ino;
02621         time_t mtime;
02622 };
02623 
02624 struct _snd_config_update {
02625         unsigned int count;
02626         struct finfo *finfo;
02627 };
02628 #endif /* DOC_HIDDEN */
02629 
02630 static snd_config_update_t *snd_config_global_update = NULL;
02631 
02632 static int snd_config_hooks_call(snd_config_t *root, snd_config_t *config, snd_config_t *private_data)
02633 {
02634         void *h = NULL;
02635         snd_config_t *c, *func_conf = NULL;
02636         char *buf = NULL;
02637         const char *lib = NULL, *func_name = NULL;
02638         const char *str;
02639         int (*func)(snd_config_t *root, snd_config_t *config, snd_config_t **dst, snd_config_t *private_data) = NULL;
02640         int err;
02641 
02642         err = snd_config_search(config, "func", &c);
02643         if (err < 0) {
02644                 SNDERR("Field func is missing");
02645                 return err;
02646         }
02647         err = snd_config_get_string(c, &str);
02648         if (err < 0) {
02649                 SNDERR("Invalid type for field func");
02650                 return err;
02651         }
02652         err = snd_config_search_definition(root, "hook_func", str, &func_conf);
02653         if (err >= 0) {
02654                 snd_config_iterator_t i, next;
02655                 if (snd_config_get_type(func_conf) != SND_CONFIG_TYPE_COMPOUND) {
02656                         SNDERR("Invalid type for func %s definition", str);
02657                         goto _err;
02658                 }
02659                 snd_config_for_each(i, next, func_conf) {
02660                         snd_config_t *n = snd_config_iterator_entry(i);
02661                         const char *id = n->id;
02662                         if (strcmp(id, "comment") == 0)
02663                                 continue;
02664                         if (strcmp(id, "lib") == 0) {
02665                                 err = snd_config_get_string(n, &lib);
02666                                 if (err < 0) {
02667                                         SNDERR("Invalid type for %s", id);
02668                                         goto _err;
02669                                 }
02670                                 continue;
02671                         }
02672                         if (strcmp(id, "func") == 0) {
02673                                 err = snd_config_get_string(n, &func_name);
02674                                 if (err < 0) {
02675                                         SNDERR("Invalid type for %s", id);
02676                                         goto _err;
02677                                 }
02678                                 continue;
02679                         }
02680                         SNDERR("Unknown field %s", id);
02681                 }
02682         }
02683         if (!func_name) {
02684                 int len = 16 + strlen(str) + 1;
02685                 buf = malloc(len);
02686                 if (! buf) {
02687                         err = -ENOMEM;
02688                         goto _err;
02689                 }
02690                 snprintf(buf, len, "snd_config_hook_%s", str);
02691                 buf[len-1] = '\0';
02692                 func_name = buf;
02693         }
02694         h = snd_dlopen(lib, RTLD_NOW);
02695         func = h ? snd_dlsym(h, func_name, SND_DLSYM_VERSION(SND_CONFIG_DLSYM_VERSION_HOOK)) : NULL;
02696         err = 0;
02697         if (!h) {
02698                 SNDERR("Cannot open shared library %s", lib);
02699                 err = -ENOENT;
02700         } else if (!func) {
02701                 SNDERR("symbol %s is not defined inside %s", func_name, lib);
02702                 snd_dlclose(h);
02703                 err = -ENXIO;
02704         }
02705         _err:
02706         if (func_conf)
02707                 snd_config_delete(func_conf);
02708         if (err >= 0) {
02709                 snd_config_t *nroot;
02710                 err = func(root, config, &nroot, private_data);
02711                 if (err < 0)
02712                         SNDERR("function %s returned error: %s", func_name, snd_strerror(err));
02713                 snd_dlclose(h);
02714                 if (err >= 0 && nroot)
02715                         err = snd_config_substitute(root, nroot);
02716         }
02717         free(buf);
02718         if (err < 0)
02719                 return err;
02720         return 0;
02721 }
02722 
02723 static int snd_config_hooks(snd_config_t *config, snd_config_t *private_data)
02724 {
02725         snd_config_t *n;
02726         snd_config_iterator_t i, next;
02727         int err, hit, idx = 0;
02728 
02729         if ((err = snd_config_search(config, "@hooks", &n)) < 0)
02730                 return 0;
02731         snd_config_remove(n);
02732         do {
02733                 hit = 0;
02734                 snd_config_for_each(i, next, n) {
02735                         snd_config_t *n = snd_config_iterator_entry(i);
02736                         const char *id = n->id;
02737                         long i;
02738                         err = safe_strtol(id, &i);
02739                         if (err < 0) {
02740                                 SNDERR("id of field %s is not and integer", id);
02741                                 err = -EINVAL;
02742                                 goto _err;
02743                         }
02744                         if (i == idx) {
02745                                 err = snd_config_hooks_call(config, n, private_data);
02746                                 if (err < 0)
02747                                         return err;
02748                                 idx++;
02749                                 hit = 1;
02750                         }
02751                 }
02752         } while (hit);
02753         err = 0;
02754        _err:
02755         snd_config_delete(n);
02756         return err;
02757 }
02758 
02768 int snd_config_hook_load(snd_config_t *root, snd_config_t *config, snd_config_t **dst, snd_config_t *private_data)
02769 {
02770         snd_config_t *n;
02771         snd_config_iterator_t i, next;
02772         struct finfo *fi = NULL;
02773         int err, idx = 0, fi_count = 0, errors = 1, hit;
02774 
02775         assert(root && dst);
02776         if ((err = snd_config_search(config, "errors", &n)) >= 0) {
02777                 char *tmp;
02778                 err = snd_config_get_ascii(n, &tmp);
02779                 if (err < 0)
02780                         return err;
02781                 errors = snd_config_get_bool_ascii(tmp);
02782                 free(tmp);
02783                 if (errors < 0) {
02784                         SNDERR("Invalid bool value in field errors");
02785                         return errors;
02786                 }
02787         }
02788         if ((err = snd_config_search(config, "files", &n)) < 0) {
02789                 SNDERR("Unable to find field files in the pre-load section");
02790                 return -EINVAL;
02791         }
02792         if ((err = snd_config_expand(n, root, NULL, private_data, &n)) < 0) {
02793                 SNDERR("Unable to expand filenames in the pre-load section");
02794                 return err;
02795         }
02796         if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
02797                 SNDERR("Invalid type for field filenames");
02798                 goto _err;
02799         }
02800         snd_config_for_each(i, next, n) {
02801                 snd_config_t *c = snd_config_iterator_entry(i);
02802                 const char *str;
02803                 if ((err = snd_config_get_string(c, &str)) < 0) {
02804                         SNDERR("Field %s is not a string", c->id);
02805                         goto _err;
02806                 }
02807                 fi_count++;
02808         }
02809         fi = calloc(fi_count, sizeof(*fi));
02810         if (fi == NULL) {
02811                 err = -ENOMEM;
02812                 goto _err;
02813         }
02814         do {
02815                 hit = 0;
02816                 snd_config_for_each(i, next, n) {
02817                         snd_config_t *n = snd_config_iterator_entry(i);
02818                         const char *id = n->id;
02819                         long i;
02820                         err = safe_strtol(id, &i);
02821                         if (err < 0) {
02822                                 SNDERR("id of field %s is not and integer", id);
02823                                 err = -EINVAL;
02824                                 goto _err;
02825                         }
02826                         if (i == idx) {
02827                                 char *name;
02828                                 if ((err = snd_config_get_ascii(n, &name)) < 0)
02829                                         goto _err;
02830                                 if ((err = snd_user_file(name, &fi[idx].name)) < 0)
02831                                         fi[idx].name = name;
02832                                 else
02833                                         free(name);
02834                                 idx++;
02835                                 hit = 1;
02836                         }
02837                 }
02838         } while (hit);
02839         for (idx = 0; idx < fi_count; idx++) {
02840                 snd_input_t *in;
02841                 if (!errors && access(fi[idx].name, R_OK) < 0)
02842                         continue;
02843                 err = snd_input_stdio_open(&in, fi[idx].name, "r");
02844                 if (err >= 0) {
02845                         err = snd_config_load(root, in);
02846                         snd_input_close(in);
02847                         if (err < 0) {
02848                                 SNDERR("%s may be old or corrupted: consider to remove or fix it", fi[idx].name);
02849                                 goto _err;
02850                         }
02851                 } else {
02852                         SNDERR("cannot access file %s", fi[idx].name);
02853                 }
02854         }
02855         *dst = NULL;
02856         err = 0;
02857        _err:
02858         if (fi)
02859                 for (idx = 0; idx < fi_count; idx++)
02860                         free(fi[idx].name);
02861         free(fi);
02862         snd_config_delete(n);
02863         return err;
02864 }
02865 #ifndef DOC_HIDDEN
02866 SND_DLSYM_BUILD_VERSION(snd_config_hook_load, SND_CONFIG_DLSYM_VERSION_HOOK);
02867 #endif
02868 
02869 #ifndef DOC_HIDDEN
02870 int snd_determine_driver(int card, char **driver);
02871 #endif
02872 
02882 int snd_config_hook_load_for_all_cards(snd_config_t *root, snd_config_t *config, snd_config_t **dst, snd_config_t *private_data ATTRIBUTE_UNUSED)
02883 {
02884         int card = -1, err;
02885         
02886         do {
02887                 err = snd_card_next(&card);
02888                 if (err < 0)
02889                         return err;
02890                 if (card >= 0) {
02891                         snd_config_t *n, *private_data = NULL;
02892                         const char *driver;
02893                         char *fdriver = NULL;
02894                         err = snd_determine_driver(card, &fdriver);
02895                         if (err < 0)
02896                                 return err;
02897                         if (snd_config_search(root, fdriver, &n) >= 0) {
02898                                 if (snd_config_get_string(n, &driver) < 0)
02899                                         continue;
02900                                 while (1) {
02901                                         char *s = strchr(driver, '.');
02902                                         if (s == NULL)
02903                                                 break;
02904                                         driver = s + 1;
02905                                 }
02906                                 if (snd_config_search(root, driver, &n) >= 0)
02907                                         continue;
02908                         } else {
02909                                 driver = fdriver;
02910                         }
02911                         err = snd_config_imake_string(&private_data, "string", driver);
02912                         if (err < 0)
02913                                 goto __err;
02914                         err = snd_config_hook_load(root, config, &n, private_data);
02915                       __err:
02916                         if (private_data)
02917                                 snd_config_delete(private_data);
02918                         free(fdriver);
02919                         if (err < 0)
02920                                 return err;
02921                 }
02922         } while (card >= 0);
02923         *dst = NULL;
02924         return 0;
02925 }
02926 #ifndef DOC_HIDDEN
02927 SND_DLSYM_BUILD_VERSION(snd_config_hook_load_for_all_cards, SND_CONFIG_DLSYM_VERSION_HOOK);
02928 #endif
02929 
02947 int snd_config_update_r(snd_config_t **_top, snd_config_update_t **_update, const char *cfgs)
02948 {
02949         int err;
02950         const char *configs, *c;
02951         unsigned int k;
02952         size_t l;
02953         snd_config_update_t *local;
02954         snd_config_update_t *update;
02955         snd_config_t *top;
02956         
02957         assert(_top && _update);
02958         top = *_top;
02959         update = *_update;
02960         configs = cfgs;
02961         if (!configs) {
02962                 configs = getenv(ALSA_CONFIG_PATH_VAR);
02963                 if (!configs)
02964                         configs = ALSA_CONFIG_PATH_DEFAULT;
02965         }
02966         for (k = 0, c = configs; (l = strcspn(c, ": ")) > 0; ) {
02967                 c += l;
02968                 k++;
02969                 if (!*c)
02970                         break;
02971                 c++;
02972         }
02973         if (k == 0) {
02974                 local = NULL;
02975                 goto _reread;
02976         }
02977         local = (snd_config_update_t *)calloc(1, sizeof(snd_config_update_t));
02978         if (!local)
02979                 return -ENOMEM;
02980         local->count = k;
02981         local->finfo = calloc(local->count, sizeof(struct finfo));
02982         if (!local->finfo) {
02983                 free(local);
02984                 return -ENOMEM;
02985         }
02986         for (k = 0, c = configs; (l = strcspn(c, ": ")) > 0; ) {
02987                 char name[l + 1];
02988                 memcpy(name, c, l);
02989                 name[l] = 0;
02990                 err = snd_user_file(name, &local->finfo[k].name);
02991                 if (err < 0)
02992                         goto _end;
02993                 c += l;
02994                 k++;
02995                 if (!*c)
02996                         break;
02997                 c++;
02998         }
02999         for (k = 0; k < local->count; ++k) {
03000                 struct stat st;
03001                 struct finfo *lf = &local->finfo[k];
03002                 if (stat(lf->name, &st) >= 0) {
03003                         lf->dev = st.st_dev;
03004                         lf->ino = st.st_ino;
03005                         lf->mtime = st.st_mtime;
03006                 } else {
03007                         memmove(&local->finfo[k], &local->finfo[k+1], sizeof(struct finfo) * (local->count - k - 1));
03008                         k--;
03009                         local->count--;
03010                 }
03011         }
03012         if (!update)
03013                 goto _reread;
03014         if (local->count != update->count)
03015                 goto _reread;
03016         for (k = 0; k < local->count; ++k) {
03017                 struct finfo *lf = &local->finfo[k];
03018                 struct finfo *uf = &update->finfo[k];
03019                 if (strcmp(lf->name, uf->name) != 0 ||
03020                     lf->dev != uf->dev ||
03021                     lf->ino != uf->ino ||
03022                     lf->mtime != uf->mtime)
03023                         goto _reread;
03024         }
03025         err = 0;
03026 
03027  _end:
03028         if (err < 0) {
03029                 if (top) {
03030                         snd_config_delete(top);
03031                         *_top = NULL;
03032                 }
03033                 if (update) {
03034                         snd_config_update_free(update);
03035                         *_update = NULL;
03036                 }
03037         }
03038         if (local)
03039                 snd_config_update_free(local);
03040         return err;
03041 
03042  _reread:
03043         *_top = NULL;
03044         *_update = NULL;
03045         if (update) {
03046                 snd_config_update_free(update);
03047                 update = NULL;
03048         }
03049         if (top) {
03050                 snd_config_delete(top);
03051                 top = NULL;
03052         }
03053         err = snd_config_top(&top);
03054         if (err < 0)
03055                 goto _end;
03056         if (!local)
03057                 goto _skip;
03058         for (k = 0; k < local->count; ++k) {
03059                 snd_input_t *in;
03060                 err = snd_input_stdio_open(&in, local->finfo[k].name, "r");
03061                 if (err >= 0) {
03062                         err = snd_config_load(top, in);
03063                         snd_input_close(in);
03064                         if (err < 0) {
03065                                 SNDERR("%s may be old or corrupted: consider to remove or fix it", local->finfo[k].name);
03066                                 goto _end;
03067                         }
03068                 } else {
03069                         SNDERR("cannot access file %s", local->finfo[k].name);
03070                 }
03071         }
03072  _skip:
03073         err = snd_config_hooks(top, NULL);
03074         if (err < 0) {
03075                 SNDERR("hooks failed, removing configuration");
03076                 goto _end;
03077         }
03078         *_top = top;
03079         *_update = local;
03080         return 1;
03081 }
03082 
03083 static pthread_mutex_t snd_config_update_mutex = PTHREAD_MUTEX_INITIALIZER;
03084 
03098 int snd_config_update(void)
03099 {
03100         int err;
03101 
03102         pthread_mutex_lock(&snd_config_update_mutex);
03103         err = snd_config_update_r(&snd_config, &snd_config_global_update, NULL);
03104         pthread_mutex_unlock(&snd_config_update_mutex);
03105         return err;
03106 }
03107 
03113 int snd_config_update_free(snd_config_update_t *update)
03114 {
03115         unsigned int k;
03116 
03117         assert(update);
03118         for (k = 0; k < update->count; k++)
03119                 free(update->finfo[k].name);
03120         free(update->finfo);
03121         free(update);
03122         return 0;
03123 }
03124 
03129 int snd_config_update_free_global(void)
03130 {
03131         pthread_mutex_lock(&snd_config_update_mutex);
03132         if (snd_config)
03133                 snd_config_delete(snd_config);
03134         snd_config = NULL;
03135         if (snd_config_global_update)
03136                 snd_config_update_free(snd_config_global_update);
03137         snd_config_global_update = NULL;
03138         pthread_mutex_unlock(&snd_config_update_mutex);
03139 
03140         /* FIXME: better to place this in another place... */
03141         snd_dlobj_cache_cleanup();
03142 
03143         return 0;
03144 }
03145 
03151 snd_config_iterator_t snd_config_iterator_first(const snd_config_t *node)
03152 {
03153         assert(node->type == SND_CONFIG_TYPE_COMPOUND);
03154         return node->u.compound.fields.next;
03155 }
03156 
03165 snd_config_iterator_t snd_config_iterator_next(const snd_config_iterator_t iterator)
03166 {
03167         return iterator->next;
03168 }
03169 
03175 snd_config_iterator_t snd_config_iterator_end(const snd_config_t *node)
03176 {
03177         assert(node->type == SND_CONFIG_TYPE_COMPOUND);
03178         return (const snd_config_iterator_t)&node->u.compound.fields;
03179 }
03180 
03186 snd_config_t *snd_config_iterator_entry(const snd_config_iterator_t iterator)
03187 {
03188         return list_entry(iterator, snd_config_t, list);
03189 }
03190 
03191 #ifndef DOC_HIDDEN
03192 typedef enum _snd_config_walk_pass {
03193         SND_CONFIG_WALK_PASS_PRE,
03194         SND_CONFIG_WALK_PASS_POST,
03195         SND_CONFIG_WALK_PASS_LEAF,
03196 } snd_config_walk_pass_t;
03197 #endif
03198 
03199 /* Return 1 if node needs to be attached to father */
03200 /* Return 2 if compound is replaced with standard node */
03201 #ifndef DOC_HIDDEN
03202 typedef int (*snd_config_walk_callback_t)(snd_config_t *src,
03203                                           snd_config_t *root,
03204                                           snd_config_t **dst,
03205                                           snd_config_walk_pass_t pass,
03206                                           snd_config_t *private_data);
03207 #endif
03208 
03209 static int snd_config_walk(snd_config_t *src,
03210                            snd_config_t *root,
03211                            snd_config_t **dst, 
03212                            snd_config_walk_callback_t callback,
03213                            snd_config_t *private_data)
03214 {
03215         int err;
03216         snd_config_iterator_t i, next;
03217 
03218         switch (snd_config_get_type(src)) {
03219         case SND_CONFIG_TYPE_COMPOUND:
03220                 err = callback(src, root, dst, SND_CONFIG_WALK_PASS_PRE, private_data);
03221                 if (err <= 0)
03222                         return err;
03223                 snd_config_for_each(i, next, src) {
03224                         snd_config_t *s = snd_config_iterator_entry(i);
03225                         snd_config_t *d = NULL;
03226 
03227                         err = snd_config_walk(s, root, (dst && *dst) ? &d : NULL,
03228                                               callback, private_data);
03229                         if (err < 0)
03230                                 goto _error;
03231                         if (err && d) {
03232                                 err = snd_config_add(*dst, d);
03233                                 if (err < 0)
03234                                         goto _error;
03235                         }
03236                 }
03237                 err = callback(src, root, dst, SND_CONFIG_WALK_PASS_POST, private_data);
03238                 if (err <= 0) {
03239                 _error:
03240                         if (dst && *dst)
03241                                 snd_config_delete(*dst);
03242                 }
03243                 break;
03244         default:
03245                 err = callback(src, root, dst, SND_CONFIG_WALK_PASS_LEAF, private_data);
03246                 break;
03247         }
03248         return err;
03249 }
03250 
03251 static int _snd_config_copy(snd_config_t *src,
03252                             snd_config_t *root ATTRIBUTE_UNUSED,
03253                             snd_config_t **dst,
03254                             snd_config_walk_pass_t pass,
03255                             snd_config_t *private_data ATTRIBUTE_UNUSED)
03256 {
03257         int err;
03258         const char *id = src->id;
03259         snd_config_type_t type = snd_config_get_type(src);
03260         switch (pass) {
03261         case SND_CONFIG_WALK_PASS_PRE:
03262                 err = snd_config_make_compound(dst, id, src->u.compound.join);
03263                 if (err < 0)
03264                         return err;
03265                 break;
03266         case SND_CONFIG_WALK_PASS_LEAF:
03267                 err = snd_config_make(dst, id, type);
03268                 if (err < 0)
03269                         return err;
03270                 switch (type) {
03271                 case SND_CONFIG_TYPE_INTEGER:
03272                 {
03273                         long v;
03274                         err = snd_config_get_integer(src, &v);
03275                         assert(err >= 0);
03276                         snd_config_set_integer(*dst, v);
03277                         break;
03278                 }
03279                 case SND_CONFIG_TYPE_INTEGER64:
03280                 {
03281                         long long v;
03282                         err = snd_config_get_integer64(src, &v);
03283                         assert(err >= 0);
03284                         snd_config_set_integer64(*dst, v);
03285                         break;
03286                 }
03287                 case SND_CONFIG_TYPE_REAL:
03288                 {
03289                         double v;
03290                         err = snd_config_get_real(src, &v);
03291                         assert(err >= 0);
03292                         snd_config_set_real(*dst, v);
03293                         break;
03294                 }
03295                 case SND_CONFIG_TYPE_STRING:
03296                 {
03297                         const char *s;
03298                         err = snd_config_get_string(src, &s);
03299                         assert(err >= 0);
03300                         err = snd_config_set_string(*dst, s);
03301                         if (err < 0)
03302                                 return err;
03303                         break;
03304                 }
03305                 default:
03306                         assert(0);
03307                 }
03308                 break;
03309         default:
03310                 break;
03311         }
03312         return 1;
03313 }
03314 
03322 int snd_config_copy(snd_config_t **dst,
03323                     snd_config_t *src)
03324 {
03325         return snd_config_walk(src, NULL, dst, _snd_config_copy, NULL);
03326 }
03327 
03328 static int _snd_config_expand(snd_config_t *src,
03329                               snd_config_t *root ATTRIBUTE_UNUSED,
03330                               snd_config_t **dst,
03331                               snd_config_walk_pass_t pass,
03332                               snd_config_t *private_data)
03333 {
03334         int err;
03335         const char *id = src->id;
03336         snd_config_type_t type = snd_config_get_type(src);
03337         switch (pass) {
03338         case SND_CONFIG_WALK_PASS_PRE:
03339         {
03340                 if (strcmp(id, "@args") == 0)
03341                         return 0;
03342                 err = snd_config_make_compound(dst, id, src->u.compound.join);
03343                 if (err < 0)
03344                         return err;
03345                 break;
03346         }
03347         case SND_CONFIG_WALK_PASS_LEAF:
03348                 switch (type) {
03349                 case SND_CONFIG_TYPE_INTEGER:
03350                 {
03351                         long v;
03352                         err = snd_config_get_integer(src, &v);
03353                         assert(err >= 0);
03354                         err = snd_config_imake_integer(dst, id, v);
03355                         if (err < 0)
03356                                 return err;
03357                         break;
03358                 }
03359                 case SND_CONFIG_TYPE_INTEGER64:
03360                 {
03361                         long long v;
03362                         err = snd_config_get_integer64(src, &v);
03363                         assert(err >= 0);
03364                         err = snd_config_imake_integer64(dst, id, v);
03365                         if (err < 0)
03366                                 return err;
03367                         break;
03368                 }
03369                 case SND_CONFIG_TYPE_REAL:
03370                 {
03371                         double v;
03372                         err = snd_config_get_real(src, &v);
03373                         assert(err >= 0);
03374                         err = snd_config_imake_real(dst, id, v);
03375                         if (err < 0)
03376                                 return err;
03377                         break;
03378                 }
03379                 case SND_CONFIG_TYPE_STRING:
03380                 {
03381                         const char *s;
03382                         snd_config_t *val;
03383                         snd_config_t *vars = private_data;
03384                         snd_config_get_string(src, &s);
03385                         if (*s == '$') {
03386                                 s++;
03387                                 if (snd_config_search(vars, s, &val) < 0)
03388                                         return 0;
03389                                 err = snd_config_copy(dst, val);
03390                                 if (err < 0)
03391                                         return err;
03392                                 err = snd_config_set_id(*dst, id);
03393                                 if (err < 0) {
03394                                         snd_config_delete(*dst);
03395                                         return err;
03396                                 }
03397                         } else {
03398                                 err = snd_config_imake_string(dst, id, s);
03399                                 if (err < 0)
03400                                         return err;
03401                         }
03402                         break;
03403                 }
03404                 default:
03405                         assert(0);
03406                 }
03407                 break;
03408         default:
03409                 break;
03410         }
03411         return 1;
03412 }
03413 
03414 static int _snd_config_evaluate(snd_config_t *src,
03415                                 snd_config_t *root,
03416                                 snd_config_t **dst ATTRIBUTE_UNUSED,
03417                                 snd_config_walk_pass_t pass,
03418                                 snd_config_t *private_data)
03419 {
03420         int err;
03421         if (pass == SND_CONFIG_WALK_PASS_PRE) {
03422                 char *buf = NULL;
03423                 const char *lib = NULL, *func_name = NULL;
03424                 const char *str;
03425                 int (*func)(snd_config_t **dst, snd_config_t *root,
03426                             snd_config_t *src, snd_config_t *private_data) = NULL;
03427                 void *h = NULL;
03428                 snd_config_t *c, *func_conf = NULL;
03429                 err = snd_config_search(src, "@func", &c);
03430                 if (err < 0)
03431                         return 1;
03432                 err = snd_config_get_string(c, &str);
03433                 if (err < 0) {
03434                         SNDERR("Invalid type for @func");
03435                         return err;
03436                 }
03437                 err = snd_config_search_definition(root, "func", str, &func_conf);
03438                 if (err >= 0) {
03439                         snd_config_iterator_t i, next;
03440                         if (snd_config_get_type(func_conf) != SND_CONFIG_TYPE_COMPOUND) {
03441                                 SNDERR("Invalid type for func %s definition", str);
03442                                 goto _err;
03443                         }
03444                         snd_config_for_each(i, next, func_conf) {
03445                                 snd_config_t *n = snd_config_iterator_entry(i);
03446                                 const char *id = n->id;
03447                                 if (strcmp(id, "comment") == 0)
03448                                         continue;
03449                                 if (strcmp(id, "lib") == 0) {
03450                                         err = snd_config_get_string(n, &lib);
03451                                         if (err < 0) {
03452                                                 SNDERR("Invalid type for %s", id);
03453                                                 goto _err;
03454                                         }
03455                                         continue;
03456                                 }
03457                                 if (strcmp(id, "func") == 0) {
03458                                         err = snd_config_get_string(n, &func_name);
03459                                         if (err < 0) {
03460                                                 SNDERR("Invalid type for %s", id);
03461                                                 goto _err;
03462                                         }
03463                                         continue;
03464                                 }
03465                                 SNDERR("Unknown field %s", id);
03466                         }
03467                 }
03468                 if (!func_name) {
03469                         int len = 9 + strlen(str) + 1;
03470                         buf = malloc(len);
03471                         if (! buf) {
03472                                 err = -ENOMEM;
03473                                 goto _err;
03474                         }
03475                         snprintf(buf, len, "snd_func_%s", str);
03476                         buf[len-1] = '\0';
03477                         func_name = buf;
03478                 }
03479                 h = snd_dlopen(lib, RTLD_NOW);
03480                 if (h)
03481                         func = snd_dlsym(h, func_name, SND_DLSYM_VERSION(SND_CONFIG_DLSYM_VERSION_EVALUATE));
03482                 err = 0;
03483                 if (!h) {
03484                         SNDERR("Cannot open shared library %s", lib);
03485                         err = -ENOENT;
03486                         goto _errbuf;
03487                 } else if (!func) {
03488                         SNDERR("symbol %s is not defined inside %s", func_name, lib);
03489                         snd_dlclose(h);
03490                         err = -ENXIO;
03491                         goto _errbuf;
03492                 }
03493                _err:
03494                 if (func_conf)
03495                         snd_config_delete(func_conf);
03496                 if (err >= 0) {
03497                         snd_config_t *eval;
03498                         err = func(&eval, root, src, private_data);
03499                         if (err < 0)
03500                                 SNDERR("function %s returned error: %s", func_name, snd_strerror(err));
03501                         snd_dlclose(h);
03502                         if (err >= 0 && eval) {
03503                                 /* substitute merges compound members */
03504                                 /* we don't want merging at all */
03505                                 err = snd_config_delete_compound_members(src);
03506                                 if (err >= 0)
03507                                         err = snd_config_substitute(src, eval);
03508                         }
03509                 }
03510                _errbuf:
03511                 free(buf);
03512                 if (err < 0)
03513                         return err;
03514                 return 0;
03515         }
03516         return 1;
03517 }
03518 
03530 int snd_config_evaluate(snd_config_t *config, snd_config_t *root,
03531                         snd_config_t *private_data, snd_config_t **result)
03532 {
03533         /* FIXME: Only in place evaluation is currently implemented */
03534         assert(result == NULL);
03535         return snd_config_walk(config, root, result, _snd_config_evaluate, private_data);
03536 }
03537 
03538 static int load_defaults(snd_config_t *subs, snd_config_t *defs)
03539 {
03540         snd_config_iterator_t d, dnext;
03541         snd_config_for_each(d, dnext, defs) {
03542                 snd_config_t *def = snd_config_iterator_entry(d);
03543                 snd_config_iterator_t f, fnext;
03544                 if (snd_config_get_type(def) != SND_CONFIG_TYPE_COMPOUND)
03545                         continue;
03546                 snd_config_for_each(f, fnext, def) {
03547                         snd_config_t *fld = snd_config_iterator_entry(f);
03548                         const char *id = fld->id;
03549                         if (strcmp(id, "type") == 0)
03550                                 continue;
03551                         if (strcmp(id, "default") == 0) {
03552                                 snd_config_t *deflt;
03553                                 int err;
03554                                 err = snd_config_copy(&deflt, fld);
03555                                 if (err < 0)
03556                                         return err;
03557                                 err = snd_config_set_id(deflt, def->id);
03558                                 if (err < 0) {
03559                                         snd_config_delete(deflt);
03560                                         return err;
03561                                 }
03562                                 err = snd_config_add(subs, deflt);
03563                                 if (err < 0) {
03564                                         snd_config_delete(deflt);
03565                                         return err;
03566                                 }
03567                                 continue;
03568                         }
03569                         SNDERR("Unknown field %s", id);
03570                         return -EINVAL;
03571                 }
03572         }
03573         return 0;
03574 }
03575 
03576 static void skip_blank(const char **ptr)
03577 {
03578         while (1) {
03579                 switch (**ptr) {
03580                 case ' ':
03581                 case '\f':
03582                 case '\t':
03583                 case '\n':
03584                 case '\r':
03585                         break;
03586                 default:
03587                         return;
03588                 }
03589                 (*ptr)++;
03590         }
03591 }
03592 
03593 static int parse_char(const char **ptr)
03594 {
03595         int c;
03596         assert(**ptr == '\\');
03597         (*ptr)++;
03598         c = **ptr;
03599         switch (c) {
03600         case 'n':
03601                 c = '\n';
03602                 break;
03603         case 't':
03604                 c = '\t';
03605                 break;
03606         case 'v':
03607                 c = '\v';
03608                 break;
03609         case 'b':
03610                 c = '\b';
03611                 break;
03612         case 'r':
03613                 c = '\r';
03614                 break;
03615         case 'f':
03616                 c = '\f';
03617                 break;
03618         case '0' ... '7':
03619         {
03620                 int num = c - '0';
03621                 int i = 1;
03622                 (*ptr)++;
03623                 do {
03624                         c = **ptr;
03625                         if (c < '0' || c > '7')
03626                                 break;
03627                         num = num * 8 + c - '0';
03628                         i++;
03629                         (*ptr)++;
03630                 } while (i < 3);
03631                 return num;
03632         }
03633         default:
03634                 break;
03635         }
03636         (*ptr)++;
03637         return c;
03638 }
03639 
03640 static int parse_id(const char **ptr)
03641 {
03642         if (!**ptr)
03643                 return -EINVAL;
03644         while (1) {
03645                 switch (**ptr) {
03646                 case '\f':
03647                 case '\t':
03648                 case '\n':
03649                 case '\r':
03650                 case ',':
03651                 case '=':
03652                 case '\0':
03653                         return 0;
03654                 default:
03655                         break;
03656                 }
03657                 (*ptr)++;
03658         }
03659 }
03660 
03661 static int parse_string(const char **ptr, char **val)
03662 {
03663         const size_t bufsize = 256;
03664         char _buf[bufsize];
03665         char *buf = _buf;
03666         size_t alloc = bufsize;
03667         char delim = **ptr;
03668         size_t idx = 0;
03669         (*ptr)++;
03670         while (1) {
03671                 int c = **ptr;
03672                 switch (c) {
03673                 case '\0':
03674                         SNDERR("Unterminated string");
03675                         return -EINVAL;
03676                 case '\\':
03677                         c = parse_char(ptr);
03678                         if (c < 0)
03679                                 return c;
03680                         break;
03681                 default:
03682                         (*ptr)++;
03683                         if (c == delim) {
03684                                 *val = malloc(idx + 1);
03685                                 if (!*val)
03686                                         return -ENOMEM;
03687                                 memcpy(*val, buf, idx);
03688                                 (*val)[idx] = 0;
03689                                 if (alloc > bufsize)
03690                                         free(buf);
03691                                 return 0;
03692                         }
03693                 }
03694                 if (idx >= alloc) {
03695                         size_t old_alloc = alloc;
03696                         alloc *= 2;
03697                         if (old_alloc == bufsize) {
03698                                 buf = malloc(alloc);
03699                                 memcpy(buf, _buf, old_alloc);
03700                         } else {
03701                                 buf = realloc(buf, alloc);
03702                         }
03703                         if (!buf)
03704                                 return -ENOMEM;
03705                 }
03706                 buf[idx++] = c;
03707         }
03708 }
03709                                 
03710 
03711 /* Parse var=val or val */
03712 static int parse_arg(const char **ptr, unsigned int *varlen, char **val)
03713 {
03714         const char *str;
03715         int err, vallen;
03716         skip_blank(ptr);
03717         str = *ptr;
03718         if (*str == '"' || *str == '\'') {
03719                 err = parse_string(ptr, val);
03720                 if (err < 0)
03721                         return err;
03722                 *varlen = 0;
03723                 return 0;
03724         }
03725         err = parse_id(ptr);
03726         if (err < 0)
03727                 return err;
03728         vallen = *ptr - str;
03729         skip_blank(ptr);
03730         if (**ptr != '=') {
03731                 *varlen = 0;
03732                 goto _value;
03733         }
03734         *varlen = vallen;
03735         (*ptr)++;
03736         skip_blank(ptr);
03737         str = *ptr;
03738         if (*str == '"' || *str == '\'') {
03739                 err = parse_string(ptr, val);
03740                 if (err < 0)
03741                         return err;
03742                 return 0;
03743         }
03744         err = parse_id(ptr);
03745         if (err < 0)
03746                 return err;
03747         vallen = *ptr - str;
03748  _value:
03749         *val = malloc(vallen + 1);
03750         if (!*val)
03751                 return -ENOMEM;
03752         memcpy(*val, str, vallen);
03753         (*val)[vallen] = 0;
03754         return 0;
03755 }
03756 
03757 
03758 /* val1, val2, ...
03759  * var1=val1,var2=val2,...
03760  * { conf syntax }
03761  */
03762 static int parse_args(snd_config_t *subs, const char *str, snd_config_t *defs)
03763 {
03764         int err;
03765         int arg = 0;
03766         if (str == NULL)
03767                 return 0;
03768         skip_blank(&str);
03769         if (!*str)
03770                 return 0;
03771         if (*str == '{') {
03772                 int len = strlen(str);
03773                 snd_input_t *input;
03774                 snd_config_iterator_t i, next;
03775                 while (1) {
03776                         switch (str[--len]) {
03777                         case ' ':
03778                         case '\f':
03779                         case '\t':
03780                         case '\n':
03781                         case '\r':
03782                                 continue;
03783                         default:
03784                                 break;
03785                         }
03786                         break;
03787                 }
03788                 if (str[len] != '}')
03789                         return -EINVAL;
03790                 err = snd_input_buffer_open(&input, str + 1, len - 1);
03791                 if (err < 0)
03792                         return err;
03793                 err = snd_config_load_override(subs, input);
03794                 snd_input_close(input);
03795                 if (err < 0)
03796                         return err;
03797                 snd_config_for_each(i, next, subs) {
03798                         snd_config_t *n = snd_config_iterator_entry(i);
03799                         snd_config_t *d;
03800                         const char *id = n->id;
03801                         err = snd_config_search(defs, id, &d);
03802                         if (err < 0) {
03803                                 SNDERR("Unknown parameter %s", id);
03804                                 return err;
03805                         }
03806                 }
03807                 return 0;
03808         }
03809         
03810         while (1) {
03811                 char buf[256];
03812                 const char *var = buf;
03813                 unsigned int varlen;
03814                 snd_config_t *def, *sub, *typ;
03815                 const char *new = str;
03816                 const char *tmp;
03817                 char *val = NULL;
03818                 err = parse_arg(&new, &varlen, &val);
03819                 if (err < 0)
03820                         goto _err;
03821                 if (varlen > 0) {
03822                         assert(varlen < sizeof(buf));
03823                         memcpy(buf, str, varlen);
03824                         buf[varlen] = 0;
03825                 } else {
03826                         sprintf(buf, "%d", arg);
03827                 }
03828                 err = snd_config_search_alias(defs, NULL, var, &def);
03829                 if (err < 0) {
03830                         SNDERR("Unknown parameter %s", var);
03831                         goto _err;
03832                 }
03833                 if (snd_config_get_type(def) != SND_CONFIG_TYPE_COMPOUND) {
03834                         SNDERR("Parameter %s definition is not correct", var);
03835                         err = -EINVAL;
03836                         goto _err;
03837                 }
03838                 var = def->id;
03839                 err = snd_config_search(subs, var, &sub);
03840                 if (err >= 0)
03841                         snd_config_delete(sub);
03842                 err = snd_config_search(def, "type", &typ);
03843                 if (err < 0) {
03844                 _invalid_type:
03845                         SNDERR("Parameter %s definition is missing a valid type info", var);
03846                         goto _err;
03847                 }
03848                 err = snd_config_get_string(typ, &tmp);
03849                 if (err < 0)
03850                         goto _invalid_type;
03851                 if (strcmp(tmp, "integer") == 0) {
03852                         long v;
03853                         err = snd_config_make(&sub, var, SND_CONFIG_TYPE_INTEGER);
03854                         if (err < 0)
03855                                 goto _err;
03856                         err = safe_strtol(val, &v);
03857                         if (err < 0) {
03858                                 SNDERR("Parameter %s must be an integer", var);
03859                                 goto _err;
03860                         }
03861                         err = snd_config_set_integer(sub, v);
03862                         if (err < 0)
03863                                 goto _err;
03864                 } else if (strcmp(tmp, "integer64") == 0) {
03865                         long long v;
03866                         err = snd_config_make(&sub, var, SND_CONFIG_TYPE_INTEGER64);
03867                         if (err < 0)
03868                                 goto _err;
03869                         err = safe_strtoll(val, &v);
03870                         if (err < 0) {
03871                                 SNDERR("Parameter %s must be an integer", var);
03872                                 goto _err;
03873                         }
03874                         err = snd_config_set_integer64(sub, v);
03875                         if (err < 0)
03876                                 goto _err;
03877                 } else if (strcmp(tmp, "real") == 0) {
03878                         double v;
03879                         err = snd_config_make(&sub, var, SND_CONFIG_TYPE_REAL);
03880                         if (err < 0)
03881                                 goto _err;
03882                         err = safe_strtod(val, &v);
03883                         if (err < 0) {
03884                                 SNDERR("Parameter %s must be a real", var);
03885                                 goto _err;
03886                         }
03887                         err = snd_config_set_real(sub, v);
03888                         if (err < 0)
03889                                 goto _err;
03890                 } else if (strcmp(tmp, "string") == 0) {
03891                         err = snd_config_make(&sub, var, SND_CONFIG_TYPE_STRING);
03892                         if (err < 0)
03893                                 goto _err;
03894                         err = snd_config_set_string(sub, val);
03895                         if (err < 0)
03896                                 goto _err;
03897                 } else {
03898                         err = -EINVAL;
03899                         goto _invalid_type;
03900                 }
03901                 err = snd_config_set_id(sub, var);
03902                 if (err < 0)
03903                         goto _err;
03904                 err = snd_config_add(subs, sub);
03905                 if (err < 0) {
03906                 _err:
03907                         free(val);
03908                         return err;
03909                 }
03910                 free(val);
03911                 if (!*new)
03912                         break;
03913                 if (*new != ',')
03914                         return -EINVAL;
03915                 str = new + 1;
03916                 arg++;
03917         }
03918         return 0;
03919 }
03920 
03931 int snd_config_expand(snd_config_t *config, snd_config_t *root, const char *args,
03932                       snd_config_t *private_data, snd_config_t **result)
03933 {
03934         int err;
03935         snd_config_t *defs, *subs = NULL, *res;
03936         err = snd_config_search(config, "@args", &defs);
03937         if (err < 0) {
03938                 err = snd_config_copy(&res, config);
03939                 if (err < 0)
03940                         return err;
03941         } else {
03942                 err = snd_config_top(&subs);
03943                 if (err < 0)
03944                         return err;
03945                 err = load_defaults(subs, defs);
03946                 if (err < 0) {
03947                         SNDERR("Load defaults error: %s", snd_strerror(err));
03948                         goto _end;
03949                 }
03950                 err = parse_args(subs, args, defs);
03951                 if (err < 0) {
03952                         SNDERR("Parse arguments error: %s", snd_strerror(err));
03953                         goto _end;
03954                 }
03955                 err = snd_config_evaluate(subs, root, private_data, NULL);
03956                 if (err < 0) {
03957                         SNDERR("Args evaluate error: %s", snd_strerror(err));
03958                         goto _end;
03959                 }
03960                 err = snd_config_walk(config, root, &res, _snd_config_expand, subs);
03961                 if (err < 0) {
03962                         SNDERR("Expand error (walk): %s", snd_strerror(err));
03963                         goto _end;
03964                 }
03965         }
03966         err = snd_config_evaluate(res, root, private_data, NULL);
03967         if (err < 0) {
03968                 SNDERR("Evaluate error: %s", snd_strerror(err));
03969                 snd_config_delete(res);
03970                 goto _end;
03971         }
03972         *result = res;
03973         err = 1;
03974  _end:
03975         if (subs)
03976                 snd_config_delete(subs);
03977         return err;
03978 }
03979         
03996 int snd_config_search_definition(snd_config_t *config,
03997                                  const char *base, const char *name,
03998                                  snd_config_t **result)
03999 {
04000         snd_config_t *conf;
04001         char *key;
04002         const char *args = strchr(name, ':');
04003         int err;
04004         if (args) {
04005                 args++;
04006                 key = alloca(args - name);
04007                 memcpy(key, name, args - name - 1);
04008                 key[args - name - 1] = '\0';
04009         } else {
04010                 key = (char *) name;
04011         }
04012         /*
04013          *  if key contains dot (.), the implicit base is ignored
04014          *  and the key starts from root given by the 'config' parameter
04015          */
04016         err = snd_config_search_alias_hooks(config, strchr(key, '.') ? NULL : base, key, &conf);
04017         if (err < 0)
04018                 return err;
04019         return snd_config_expand(conf, config, args, NULL, result);
04020 }
04021 
04022 #ifndef DOC_HIDDEN
04023 void snd_config_set_hop(snd_config_t *conf, int hop)
04024 {
04025         conf->hop = hop;
04026 }
04027 
04028 int snd_config_check_hop(snd_config_t *conf)
04029 {
04030         if (conf) {
04031                 if (conf->hop >= SND_CONF_MAX_HOPS) {
04032                         SYSERR("Too many definition levels (looped?)\n");
04033                         return -EINVAL;
04034                 }
04035                 return conf->hop;
04036         }
04037         return 0;
04038 }
04039 #endif
04040 
04041 #if 0
04042 /* Not strictly needed, but useful to check for memory leaks */
04043 void _snd_config_end(void) __attribute__ ((destructor));
04044 
04045 static void _snd_config_end(void)
04046 {
04047         int k;
04048         if (snd_config)
04049                 snd_config_delete(snd_config);
04050         snd_config = 0;
04051         for (k = 0; k < files_info_count; ++k)
04052                 free(files_info[k].name);
04053         free(files_info);
04054         files_info = NULL;
04055         files_info_count = 0;
04056 }
04057 #endif

Generated on Fri Nov 28 00:06:23 2008 for elphel by  doxygen 1.5.1