1/***
2  This file is part of PulseAudio.
3
4  Copyright 2004-2006 Lennart Poettering
5
6  PulseAudio is free software; you can redistribute it and/or modify
7  it under the terms of the GNU Lesser General Public License as published
8  by the Free Software Foundation; either version 2.1 of the License,
9  or (at your option) any later version.
10
11  PulseAudio is distributed in the hope that it will be useful, but
12  WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  General Public License for more details.
15
16  You should have received a copy of the GNU Lesser General Public License
17  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18***/
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <stdlib.h>
25#include <string.h>
26#include <string.h>
27#include <stdio.h>
28
29#include <pulse/xmalloc.h>
30
31#include <pulsecore/source.h>
32#include <pulsecore/sink.h>
33#include <pulsecore/core-subscribe.h>
34#include <pulsecore/core-util.h>
35#include <pulsecore/macro.h>
36
37#include "namereg.h"
38
39struct namereg_entry {
40    pa_namereg_type_t type;
41    char *name;
42    void *data;
43};
44
45static bool is_valid_char(char c) {
46    return
47        (c >= 'a' && c <= 'z') ||
48        (c >= 'A' && c <= 'Z') ||
49        (c >= '0' && c <= '9') ||
50        c == '.' ||
51        c == '-' ||
52        c == '_';
53}
54
55bool pa_namereg_is_valid_name(const char *name) {
56    const char *c;
57
58    pa_assert(name);
59
60    if (*name == 0)
61        return false;
62
63    for (c = name; *c && (c-name < PA_NAME_MAX); c++)
64        if (!is_valid_char(*c))
65            return false;
66
67    if (*c)
68        return false;
69
70    return true;
71}
72
73bool pa_namereg_is_valid_name_or_wildcard(const char *name, pa_namereg_type_t type) {
74
75    pa_assert(name);
76
77    if (pa_namereg_is_valid_name(name))
78        return true;
79
80    if (type == PA_NAMEREG_SINK &&
81        pa_streq(name, "@DEFAULT_SINK@"))
82        return true;
83
84    if (type == PA_NAMEREG_SOURCE &&
85        (pa_streq(name, "@DEFAULT_SOURCE@") ||
86         pa_streq(name, "@DEFAULT_MONITOR@")))
87        return true;
88
89    return false;
90}
91
92char* pa_namereg_make_valid_name(const char *name) {
93    const char *a;
94    char *b, *n;
95
96    if (*name == 0)
97        return NULL;
98
99    n = pa_xnew(char, strlen(name)+1);
100
101    for (a = name, b = n; *a && (a-name < PA_NAME_MAX); a++, b++)
102        *b = (char) (is_valid_char(*a) ? *a : '_');
103
104    *b = 0;
105
106    return n;
107}
108
109const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, bool fail) {
110    struct namereg_entry *e;
111    char *n = NULL;
112
113    pa_assert(c);
114    pa_assert(name);
115    pa_assert(data);
116
117    if (!*name)
118        return NULL;
119
120    if ((type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE || type == PA_NAMEREG_CARD) &&
121        !pa_namereg_is_valid_name(name)) {
122
123        if (fail)
124            return NULL;
125
126        if (!(name = n = pa_namereg_make_valid_name(name)))
127            return NULL;
128    }
129
130    if ((e = pa_hashmap_get(c->namereg, name)) && fail) {
131        pa_xfree(n);
132        return NULL;
133    }
134
135    if (e) {
136        unsigned i;
137        size_t l = strlen(name);
138        char *k;
139
140        if (l+4 > PA_NAME_MAX) {
141            pa_xfree(n);
142            return NULL;
143        }
144
145        k = pa_xmalloc(l+4);
146
147        for (i = 2; i <= 99; i++) {
148            pa_snprintf(k, l+4, "%s.%u", name, i);
149
150            if (!(e = pa_hashmap_get(c->namereg, k)))
151                break;
152        }
153
154        if (e) {
155            pa_xfree(n);
156            pa_xfree(k);
157            return NULL;
158        }
159
160        pa_xfree(n);
161        n = k;
162    }
163
164    e = pa_xnew(struct namereg_entry, 1);
165    e->type = type;
166    e->name = n ? n : pa_xstrdup(name);
167    e->data = data;
168
169    pa_assert_se(pa_hashmap_put(c->namereg, e->name, e) >= 0);
170
171    return e->name;
172}
173
174void pa_namereg_unregister(pa_core *c, const char *name) {
175    struct namereg_entry *e;
176
177    pa_assert(c);
178    pa_assert(name);
179
180    pa_assert_se(e = pa_hashmap_remove(c->namereg, name));
181    pa_xfree(e->name);
182    pa_xfree(e);
183}
184
185void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) {
186    struct namereg_entry *e;
187    uint32_t idx;
188    pa_assert(c);
189
190    if (type == PA_NAMEREG_SOURCE && (!name || pa_streq(name, "@DEFAULT_SOURCE@"))) {
191        return c->default_source;
192
193    } else if (type == PA_NAMEREG_SINK && (!name || pa_streq(name, "@DEFAULT_SINK@"))) {
194        return c->default_sink;
195
196    } else if (type == PA_NAMEREG_SOURCE && name && pa_streq(name, "@DEFAULT_MONITOR@")) {
197        if (c->default_sink)
198            return c->default_sink->monitor_source;
199        else
200            return NULL;
201    }
202
203    if (!name)
204        return NULL;
205
206    if ((type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE || type == PA_NAMEREG_CARD) &&
207        !pa_namereg_is_valid_name(name))
208        return NULL;
209
210    if ((e = pa_hashmap_get(c->namereg, name)))
211        if (e->type == type)
212            return e->data;
213
214    if (pa_atou(name, &idx) < 0)
215        return NULL;
216
217    if (type == PA_NAMEREG_SINK)
218        return pa_idxset_get_by_index(c->sinks, idx);
219    else if (type == PA_NAMEREG_SOURCE)
220        return pa_idxset_get_by_index(c->sources, idx);
221    else if (type == PA_NAMEREG_SAMPLE && c->scache)
222        return pa_idxset_get_by_index(c->scache, idx);
223    else if (type == PA_NAMEREG_CARD)
224        return pa_idxset_get_by_index(c->cards, idx);
225
226    return NULL;
227}
228