Merge branch 'vendor/LIBARCHIVE'
[dragonfly.git] / libexec / rtld-elf / libmap.c
1 /*
2  * $FreeBSD: src/libexec/rtld-elf/libmap.c,v 1.15 2006/01/31 06:08:28 peter Exp $
3  */
4
5 #include <stdio.h>
6 #include <ctype.h>
7 #include <string.h>
8 #include <stdlib.h>
9 #include <sys/queue.h>
10 #include <sys/param.h>
11
12 #include "debug.h"
13 #include "rtld.h"
14 #include "libmap.h"
15
16 #ifndef _PATH_LIBMAP_CONF
17 #define _PATH_LIBMAP_CONF       "/etc/libmap.conf"
18 #endif
19
20 TAILQ_HEAD(lm_list, lm);
21 struct lm {
22         char *f;
23         char *t;
24
25         TAILQ_ENTRY(lm) lm_link;
26 };
27
28 TAILQ_HEAD(lmp_list, lmp) lmp_head = TAILQ_HEAD_INITIALIZER(lmp_head);
29 struct lmp {
30         char *p;
31         enum { T_EXACT=0, T_BASENAME, T_DIRECTORY } type;
32         struct lm_list lml;
33         TAILQ_ENTRY(lmp) lmp_link;
34 };
35
36 static int      lm_count;
37
38 static void             lmc_parse       (FILE *);
39 static void             lm_add          (const char *, const char *, const char *);
40 static void             lm_free         (struct lm_list *);
41 static char *           lml_find        (struct lm_list *, const char *);
42 static struct lm_list * lmp_find        (const char *);
43 static struct lm_list * lmp_init        (char *);
44 static const char * quickbasename       (const char *);
45 static int      readstrfn       (void * cookie, char *buf, int len);
46 static int      closestrfn      (void * cookie);
47
48 #define iseol(c)        (((c) == '#') || ((c) == '\0') || \
49                          ((c) == '\n') || ((c) == '\r'))
50
51 int
52 lm_init (char *libmap_override)
53 {
54         FILE    *fp;
55
56         dbg("%s(\"%s\")", __func__, libmap_override);
57
58         TAILQ_INIT(&lmp_head);
59
60         fp = fopen(_PATH_LIBMAP_CONF, "r");
61         if (fp) {
62                 lmc_parse(fp);
63                 fclose(fp);
64         }
65
66         if (libmap_override) {
67                 char    *p;
68                 /* do some character replacement to make $LIBMAP look like a
69                    text file, then "open" it with funopen */
70                 libmap_override = xstrdup(libmap_override);
71
72                 for (p = libmap_override; *p; p++) {
73                         switch (*p) {
74                                 case '=':
75                                         *p = ' '; break;
76                                 case ',':
77                                         *p = '\n'; break;
78                         }
79                 }
80                 fp = funopen(libmap_override, readstrfn, NULL, NULL, closestrfn);
81                 if (fp) {
82                         lmc_parse(fp);
83                         fclose(fp);
84                 }
85         }
86
87         return (lm_count == 0);
88 }
89
90 static void
91 lmc_parse (FILE *fp)
92 {
93         char    *cp;
94         char    *f, *t, *c, *p;
95         char    prog[MAXPATHLEN];
96         char    line[MAXPATHLEN + 2];
97
98         dbg("%s(%p)", __func__, fp);
99
100         p = NULL;
101         while ((cp = fgets(line, MAXPATHLEN + 1, fp)) != NULL) {
102                 t = f = c = NULL;
103
104                 /* Skip over leading space */
105                 while (isspace(*cp)) cp++;
106
107                 /* Found a comment or EOL */
108                 if (iseol(*cp)) continue;
109
110                 /* Found a constraint selector */
111                 if (*cp == '[') {
112                         cp++;
113
114                         /* Skip leading space */
115                         while (isspace(*cp)) cp++;
116
117                         /* Found comment, EOL or end of selector */
118                         if  (iseol(*cp) || *cp == ']')
119                                 continue;
120
121                         c = cp++;
122                         /* Skip to end of word */
123                         while (!isspace(*cp) && !iseol(*cp) && *cp != ']')
124                                 cp++;
125
126                         /* Skip and zero out trailing space */
127                         while (isspace(*cp)) *cp++ = '\0';
128
129                         /* Check if there is a closing brace */
130                         if (*cp != ']') continue;
131
132                         /* Terminate string if there was no trailing space */
133                         *cp++ = '\0';
134
135                         /*
136                          * There should be nothing except whitespace or comment
137                           from this point to the end of the line.
138                          */
139                         while(isspace(*cp)) cp++;
140                         if (!iseol(*cp)) continue;
141
142                         strcpy(prog, c);
143                         p = prog;
144                         continue;
145                 }
146
147                 /* Parse the 'from' candidate. */
148                 f = cp++;
149                 while (!isspace(*cp) && !iseol(*cp)) cp++;
150
151                 /* Skip and zero out the trailing whitespace */
152                 while (isspace(*cp)) *cp++ = '\0';
153
154                 /* Found a comment or EOL */
155                 if (iseol(*cp)) continue;
156
157                 /* Parse 'to' mapping */
158                 t = cp++;
159                 while (!isspace(*cp) && !iseol(*cp)) cp++;
160
161                 /* Skip and zero out the trailing whitespace */
162                 while (isspace(*cp)) *cp++ = '\0';
163
164                 /* Should be no extra tokens at this point */
165                 if (!iseol(*cp)) continue;
166
167                 *cp = '\0';
168                 lm_add(p, f, t);
169         }
170 }
171
172 static void
173 lm_free (struct lm_list *lml)
174 {
175         struct lm *lm;
176
177         dbg("%s(%p)", __func__, lml);
178
179         while (!TAILQ_EMPTY(lml)) {
180                 lm = TAILQ_FIRST(lml);
181                 TAILQ_REMOVE(lml, lm, lm_link);
182                 free(lm->f);
183                 free(lm->t);
184                 free(lm);
185         }
186         return;
187 }
188
189 void
190 lm_fini (void)
191 {
192         struct lmp *lmp;
193
194         dbg("%s()", __func__);
195
196         while (!TAILQ_EMPTY(&lmp_head)) {
197                 lmp = TAILQ_FIRST(&lmp_head);
198                 TAILQ_REMOVE(&lmp_head, lmp, lmp_link);
199                 free(lmp->p);
200                 lm_free(&lmp->lml);
201                 free(lmp);
202         }
203         return;
204 }
205
206 static void
207 lm_add (const char *p, const char *f, const char *t)
208 {
209         struct lm_list *lml;
210         struct lm *lm;
211
212         if (p == NULL)
213                 p = "$DEFAULT$";
214
215         dbg("%s(\"%s\", \"%s\", \"%s\")", __func__, p, f, t);
216
217         if ((lml = lmp_find(p)) == NULL)
218                 lml = lmp_init(xstrdup(p));
219
220         lm = xmalloc(sizeof(struct lm));
221         lm->f = xstrdup(f);
222         lm->t = xstrdup(t);
223         TAILQ_INSERT_HEAD(lml, lm, lm_link);
224         lm_count++;
225 }
226
227 char *
228 lm_find (const char *p, const char *f)
229 {
230         struct lm_list *lml;
231         char *t;
232
233         dbg("%s(\"%s\", \"%s\")", __func__, p, f);
234
235         if (p != NULL && (lml = lmp_find(p)) != NULL) {
236                 t = lml_find(lml, f);
237                 if (t != NULL) {
238                         /*
239                          * Add a global mapping if we have
240                          * a successful constrained match.
241                          */
242                         lm_add(NULL, f, t);
243                         return (t);
244                 }
245         }
246         lml = lmp_find("$DEFAULT$");
247         if (lml != NULL)
248                 return (lml_find(lml, f));
249         else
250                 return (NULL);
251 }
252
253 static char *
254 lml_find (struct lm_list *lmh, const char *f)
255 {
256         struct lm *lm;
257
258         dbg("%s(%p, \"%s\")", __func__, lmh, f);
259
260         TAILQ_FOREACH(lm, lmh, lm_link)
261                 if (strcmp(f, lm->f) == 0)
262                         return (lm->t);
263         return (NULL);
264 }
265
266 /* Given an executable name, return a pointer to the translation list or
267    NULL if no matches */
268 static struct lm_list *
269 lmp_find (const char *n)
270 {
271         struct lmp *lmp;
272
273         dbg("%s(\"%s\")", __func__, n);
274
275         TAILQ_FOREACH(lmp, &lmp_head, lmp_link)
276                 if ((lmp->type == T_EXACT && strcmp(n, lmp->p) == 0) ||
277                     (lmp->type == T_DIRECTORY && strncmp(n, lmp->p, strlen(lmp->p)) == 0) ||
278                     (lmp->type == T_BASENAME && strcmp(quickbasename(n), lmp->p) == 0))
279                         return (&lmp->lml);
280         return (NULL);
281 }
282
283 static struct lm_list *
284 lmp_init (char *n)
285 {
286         struct lmp *lmp;
287
288         dbg("%s(\"%s\")", __func__, n);
289
290         lmp = xmalloc(sizeof(struct lmp));
291         lmp->p = n;
292         if (n[strlen(n)-1] == '/')
293                 lmp->type = T_DIRECTORY;
294         else if (strchr(n,'/') == NULL)
295                 lmp->type = T_BASENAME;
296         else
297                 lmp->type = T_EXACT;
298         TAILQ_INIT(&lmp->lml);
299         TAILQ_INSERT_HEAD(&lmp_head, lmp, lmp_link);
300
301         return (&lmp->lml);
302 }
303
304 /* libc basename is overkill.  Return a pointer to the character after the
305    last /, or the original string if there are no slashes. */
306 static const char *
307 quickbasename (const char *path)
308 {
309         const char *p = path;
310         for (; *path; path++) {
311                 if (*path == '/')
312                         p = path+1;
313         }
314         return (p);
315 }
316
317 static int
318 readstrfn(void * cookie, char *buf, int len)
319 {
320         static char     *current;
321         static int      left;
322         int     copied;
323
324         copied = 0;
325         if (!current) {
326                 current = cookie;
327                 left = strlen(cookie);
328         }
329         while (*current && left && len) {
330                 *buf++ = *current++;
331                 left--;
332                 len--;
333                 copied++;
334         }
335         return copied;
336 }
337
338 static int
339 closestrfn(void * cookie)
340 {
341         free(cookie);
342         return 0;
343 }