1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1997-2010 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                  Common Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
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	char		buf[32];
221
222	if (!(vm = vmopen(Vmdcheap, Vmlast, 0)))
223		return 0;
224	if (lib && *lib && (*lib != '-' || *(lib + 1)))
225	{
226		/*
227		 * grab the local part of the library id
228		 */
229
230		if (s = strrchr(lib, ':'))
231			lib = (const char*)(s + 1);
232		i = 2 * sizeof(char**) + strlen(lib) + 5;
233	}
234	else
235	{
236		lib = 0;
237		i = 0;
238	}
239	if (version && *version && (*version != '-' || *(version + 1)))
240		version = 0;
241	if (!(scan = vmnewof(vm, 0, Dllscan_t, 1, i)) || !(scan->tmp = sfstropen()))
242	{
243		vmclose(vm);
244		return 0;
245	}
246	scan->vm = vm;
247	info = dllinfo();
248	scan->flags = info->flags;
249	if (lib)
250	{
251		scan->lib = (char**)(scan + 1);
252		s = *scan->lib = (char*)(scan->lib + 2);
253		sfsprintf(s, i, "lib/%s", lib);
254		if (!version && streq(info->suffix, ".dylib"))
255			version = "0.0";
256	}
257	if (!name || !*name || *name == '-' && !*(name + 1))
258	{
259		name = (const char*)"?*";
260		scan->flags |= DLL_MATCH_NAME;
261	}
262	else if (t = strrchr(name, '/'))
263	{
264		if (!(scan->pb = vmnewof(vm, 0, char, t - (char*)name, 2)))
265			goto bad;
266		memcpy(scan->pb, name, t - (char*)name);
267		name = (const char*)(t + 1);
268	}
269	if (name && !version)
270		for (t = (char*)name; *t; t++)
271			if ((*t == '-' || *t == '.' || *t == '?') && isdigit(*(t + 1)))
272			{
273				if (*t != '-')
274					scan->flags |= DLL_MATCH_VERSION;
275				version = t + 1;
276				if (!(s = vmnewof(vm, 0, char, t - (char*)name, 1)))
277					goto bad;
278				memcpy(s, name, t - (char*)name);
279				name = (const char*)s;
280				break;
281			}
282	if (!version)
283	{
284		scan->flags |= DLL_MATCH_VERSION;
285		sfsprintf(scan->nam, sizeof(scan->nam), "%s%s%s", info->prefix, name, info->suffix);
286	}
287	else if (scan->flags & DLL_INFO_PREVER)
288	{
289		sfprintf(scan->tmp, "%s%s", info->prefix, name);
290		for (s = (char*)version; *s; s++)
291			if (isdigit(*s))
292				sfputc(scan->tmp, *s);
293		sfprintf(scan->tmp, "%s", info->suffix);
294		if (!(s = sfstruse(scan->tmp)))
295			goto bad;
296		sfsprintf(scan->nam, sizeof(scan->nam), "%s", s);
297	}
298	else
299		sfsprintf(scan->nam, sizeof(scan->nam), "%s%s%s.%s", info->prefix, name, info->suffix, version);
300	if (scan->flags & (DLL_MATCH_NAME|DLL_MATCH_VERSION))
301	{
302		if (scan->flags & DLL_INFO_PREVER)
303		{
304			if (!version)
305				version = "*([0-9_])";
306			else
307			{
308				t = buf;
309				for (s = (char*)version; *s; s++)
310					if (isdigit(*s) && t < &buf[sizeof(buf)-1])
311						*t++ = *s;
312				*t = 0;
313				version = (const char*)buf;
314			}
315			sfsprintf(scan->pat, sizeof(scan->pat), "%s%s%s%s", info->prefix, name, version, info->suffix);
316		}
317		else if (version)
318			sfsprintf(scan->pat, sizeof(scan->pat), "%s%s@(%s([-.])%s%s|%s.%s)", info->prefix, name, strchr(version, '.') ? "@" : "?", version, info->suffix, info->suffix, version);
319		else
320		{
321			version = "*([0-9.])";
322			sfsprintf(scan->pat, sizeof(scan->pat), "%s%s@(?([-.])%s%s|%s%s)", info->prefix, name, version, info->suffix, info->suffix, version);
323		}
324	}
325	scan->sp = scan->sb = (scan->lib ? scan->lib : info->sibling);
326	scan->prelen = strlen(info->prefix);
327	scan->suflen = strlen(info->suffix);
328	return scan;
329 bad:
330	dllsclose(scan);
331	return 0;
332}
333
334/*
335 * close a scan stream
336 */
337
338int
339dllsclose(Dllscan_t* scan)
340{
341	if (!scan)
342		return -1;
343	if (scan->fts)
344		fts_close(scan->fts);
345	if (scan->dict)
346		dtclose(scan->dict);
347	if (scan->tmp)
348		sfclose(scan->tmp);
349	if (scan->vm)
350		vmclose(scan->vm);
351	return 0;
352}
353
354/*
355 * return the next scan stream entry
356 */
357
358Dllent_t*
359dllsread(register Dllscan_t* scan)
360{
361	register char*		p;
362	register char*		b;
363	register char*		t;
364	register Uniq_t*	u;
365	register int		n;
366	register int		m;
367
368	if (scan->flags & DLL_MATCH_DONE)
369		return 0;
370 again:
371	do
372	{
373		while (!scan->ent || !(scan->ent = scan->ent->fts_link))
374		{
375			if (scan->fts)
376			{
377				fts_close(scan->fts);
378				scan->fts = 0;
379			}
380			if (!scan->pb)
381				scan->pb = pathbin();
382			else if (!*scan->sp)
383			{
384				scan->sp = scan->sb;
385				if (!*scan->pe++)
386					return 0;
387				scan->pb = scan->pe;
388			}
389			for (p = scan->pp = scan->pb; *p && *p != ':'; p++)
390				if (*p == '/')
391					scan->pp = p;
392			scan->pe = p;
393			if (*scan->sp == bin)
394				scan->off = sfprintf(scan->tmp, "%-.*s", scan->pe - scan->pb, scan->pb);
395			else
396				scan->off = sfprintf(scan->tmp, "%-.*s/%s", scan->pp - scan->pb, scan->pb, *scan->sp);
397			scan->sp++;
398			if (!(scan->flags & DLL_MATCH_NAME))
399			{
400				sfprintf(scan->tmp, "/%s", scan->nam);
401				if (!(p = sfstruse(scan->tmp)))
402					return 0;
403				if (!eaccess(p, R_OK))
404				{
405					b = scan->nam;
406					goto found;
407				}
408				if (errno != ENOENT)
409					continue;
410			}
411			if (scan->flags & (DLL_MATCH_NAME|DLL_MATCH_VERSION))
412			{
413				sfstrseek(scan->tmp, scan->off, SEEK_SET);
414				if (!(t = sfstruse(scan->tmp)))
415					return 0;
416				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)))
417					break;
418			}
419		}
420	} while (!strmatch(scan->ent->fts_name, scan->pat));
421	b = scan->ent->fts_name;
422	sfstrseek(scan->tmp, scan->off, SEEK_SET);
423	sfprintf(scan->tmp, "/%s", b);
424	if (!(p = sfstruse(scan->tmp)))
425		return 0;
426 found:
427	b = scan->buf + sfsprintf(scan->buf, sizeof(scan->buf), "%s", b + scan->prelen);
428	if (!(scan->flags & DLL_INFO_PREVER))
429		while (b > scan->buf)
430		{
431			if (!isdigit(*(b - 1)) && *(b - 1) != '.')
432				break;
433			b--;
434		}
435	b -= scan->suflen;
436	if (b > (scan->buf + 2) && (*(b - 1) == 'g' || *(b - 1) == 'O') && *(b - 2) == '-')
437		b -= 2;
438	n = m = 0;
439	for (t = b; t > scan->buf; t--)
440		if (isdigit(*(t - 1)))
441			n = 1;
442		else if (*(t - 1) != m)
443		{
444			if (*(t - 1) == '.' || *(t - 1) == '-' || *(t - 1) == '_')
445			{
446				n = 1;
447				if (m)
448				{
449					m = -1;
450					t--;
451					break;
452				}
453				m = *(t - 1);
454			}
455			else
456				break;
457		}
458	if (n)
459	{
460		if (isdigit(t[0]) && isdigit(t[1]) && !isdigit(t[2]))
461			n = (t[0] - '0') * 10 + (t[1] - '0');
462		else if (isdigit(t[1]) && isdigit(t[2]) && !isdigit(t[3]))
463			n = (t[1] - '0') * 10 + (t[2] - '0');
464		else
465			n = 0;
466		if (n && !(n & (n - 1)))
467		{
468			if (!isdigit(t[0]))
469				t++;
470			m = *(t += 2);
471		}
472		if (m || (scan->flags & DLL_INFO_PREVER))
473			b = t;
474	}
475	*b = 0;
476	if (!*(b = scan->buf))
477		goto again;
478	if (scan->uniq)
479	{
480		if (!scan->dict)
481		{
482			scan->disc.key = offsetof(Uniq_t, name);
483			scan->disc.size = 0;
484			scan->disc.link = offsetof(Uniq_t, link);
485			if (!(scan->dict = dtopen(&scan->disc, Dthash)))
486				return 0;
487			dtinsert(scan->dict, scan->uniq);
488		}
489		if (dtmatch(scan->dict, b))
490			goto again;
491		if (!(u = vmnewof(scan->vm, 0, Uniq_t, 1, strlen(b))))
492			return 0;
493		strcpy(u->name, b);
494		dtinsert(scan->dict, u);
495	}
496	else if (!(scan->flags & DLL_MATCH_NAME))
497		scan->flags |= DLL_MATCH_DONE;
498	else if (!(scan->uniq = vmnewof(scan->vm, 0, Uniq_t, 1, strlen(b))))
499		return 0;
500	else
501		strcpy(scan->uniq->name, b);
502	scan->entry.name = b;
503	scan->entry.path = p;
504	return &scan->entry;
505}
506