1140072Strhodes/*
2140072Strhodes * Copyright (c) 1993 Paul Kranenburg
3140072Strhodes * All rights reserved.
4140072Strhodes *
5140072Strhodes * Redistribution and use in source and binary forms, with or without
6140072Strhodes * modification, are permitted provided that the following conditions
7140072Strhodes * are met:
8140072Strhodes * 1. Redistributions of source code must retain the above copyright
9140072Strhodes *    notice, this list of conditions and the following disclaimer.
10140072Strhodes * 2. Redistributions in binary form must reproduce the above copyright
11140072Strhodes *    notice, this list of conditions and the following disclaimer in the
12140072Strhodes *    documentation and/or other materials provided with the distribution.
13140072Strhodes * 3. All advertising materials mentioning features or use of this software
14140072Strhodes *    must display the following acknowledgement:
15140072Strhodes *      This product includes software developed by Paul Kranenburg.
16140072Strhodes * 4. The name of the author may not be used to endorse or promote products
17140072Strhodes *    derived from this software without specific prior written permission
18140072Strhodes *
19140072Strhodes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20140072Strhodes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21140072Strhodes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22140072Strhodes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23140072Strhodes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24140072Strhodes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25140072Strhodes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26140072Strhodes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27140072Strhodes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28140072Strhodes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29140072Strhodes *
30140072Strhodes * $FreeBSD$
31140072Strhodes */
32140072Strhodes
33140072Strhodes#include <sys/param.h>
34140072Strhodes#include <sys/types.h>
35140072Strhodes#include <sys/stat.h>
36140072Strhodes#include <sys/file.h>
37140072Strhodes#include <sys/time.h>
38140072Strhodes#include <a.out.h>
39140072Strhodes#include <ctype.h>
40140072Strhodes#include <dirent.h>
41140072Strhodes#include <err.h>
42140072Strhodes#include <fcntl.h>
43140072Strhodes#include <stdio.h>
44140072Strhodes#include <stdlib.h>
45140072Strhodes#include <string.h>
46140072Strhodes
47140072Strhodes#include <sys/link_aout.h>
48140072Strhodes#include "shlib.h"
49140072Strhodes#include "support.h"
50140072Strhodes
51140072Strhodes/*
52140072Strhodes * Standard directories to search for files specified by -l.
53140072Strhodes */
54140072Strhodes#ifndef STANDARD_SEARCH_DIRS
55140072Strhodes#define STANDARD_SEARCH_DIRS    "/usr/lib/aout"
56140072Strhodes#endif
57140072Strhodes
58140072Strhodes/*
59140072Strhodes * Actual vector of library search directories,
60140072Strhodes * including `-L'ed and LD_LIBRARY_PATH spec'd ones.
61140072Strhodes */
62140072Strhodeschar	 **search_dirs;
63140072Strhodesint	n_search_dirs;
64140072Strhodes
65241737Sedstatic const char *standard_search_dirs[] = {
66140072Strhodes	STANDARD_SEARCH_DIRS
67140072Strhodes};
68140072Strhodes
69140072Strhodes
70140072Strhodesvoid
71140241Sdelphijadd_search_dir(const char *name)
72140072Strhodes{
73140072Strhodes	int n;
74140072Strhodes
75140072Strhodes	for (n = 0; n < n_search_dirs; n++)
76140072Strhodes		if (strcmp(search_dirs[n], name) == 0)
77140072Strhodes			return;
78140072Strhodes	n_search_dirs++;
79140072Strhodes	search_dirs = (char **)
80140072Strhodes		xrealloc(search_dirs, n_search_dirs * sizeof search_dirs[0]);
81140072Strhodes	search_dirs[n_search_dirs - 1] = strdup(name);
82140072Strhodes}
83140072Strhodes
84140072Strhodesvoid
85201217Sedadd_search_path(char *path)
86140072Strhodes{
87140072Strhodes	register char	*cp, *dup;
88140072Strhodes
89140072Strhodes	if (path == NULL)
90140072Strhodes		return;
91140072Strhodes
92140072Strhodes	/* Add search directories from `path' */
93140072Strhodes	path = dup = strdup(path);
94140072Strhodes	while ((cp = strsep(&path, ":")) != NULL)
95140072Strhodes		add_search_dir(cp);
96140072Strhodes	free(dup);
97140072Strhodes}
98140072Strhodes
99140072Strhodesvoid
100201217Sedstd_search_path(void)
101140072Strhodes{
102140072Strhodes	int	i, n;
103140072Strhodes
104140072Strhodes	/* Append standard search directories */
105140072Strhodes	n = sizeof standard_search_dirs / sizeof standard_search_dirs[0];
106140072Strhodes	for (i = 0; i < n; i++)
107140072Strhodes		add_search_dir(standard_search_dirs[i]);
108140072Strhodes}
109140072Strhodes
110140072Strhodes/*
111140072Strhodes * Return true if CP points to a valid dewey number.
112140072Strhodes * Decode and leave the result in the array DEWEY.
113140072Strhodes * Return the number of decoded entries in DEWEY.
114140072Strhodes */
115140072Strhodes
116140072Strhodesint
117201217Sedgetdewey(int dewey[], char *cp)
118140072Strhodes{
119140072Strhodes	int	i, n;
120140072Strhodes
121140072Strhodes	for (n = 0, i = 0; i < MAXDEWEY; i++) {
122140072Strhodes		if (*cp == '\0')
123140072Strhodes			break;
124140072Strhodes
125140072Strhodes		if (*cp == '.') cp++;
126140072Strhodes		if (!isdigit(*cp))
127140072Strhodes			return 0;
128140072Strhodes
129140072Strhodes		dewey[n++] = strtol(cp, &cp, 10);
130140072Strhodes	}
131140072Strhodes
132140072Strhodes	return n;
133140072Strhodes}
134140072Strhodes
135140072Strhodes/*
136140072Strhodes * Compare two dewey arrays.
137140072Strhodes * Return -1 if `d1' represents a smaller value than `d2'.
138140072Strhodes * Return  1 if `d1' represents a greater value than `d2'.
139140072Strhodes * Return  0 if equal.
140140072Strhodes */
141140072Strhodesint
142201217Sedcmpndewey(int d1[], int n1, int d2[], int n2)
143140072Strhodes{
144140072Strhodes	register int	i;
145140072Strhodes
146140072Strhodes	for (i = 0; i < n1 && i < n2; i++) {
147140072Strhodes		if (d1[i] < d2[i])
148140072Strhodes			return -1;
149140072Strhodes		if (d1[i] > d2[i])
150140072Strhodes			return 1;
151140072Strhodes	}
152140072Strhodes
153140072Strhodes	if (n1 == n2)
154140072Strhodes		return 0;
155140072Strhodes
156140072Strhodes	if (i == n1)
157140072Strhodes		return -1;
158140072Strhodes
159140072Strhodes	if (i == n2)
160140072Strhodes		return 1;
161140072Strhodes
162229780Suqs	errx(1, "cmpndewey: can't happen");
163140072Strhodes	return 0;
164140072Strhodes}
165140072Strhodes
166140072Strhodes/*
167140072Strhodes * Search directories for a shared library matching the given
168140072Strhodes * major and minor version numbers.  See search_lib_dir() below for
169140072Strhodes * the detailed matching rules.
170140072Strhodes *
171140072Strhodes * As soon as a directory with an acceptable match is found, the search
172140072Strhodes * terminates.  Subsequent directories are not searched for a better
173140072Strhodes * match.  This is in conformance with the SunOS searching rules.  Also,
174140072Strhodes * it avoids a lot of directory searches that are virtually guaranteed to
175140072Strhodes * be fruitless.
176140072Strhodes *
177140072Strhodes * The return value is a full pathname to the matching library.  The
178140072Strhodes * string is dynamically allocated.  If no matching library is found, the
179140072Strhodes * function returns NULL.
180140072Strhodes */
181140072Strhodes
182140072Strhodeschar *
183201217Sedfindshlib(char *name, int *majorp, int *minorp, int do_dot_a)
184140072Strhodes{
185140072Strhodes	int		i;
186140072Strhodes
187140072Strhodes	for (i = 0; i < n_search_dirs; i++) {
188140072Strhodes		char	*path;
189140072Strhodes
190140072Strhodes		path = search_lib_dir(search_dirs[i], name, majorp, minorp,
191140072Strhodes			do_dot_a);
192140072Strhodes		if(path != NULL)
193140072Strhodes			return path;
194140072Strhodes	}
195140072Strhodes
196140072Strhodes	return NULL;
197140072Strhodes}
198140072Strhodes
199140072Strhodes/*
200140072Strhodes * Search library directories for a file with the given name.  The
201140072Strhodes * return value is a full pathname to the matching file.  The string
202140072Strhodes * is dynamically allocated.  If no matching file is found, the function
203140072Strhodes * returns NULL.
204140072Strhodes */
205140072Strhodes
206140072Strhodeschar *
207201217Sedfind_lib_file(const char *name)
208140072Strhodes{
209140072Strhodes	int		i;
210140072Strhodes
211140072Strhodes	for (i = 0; i < n_search_dirs; i++) {
212140072Strhodes		char		*path = concat(search_dirs[i], "/", name);
213140072Strhodes		struct stat	sb;
214140072Strhodes
215140072Strhodes		if (lstat(path, &sb) != -1)	/* We found it */
216140072Strhodes			return path;
217140072Strhodes
218140072Strhodes		free(path);
219140072Strhodes	}
220140072Strhodes
221140072Strhodes	return NULL;
222140072Strhodes}
223140072Strhodes
224140072Strhodes/*
225140072Strhodes * Search a given directory for a library (preferably shared) satisfying
226140072Strhodes * the given criteria.
227140072Strhodes *
228140072Strhodes * The matching rules are as follows:
229140072Strhodes *
230140072Strhodes *	if(*majorp == -1)
231140072Strhodes *		find the library with the highest major version;
232140072Strhodes *	else
233140072Strhodes *		insist on a major version identical to *majorp;
234140072Strhodes *
235140072Strhodes *	Always find the library with the highest minor version;
236140072Strhodes *	if(*minorp != -1)
237140072Strhodes *		insist on a minor version >= *minorp;
238140072Strhodes *
239140072Strhodes * It is invalid to specify a specific minor number while wildcarding
240140072Strhodes * the major number.
241140072Strhodes *
242140072Strhodes * The actual major and minor numbers found are returned via the pointer
243140072Strhodes * arguments.
244140072Strhodes *
245140072Strhodes * A suitable shared library is always preferred over a static (.a) library.
246140072Strhodes * If do_dot_a is false, then a static library will not be accepted in
247140072Strhodes * any case.
248140072Strhodes *
249140072Strhodes * The return value is a full pathname to the matching library.  The
250140072Strhodes * string is dynamically allocated.  If no matching library is found, the
251140072Strhodes * function returns NULL.
252140072Strhodes */
253140072Strhodes
254140072Strhodeschar *
255201217Sedsearch_lib_dir(char *dir, char *name, int *majorp, int *minorp, int do_dot_a)
256140072Strhodes{
257140241Sdelphij	size_t		namelen;
258140072Strhodes	DIR		*dd;
259140072Strhodes	struct dirent	*dp;
260140072Strhodes	int		best_dewey[MAXDEWEY];
261140072Strhodes	int		best_ndewey;
262140072Strhodes	char		dot_a_name[MAXNAMLEN+1];
263140072Strhodes	char		dot_so_name[MAXNAMLEN+1];
264140072Strhodes
265140072Strhodes	if((dd = opendir(dir)) == NULL)
266140072Strhodes		return NULL;
267140072Strhodes
268140072Strhodes	namelen = strlen(name);
269140072Strhodes	best_ndewey = 0;
270140072Strhodes	dot_a_name[0] = '\0';
271140072Strhodes	dot_so_name[0] = '\0';
272140072Strhodes
273140072Strhodes	while((dp = readdir(dd)) != NULL) {
274140072Strhodes		char *extension;
275140072Strhodes
276140072Strhodes		if(strlen(dp->d_name) < 3 + namelen + 2 ||	/* lib+xxx+.a */
277140072Strhodes		   strncmp(dp->d_name, "lib", 3) != 0 ||
278140072Strhodes		   strncmp(dp->d_name + 3, name, namelen) != 0 ||
279140072Strhodes		   dp->d_name[3+namelen] != '.')
280140072Strhodes			continue;
281140072Strhodes
282140072Strhodes		extension = dp->d_name + 3 + namelen + 1;	/* a or so.* */
283140072Strhodes
284140072Strhodes		if(strncmp(extension, "so.", 3) == 0) {
285140072Strhodes			int cur_dewey[MAXDEWEY];
286140072Strhodes			int cur_ndewey;
287140072Strhodes
288140072Strhodes			cur_ndewey = getdewey(cur_dewey, extension+3);
289140072Strhodes			if(cur_ndewey < 2)	/* Too few version numbers */
290140072Strhodes				continue;
291140072Strhodes
292140072Strhodes			if(*majorp != -1) {	/* Need exact match on major */
293140072Strhodes				if(cur_dewey[0] != *majorp)
294140072Strhodes					continue;
295140072Strhodes				if(*minorp != -1) {  /* Need minor >= minimum */
296140072Strhodes					if(cur_dewey[1] < *minorp)
297140072Strhodes						continue;
298140072Strhodes				}
299140072Strhodes			}
300140072Strhodes
301140072Strhodes			if(cmpndewey(cur_dewey, cur_ndewey, best_dewey,
302140072Strhodes			   best_ndewey) <= 0)	/* No better than prior match */
303140072Strhodes				continue;
304140072Strhodes
305140072Strhodes			/* We found a better match */
306140072Strhodes			strcpy(dot_so_name, dp->d_name);
307140072Strhodes			bcopy(cur_dewey, best_dewey,
308140072Strhodes				cur_ndewey * sizeof best_dewey[0]);
309140072Strhodes			best_ndewey = cur_ndewey;
310140072Strhodes		} else if(do_dot_a && strcmp(extension, "a") == 0)
311140072Strhodes			strcpy(dot_a_name, dp->d_name);
312140072Strhodes	}
313140072Strhodes	closedir(dd);
314140072Strhodes
315140072Strhodes	if(dot_so_name[0] != '\0') {
316140072Strhodes		*majorp = best_dewey[0];
317140072Strhodes		*minorp = best_dewey[1];
318140072Strhodes		return concat(dir, "/", dot_so_name);
319140072Strhodes	}
320140072Strhodes
321140072Strhodes	if(dot_a_name[0] != '\0')
322140072Strhodes		return concat(dir, "/", dot_a_name);
323140072Strhodes
324140072Strhodes	return NULL;
325140072Strhodes}
326