1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1997-2012 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                 Eclipse Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*          http://www.eclipse.org/org/documents/epl-v10.html           *
11*         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                 Glenn Fowler <gsf@research.att.com>                  *
18*                                                                      *
19***********************************************************************/
20#pragma prototyped
21/*
22 * Glenn Fowler
23 * AT&T Research
24 */
25
26#define _DLLINFO_PRIVATE_ \
27	char*	sib[3]; \
28	char	sibbuf[64]; \
29	char	envbuf[64];
30
31#define _DLLSCAN_PRIVATE_ \
32	Dllent_t	entry; \
33	Uniq_t*		uniq; \
34	int		flags; \
35	Vmalloc_t*	vm; \
36	Dt_t*		dict; \
37	Dtdisc_t	disc; \
38	FTS*		fts; \
39	FTSENT*		ent; \
40	Sfio_t*		tmp; \
41	char**		sb; \
42	char**		sp; \
43	char*		pb; \
44	char*		pp; \
45	char*		pe; \
46	int		off; \
47	int		prelen; \
48	int		suflen; \
49	char**		lib; \
50	char		nam[64]; \
51	char		pat[64]; \
52	char		buf[64];
53
54#define DLL_MATCH_DONE		0x8000
55#define DLL_MATCH_NAME		0x4000
56#define DLL_MATCH_VERSION	0x2000
57
58#include <ast.h>
59#include <cdt.h>
60#include <ctype.h>
61#include <error.h>
62#include <fts.h>
63#include <vmalloc.h>
64
65typedef struct Uniq_s
66{
67	Dtlink_t	link;
68	char		name[1];
69} Uniq_t;
70
71#include <dlldefs.h>
72
73static char		bin[] = "bin";
74static char		lib[] = "lib";
75
76/*
77 * we need a sibling dir in PATH to search for dlls
78 * the confstr LIBPATH provides the local info
79 *
80 *	<sibling-dir>[:<env-var>[:<host-pattern>]][,...]
81 *
82 * if <host-pattern> is present then it must match confstr HOSTTYPE
83 */
84
85Dllinfo_t*
86dllinfo(void)
87{
88	register char*		s;
89	register char*		h;
90	char*			d;
91	char*			v;
92	char*			p;
93	int			dn;
94	int			vn;
95	int			pn;
96	char			pat[256];
97
98	static Dllinfo_t	info;
99
100	if (!info.sibling)
101	{
102		info.sibling = info.sib;
103		if (*(s = astconf("LIBPATH", NiL, NiL)))
104		{
105			while (*s == ':' || *s == ',')
106				s++;
107			if (*s)
108			{
109				h = 0;
110				for (;;)
111				{
112					for (d = s; *s && *s != ':' && *s != ','; s++);
113					if (!(dn = s - d))
114						d = 0;
115					if (*s == ':')
116					{
117						for (v = ++s; *s && *s != ':' && *s != ','; s++);
118						if (!(vn = s - v))
119							v = 0;
120						if (*s == ':')
121						{
122							for (p = ++s; *s && *s != ':' && *s != ','; s++);
123							if (!(pn = s - p))
124								p = 0;
125						}
126						else
127							p = 0;
128					}
129					else
130					{
131						v = 0;
132						p = 0;
133					}
134					while (*s && *s++ != ',');
135					if (!*s || !p || !h && !*(h = astconf("HOSTTYPE", NiL, NiL)))
136						break;
137					if (pn >= sizeof(pat))
138						pn = sizeof(pat) - 1;
139					memcpy(pat, p, pn);
140					pat[pn] = 0;
141					if (strmatch(h, pat))
142						break;
143				}
144				if (d && dn < sizeof(info.sibbuf))
145				{
146					memcpy(info.sibbuf, d, dn);
147					info.sibling[0] = info.sibbuf;
148				}
149				if (v && vn < sizeof(info.envbuf))
150				{
151					memcpy(info.envbuf, v, vn);
152					info.env = info.envbuf;
153				}
154			}
155		}
156		if (!info.sibling[0] || streq(info.sibling[0], bin))
157			info.sibling[0] = bin;
158		if (!streq(info.sibling[0], lib))
159			info.sibling[1] = lib;
160		if (!info.env)
161			info.env = "LD_LIBRARY_PATH";
162		info.prefix = astconf("LIBPREFIX", NiL, NiL);
163		info.suffix = astconf("LIBSUFFIX", NiL, NiL);
164		if (streq(info.suffix, ".dll"))
165			info.flags |= DLL_INFO_PREVER;
166		else
167			info.flags |= DLL_INFO_DOTVER;
168	}
169	return &info;
170}
171
172/*
173 * fts version sort order
174 * higher versions appear first
175 */
176
177static int
178vercmp(FTSENT* const* ap, FTSENT* const* bp)
179{
180	register unsigned char*	a = (unsigned char*)(*ap)->fts_name;
181	register unsigned char*	b = (unsigned char*)(*bp)->fts_name;
182	register int		n;
183	register int		m;
184	char*			e;
185
186	for (;;)
187	{
188		if (isdigit(*a) && isdigit(*b))
189		{
190			m = strtol((char*)a, &e, 10);
191			a = (unsigned char*)e;
192			n = strtol((char*)b, &e, 10);
193			b = (unsigned char*)e;
194			if (n -= m)
195				return n;
196		}
197		if (n = *a - *b)
198			return n;
199		if (!*a++)
200			return *b ? 0 : -1;
201		if (!*b++)
202			return 1;
203	}
204	/*NOTREACHED*/
205}
206
207/*
208 * open a scan stream
209 */
210
211Dllscan_t*
212dllsopen(const char* lib, const char* name, const char* version)
213{
214	register char*	s;
215	register char*	t;
216	Dllscan_t*	scan;
217	Dllinfo_t*	info;
218	Vmalloc_t*	vm;
219	int		i;
220	int		j;
221	int		k;
222	char		buf[32];
223
224	if (!(vm = vmopen(Vmdcheap, Vmlast, 0)))
225		return 0;
226	if (lib && *lib && (*lib != '-' || *(lib + 1)))
227	{
228		/*
229		 * grab the local part of the library id
230		 */
231
232		if (s = strrchr(lib, ':'))
233			lib = (const char*)(s + 1);
234		i = 2 * sizeof(char**) + strlen(lib) + 5;
235	}
236	else
237	{
238		lib = 0;
239		i = 0;
240	}
241	if (version && (!*version || *version == '-' && !*(version + 1)))
242		version = 0;
243	if (!(scan = vmnewof(vm, 0, Dllscan_t, 1, i)) || !(scan->tmp = sfstropen()))
244	{
245		vmclose(vm);
246		return 0;
247	}
248	scan->vm = vm;
249	info = dllinfo();
250	scan->flags = info->flags;
251	if (lib)
252	{
253		scan->lib = (char**)(scan + 1);
254		s = *scan->lib = (char*)(scan->lib + 2);
255		sfsprintf(s, i, "lib/%s", lib);
256		if (!version && streq(info->suffix, ".dylib"))
257			version = "0.0";
258	}
259	if (!name || !*name || *name == '-' && !*(name + 1))
260	{
261		name = (const char*)"?*";
262		scan->flags |= DLL_MATCH_NAME;
263	}
264	else if (t = strrchr(name, '/'))
265	{
266		if (!(scan->pb = vmnewof(vm, 0, char, t - (char*)name, 2)))
267			goto bad;
268		memcpy(scan->pb, name, t - (char*)name);
269		name = (const char*)(t + 1);
270	}
271	if (name)
272	{
273		i = strlen(name);
274		j = strlen(info->prefix);
275		if (!j || i > j && strneq(name, info->prefix, j))
276		{
277			k = strlen(info->suffix);
278			if (i > k && streq(name + i - k, info->suffix))
279			{
280				i -= j + k;
281				if (!(t = vmnewof(vm, 0, char, i, 1)))
282					goto bad;
283				memcpy(t, name + j, i);
284				t[i] = 0;
285				name = (const char*)t;
286			}
287		}
288		if (!version)
289			for (t = (char*)name; *t; t++)
290				if ((*t == '-' || *t == '.' || *t == '?') && isdigit(*(t + 1)))
291				{
292					if (*t != '-')
293						scan->flags |= DLL_MATCH_VERSION;
294					version = t + 1;
295					if (!(s = vmnewof(vm, 0, char, t - (char*)name, 1)))
296						goto bad;
297					memcpy(s, name, t - (char*)name);
298					name = (const char*)s;
299					break;
300				}
301	}
302	if (!version)
303	{
304		scan->flags |= DLL_MATCH_VERSION;
305		sfsprintf(scan->nam, sizeof(scan->nam), "%s%s%s", info->prefix, name, info->suffix);
306	}
307	else if (scan->flags & DLL_INFO_PREVER)
308	{
309		sfprintf(scan->tmp, "%s%s", info->prefix, name);
310		for (s = (char*)version; *s; s++)
311			if (isdigit(*s))
312				sfputc(scan->tmp, *s);
313		sfprintf(scan->tmp, "%s", info->suffix);
314		if (!(s = sfstruse(scan->tmp)))
315			goto bad;
316		sfsprintf(scan->nam, sizeof(scan->nam), "%s", s);
317	}
318	else
319		sfsprintf(scan->nam, sizeof(scan->nam), "%s%s%s.%s", info->prefix, name, info->suffix, version);
320	if (scan->flags & (DLL_MATCH_NAME|DLL_MATCH_VERSION))
321	{
322		if (scan->flags & DLL_INFO_PREVER)
323		{
324			if (!version)
325				version = "*([0-9_])";
326			else
327			{
328				t = buf;
329				for (s = (char*)version; *s; s++)
330					if (isdigit(*s) && t < &buf[sizeof(buf)-1])
331						*t++ = *s;
332				*t = 0;
333				version = (const char*)buf;
334			}
335			sfsprintf(scan->pat, sizeof(scan->pat), "%s%s%s%s", info->prefix, name, version, info->suffix);
336		}
337		else if (version)
338			sfsprintf(scan->pat, sizeof(scan->pat), "%s%s@(%s([-.])%s%s|%s.%s)", info->prefix, name, strchr(version, '.') ? "@" : "?", version, info->suffix, info->suffix, version);
339		else
340		{
341			version = "*([0-9.])";
342			sfsprintf(scan->pat, sizeof(scan->pat), "%s%s@(?([-.])%s%s|%s%s)", info->prefix, name, version, info->suffix, info->suffix, version);
343		}
344	}
345	scan->sp = scan->sb = (scan->lib ? scan->lib : info->sibling);
346	scan->prelen = strlen(info->prefix);
347	scan->suflen = strlen(info->suffix);
348	return scan;
349 bad:
350	dllsclose(scan);
351	return 0;
352}
353
354/*
355 * close a scan stream
356 */
357
358int
359dllsclose(Dllscan_t* scan)
360{
361	if (!scan)
362		return -1;
363	if (scan->fts)
364		fts_close(scan->fts);
365	if (scan->dict)
366		dtclose(scan->dict);
367	if (scan->tmp)
368		sfclose(scan->tmp);
369	if (scan->vm)
370		vmclose(scan->vm);
371	return 0;
372}
373
374/*
375 * return the next scan stream entry
376 */
377
378Dllent_t*
379dllsread(register Dllscan_t* scan)
380{
381	register char*		p;
382	register char*		b;
383	register char*		t;
384	register Uniq_t*	u;
385	register int		n;
386	register int		m;
387
388	if (scan->flags & DLL_MATCH_DONE)
389		return 0;
390 again:
391	do
392	{
393		while (!scan->ent || !(scan->ent = scan->ent->fts_link))
394		{
395			if (scan->fts)
396			{
397				fts_close(scan->fts);
398				scan->fts = 0;
399			}
400			if (!scan->pb)
401				scan->pb = pathbin();
402			else if (!*scan->sp)
403			{
404				scan->sp = scan->sb;
405				if (!*scan->pe++)
406					return 0;
407				scan->pb = scan->pe;
408			}
409			for (p = scan->pp = scan->pb; *p && *p != ':'; p++)
410				if (*p == '/')
411					scan->pp = p;
412			scan->pe = p;
413			if (*scan->sp == bin)
414				scan->off = sfprintf(scan->tmp, "%-.*s", scan->pe - scan->pb, scan->pb);
415			else
416				scan->off = sfprintf(scan->tmp, "%-.*s/%s", scan->pp - scan->pb, scan->pb, *scan->sp);
417			scan->sp++;
418			if (!(scan->flags & DLL_MATCH_NAME))
419			{
420				sfprintf(scan->tmp, "/%s", scan->nam);
421				if (!(p = sfstruse(scan->tmp)))
422					return 0;
423				if (!eaccess(p, R_OK))
424				{
425					b = scan->nam;
426					goto found;
427				}
428				if (errno != ENOENT)
429					continue;
430			}
431			if (scan->flags & (DLL_MATCH_NAME|DLL_MATCH_VERSION))
432			{
433				sfstrseek(scan->tmp, scan->off, SEEK_SET);
434				if (!(t = sfstruse(scan->tmp)))
435					return 0;
436				if ((scan->fts = fts_open((char**)t, FTS_LOGICAL|FTS_NOPOSTORDER|FTS_ONEPATH, vercmp)) && (scan->ent = fts_read(scan->fts)) && (scan->ent = fts_children(scan->fts, FTS_NOSTAT)))
437					break;
438			}
439		}
440	} while (!strmatch(scan->ent->fts_name, scan->pat));
441	b = scan->ent->fts_name;
442	sfstrseek(scan->tmp, scan->off, SEEK_SET);
443	sfprintf(scan->tmp, "/%s", b);
444	if (!(p = sfstruse(scan->tmp)))
445		return 0;
446 found:
447	b = scan->buf + sfsprintf(scan->buf, sizeof(scan->buf), "%s", b + scan->prelen);
448	if (!(scan->flags & DLL_INFO_PREVER))
449		while (b > scan->buf)
450		{
451			if (!isdigit(*(b - 1)) && *(b - 1) != '.')
452				break;
453			b--;
454		}
455	b -= scan->suflen;
456	if (b > (scan->buf + 2) && (*(b - 1) == 'g' || *(b - 1) == 'O') && *(b - 2) == '-')
457		b -= 2;
458	n = m = 0;
459	for (t = b; t > scan->buf; t--)
460		if (isdigit(*(t - 1)))
461			n = 1;
462		else if (*(t - 1) != m)
463		{
464			if (*(t - 1) == '.' || *(t - 1) == '-' || *(t - 1) == '_')
465			{
466				n = 1;
467				if (m)
468				{
469					m = -1;
470					t--;
471					break;
472				}
473				m = *(t - 1);
474			}
475			else
476				break;
477		}
478	if (n)
479	{
480		if (isdigit(t[0]) && isdigit(t[1]) && !isdigit(t[2]))
481			n = (t[0] - '0') * 10 + (t[1] - '0');
482		else if (isdigit(t[1]) && isdigit(t[2]) && !isdigit(t[3]))
483			n = (t[1] - '0') * 10 + (t[2] - '0');
484		else
485			n = 0;
486		if (n && !(n & (n - 1)))
487		{
488			if (!isdigit(t[0]))
489				t++;
490			m = *(t += 2);
491		}
492		if (m || (scan->flags & DLL_INFO_PREVER))
493			b = t;
494	}
495	*b = 0;
496	if (!*(b = scan->buf))
497		goto again;
498	if (scan->uniq)
499	{
500		if (!scan->dict)
501		{
502			scan->disc.key = offsetof(Uniq_t, name);
503			scan->disc.size = 0;
504			scan->disc.link = offsetof(Uniq_t, link);
505			if (!(scan->dict = dtopen(&scan->disc, Dtset)))
506				return 0;
507			dtinsert(scan->dict, scan->uniq);
508		}
509		if (dtmatch(scan->dict, b))
510			goto again;
511		if (!(u = vmnewof(scan->vm, 0, Uniq_t, 1, strlen(b))))
512			return 0;
513		strcpy(u->name, b);
514		dtinsert(scan->dict, u);
515	}
516	else if (!(scan->flags & DLL_MATCH_NAME))
517		scan->flags |= DLL_MATCH_DONE;
518	else if (!(scan->uniq = vmnewof(scan->vm, 0, Uniq_t, 1, strlen(b))))
519		return 0;
520	else
521		strcpy(scan->uniq->name, b);
522	scan->entry.name = b;
523	scan->entry.path = p;
524	errorf("dll", NiL, -1, "dllsread: %s bound to %s", b, p);
525	return &scan->entry;
526}
527