1*7c478bd9Sstevel@tonic-gate /*-
2*7c478bd9Sstevel@tonic-gate  * See the file LICENSE for redistribution information.
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * Copyright (c) 1997, 1998
5*7c478bd9Sstevel@tonic-gate  *	Sleepycat Software.  All rights reserved.
6*7c478bd9Sstevel@tonic-gate  */
7*7c478bd9Sstevel@tonic-gate 
8*7c478bd9Sstevel@tonic-gate #include "config.h"
9*7c478bd9Sstevel@tonic-gate 
10*7c478bd9Sstevel@tonic-gate #ifndef lint
11*7c478bd9Sstevel@tonic-gate static const char sccsid[] = "@(#)log_archive.c	10.44 (Sleepycat) 10/9/98";
12*7c478bd9Sstevel@tonic-gate #endif /* not lint */
13*7c478bd9Sstevel@tonic-gate 
14*7c478bd9Sstevel@tonic-gate #ifndef NO_SYSTEM_INCLUDES
15*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
16*7c478bd9Sstevel@tonic-gate 
17*7c478bd9Sstevel@tonic-gate #include <errno.h>
18*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
19*7c478bd9Sstevel@tonic-gate #include <string.h>
20*7c478bd9Sstevel@tonic-gate #include <unistd.h>
21*7c478bd9Sstevel@tonic-gate #endif
22*7c478bd9Sstevel@tonic-gate 
23*7c478bd9Sstevel@tonic-gate #include "db_int.h"
24*7c478bd9Sstevel@tonic-gate #include "db_dispatch.h"
25*7c478bd9Sstevel@tonic-gate #include "shqueue.h"
26*7c478bd9Sstevel@tonic-gate #include "log.h"
27*7c478bd9Sstevel@tonic-gate #include "common_ext.h"
28*7c478bd9Sstevel@tonic-gate #include "clib_ext.h"			/* XXX: needed for getcwd. */
29*7c478bd9Sstevel@tonic-gate 
30*7c478bd9Sstevel@tonic-gate static int __absname __P((char *, char *, char **));
31*7c478bd9Sstevel@tonic-gate static int __build_data __P((DB_LOG *, char *, char ***, void *(*)(size_t)));
32*7c478bd9Sstevel@tonic-gate static int __cmpfunc __P((const void *, const void *));
33*7c478bd9Sstevel@tonic-gate static int __usermem __P((char ***, void *(*)(size_t)));
34*7c478bd9Sstevel@tonic-gate 
35*7c478bd9Sstevel@tonic-gate /*
36*7c478bd9Sstevel@tonic-gate  * log_archive --
37*7c478bd9Sstevel@tonic-gate  *	Supporting function for db_archive(1).
38*7c478bd9Sstevel@tonic-gate  */
39*7c478bd9Sstevel@tonic-gate int
log_archive(dblp,listp,flags,db_malloc)40*7c478bd9Sstevel@tonic-gate log_archive(dblp, listp, flags, db_malloc)
41*7c478bd9Sstevel@tonic-gate 	DB_LOG *dblp;
42*7c478bd9Sstevel@tonic-gate 	char ***listp;
43*7c478bd9Sstevel@tonic-gate 	u_int32_t flags;
44*7c478bd9Sstevel@tonic-gate 	void *(*db_malloc) __P((size_t));
45*7c478bd9Sstevel@tonic-gate {
46*7c478bd9Sstevel@tonic-gate 	DBT rec;
47*7c478bd9Sstevel@tonic-gate 	DB_LSN stable_lsn;
48*7c478bd9Sstevel@tonic-gate 	u_int32_t fnum;
49*7c478bd9Sstevel@tonic-gate 	int array_size, n, ret;
50*7c478bd9Sstevel@tonic-gate 	char **array, **arrayp, *name, *p, *pref, buf[MAXPATHLEN];
51*7c478bd9Sstevel@tonic-gate 
52*7c478bd9Sstevel@tonic-gate 	name = NULL;
53*7c478bd9Sstevel@tonic-gate 	COMPQUIET(fnum, 0);
54*7c478bd9Sstevel@tonic-gate 
55*7c478bd9Sstevel@tonic-gate 	LOG_PANIC_CHECK(dblp);
56*7c478bd9Sstevel@tonic-gate 
57*7c478bd9Sstevel@tonic-gate #define	OKFLAGS	(DB_ARCH_ABS | DB_ARCH_DATA | DB_ARCH_LOG)
58*7c478bd9Sstevel@tonic-gate 	if (flags != 0) {
59*7c478bd9Sstevel@tonic-gate 		if ((ret =
60*7c478bd9Sstevel@tonic-gate 		    __db_fchk(dblp->dbenv, "log_archive", flags, OKFLAGS)) != 0)
61*7c478bd9Sstevel@tonic-gate 			return (ret);
62*7c478bd9Sstevel@tonic-gate 		if ((ret =
63*7c478bd9Sstevel@tonic-gate 		    __db_fcchk(dblp->dbenv,
64*7c478bd9Sstevel@tonic-gate 		        "log_archive", flags, DB_ARCH_DATA, DB_ARCH_LOG)) != 0)
65*7c478bd9Sstevel@tonic-gate 			return (ret);
66*7c478bd9Sstevel@tonic-gate 	}
67*7c478bd9Sstevel@tonic-gate 
68*7c478bd9Sstevel@tonic-gate 	/*
69*7c478bd9Sstevel@tonic-gate 	 * Get the absolute pathname of the current directory.  It would
70*7c478bd9Sstevel@tonic-gate 	 * be nice to get the shortest pathname of the database directory,
71*7c478bd9Sstevel@tonic-gate 	 * but that's just not possible.
72*7c478bd9Sstevel@tonic-gate 	 */
73*7c478bd9Sstevel@tonic-gate 	if (LF_ISSET(DB_ARCH_ABS)) {
74*7c478bd9Sstevel@tonic-gate 		errno = 0;
75*7c478bd9Sstevel@tonic-gate 		if ((pref = getcwd(buf, sizeof(buf))) == NULL)
76*7c478bd9Sstevel@tonic-gate 			return (errno == 0 ? ENOMEM : errno);
77*7c478bd9Sstevel@tonic-gate 	} else
78*7c478bd9Sstevel@tonic-gate 		pref = NULL;
79*7c478bd9Sstevel@tonic-gate 
80*7c478bd9Sstevel@tonic-gate 	switch (LF_ISSET(~DB_ARCH_ABS)) {
81*7c478bd9Sstevel@tonic-gate 	case DB_ARCH_DATA:
82*7c478bd9Sstevel@tonic-gate 		return (__build_data(dblp, pref, listp, db_malloc));
83*7c478bd9Sstevel@tonic-gate 	case DB_ARCH_LOG:
84*7c478bd9Sstevel@tonic-gate 		memset(&rec, 0, sizeof(rec));
85*7c478bd9Sstevel@tonic-gate 		if (F_ISSET(dblp, DB_AM_THREAD))
86*7c478bd9Sstevel@tonic-gate 			F_SET(&rec, DB_DBT_MALLOC);
87*7c478bd9Sstevel@tonic-gate 		if ((ret = log_get(dblp, &stable_lsn, &rec, DB_LAST)) != 0)
88*7c478bd9Sstevel@tonic-gate 			return (ret);
89*7c478bd9Sstevel@tonic-gate 		if (F_ISSET(dblp, DB_AM_THREAD))
90*7c478bd9Sstevel@tonic-gate 			__os_free(rec.data, rec.size);
91*7c478bd9Sstevel@tonic-gate 		fnum = stable_lsn.file;
92*7c478bd9Sstevel@tonic-gate 		break;
93*7c478bd9Sstevel@tonic-gate 	case 0:
94*7c478bd9Sstevel@tonic-gate 		if ((ret = __log_findckp(dblp, &stable_lsn)) != 0) {
95*7c478bd9Sstevel@tonic-gate 			/*
96*7c478bd9Sstevel@tonic-gate 			 * A return of DB_NOTFOUND means that we didn't find
97*7c478bd9Sstevel@tonic-gate 			 * any records in the log (so we are not going to be
98*7c478bd9Sstevel@tonic-gate 			 * deleting any log files).
99*7c478bd9Sstevel@tonic-gate 			 */
100*7c478bd9Sstevel@tonic-gate 			if (ret != DB_NOTFOUND)
101*7c478bd9Sstevel@tonic-gate 				return (ret);
102*7c478bd9Sstevel@tonic-gate 			*listp = NULL;
103*7c478bd9Sstevel@tonic-gate 			return (0);
104*7c478bd9Sstevel@tonic-gate 		}
105*7c478bd9Sstevel@tonic-gate 		/* Remove any log files before the last stable LSN. */
106*7c478bd9Sstevel@tonic-gate 		fnum = stable_lsn.file - 1;
107*7c478bd9Sstevel@tonic-gate 		break;
108*7c478bd9Sstevel@tonic-gate 	}
109*7c478bd9Sstevel@tonic-gate 
110*7c478bd9Sstevel@tonic-gate #define	LIST_INCREMENT	64
111*7c478bd9Sstevel@tonic-gate 	/* Get some initial space. */
112*7c478bd9Sstevel@tonic-gate 	array_size = 10;
113*7c478bd9Sstevel@tonic-gate 	if ((ret = __os_malloc(sizeof(char *) * array_size, NULL, &array)) != 0)
114*7c478bd9Sstevel@tonic-gate 		return (ret);
115*7c478bd9Sstevel@tonic-gate 	array[0] = NULL;
116*7c478bd9Sstevel@tonic-gate 
117*7c478bd9Sstevel@tonic-gate 	/* Build an array of the file names. */
118*7c478bd9Sstevel@tonic-gate 	for (n = 0; fnum > 0; --fnum) {
119*7c478bd9Sstevel@tonic-gate 		if ((ret = __log_name(dblp, fnum, &name, NULL, 0)) != 0)
120*7c478bd9Sstevel@tonic-gate 			goto err;
121*7c478bd9Sstevel@tonic-gate 		if (__os_exists(name, NULL) != 0) {
122*7c478bd9Sstevel@tonic-gate 			__os_freestr(name);
123*7c478bd9Sstevel@tonic-gate 			name = NULL;
124*7c478bd9Sstevel@tonic-gate 			break;
125*7c478bd9Sstevel@tonic-gate 		}
126*7c478bd9Sstevel@tonic-gate 
127*7c478bd9Sstevel@tonic-gate 		if (n >= array_size - 1) {
128*7c478bd9Sstevel@tonic-gate 			array_size += LIST_INCREMENT;
129*7c478bd9Sstevel@tonic-gate 			if ((ret = __os_realloc(&array,
130*7c478bd9Sstevel@tonic-gate 			    sizeof(char *) * array_size)) != 0)
131*7c478bd9Sstevel@tonic-gate 				goto err;
132*7c478bd9Sstevel@tonic-gate 		}
133*7c478bd9Sstevel@tonic-gate 
134*7c478bd9Sstevel@tonic-gate 		if (LF_ISSET(DB_ARCH_ABS)) {
135*7c478bd9Sstevel@tonic-gate 			if ((ret = __absname(pref, name, &array[n])) != 0)
136*7c478bd9Sstevel@tonic-gate 				goto err;
137*7c478bd9Sstevel@tonic-gate 			__os_freestr(name);
138*7c478bd9Sstevel@tonic-gate 		} else if ((p = __db_rpath(name)) != NULL) {
139*7c478bd9Sstevel@tonic-gate 			if ((ret = __os_strdup(p + 1, &array[n])) != 0)
140*7c478bd9Sstevel@tonic-gate 				goto err;
141*7c478bd9Sstevel@tonic-gate 			__os_freestr(name);
142*7c478bd9Sstevel@tonic-gate 		} else
143*7c478bd9Sstevel@tonic-gate 			array[n] = name;
144*7c478bd9Sstevel@tonic-gate 
145*7c478bd9Sstevel@tonic-gate 		name = NULL;
146*7c478bd9Sstevel@tonic-gate 		array[++n] = NULL;
147*7c478bd9Sstevel@tonic-gate 	}
148*7c478bd9Sstevel@tonic-gate 
149*7c478bd9Sstevel@tonic-gate 	/* If there's nothing to return, we're done. */
150*7c478bd9Sstevel@tonic-gate 	if (n == 0) {
151*7c478bd9Sstevel@tonic-gate 		*listp = NULL;
152*7c478bd9Sstevel@tonic-gate 		ret = 0;
153*7c478bd9Sstevel@tonic-gate 		goto err;
154*7c478bd9Sstevel@tonic-gate 	}
155*7c478bd9Sstevel@tonic-gate 
156*7c478bd9Sstevel@tonic-gate 	/* Sort the list. */
157*7c478bd9Sstevel@tonic-gate 	qsort(array, (size_t)n, sizeof(char *), __cmpfunc);
158*7c478bd9Sstevel@tonic-gate 
159*7c478bd9Sstevel@tonic-gate 	/* Rework the memory. */
160*7c478bd9Sstevel@tonic-gate 	if ((ret = __usermem(&array, db_malloc)) != 0)
161*7c478bd9Sstevel@tonic-gate 		goto err;
162*7c478bd9Sstevel@tonic-gate 
163*7c478bd9Sstevel@tonic-gate 	*listp = array;
164*7c478bd9Sstevel@tonic-gate 	return (0);
165*7c478bd9Sstevel@tonic-gate 
166*7c478bd9Sstevel@tonic-gate err:	if (array != NULL) {
167*7c478bd9Sstevel@tonic-gate 		for (arrayp = array; *arrayp != NULL; ++arrayp)
168*7c478bd9Sstevel@tonic-gate 			__os_freestr(*arrayp);
169*7c478bd9Sstevel@tonic-gate 		__os_free(array, sizeof(char *) * array_size);
170*7c478bd9Sstevel@tonic-gate 	}
171*7c478bd9Sstevel@tonic-gate 	if (name != NULL)
172*7c478bd9Sstevel@tonic-gate 		__os_freestr(name);
173*7c478bd9Sstevel@tonic-gate 	return (ret);
174*7c478bd9Sstevel@tonic-gate }
175*7c478bd9Sstevel@tonic-gate 
176*7c478bd9Sstevel@tonic-gate /*
177*7c478bd9Sstevel@tonic-gate  * __build_data --
178*7c478bd9Sstevel@tonic-gate  *	Build a list of datafiles for return.
179*7c478bd9Sstevel@tonic-gate  */
180*7c478bd9Sstevel@tonic-gate static int
__build_data(dblp,pref,listp,db_malloc)181*7c478bd9Sstevel@tonic-gate __build_data(dblp, pref, listp, db_malloc)
182*7c478bd9Sstevel@tonic-gate 	DB_LOG *dblp;
183*7c478bd9Sstevel@tonic-gate 	char *pref, ***listp;
184*7c478bd9Sstevel@tonic-gate 	void *(*db_malloc) __P((size_t));
185*7c478bd9Sstevel@tonic-gate {
186*7c478bd9Sstevel@tonic-gate 	DBT rec;
187*7c478bd9Sstevel@tonic-gate 	DB_LSN lsn;
188*7c478bd9Sstevel@tonic-gate 	__log_register_args *argp;
189*7c478bd9Sstevel@tonic-gate 	u_int32_t rectype;
190*7c478bd9Sstevel@tonic-gate 	int array_size, last, n, nxt, ret;
191*7c478bd9Sstevel@tonic-gate 	char **array, **arrayp, *p, *real_name;
192*7c478bd9Sstevel@tonic-gate 
193*7c478bd9Sstevel@tonic-gate 	/* Get some initial space. */
194*7c478bd9Sstevel@tonic-gate 	array_size = 10;
195*7c478bd9Sstevel@tonic-gate 	if ((ret = __os_malloc(sizeof(char *) * array_size, NULL, &array)) != 0)
196*7c478bd9Sstevel@tonic-gate 		return (ret);
197*7c478bd9Sstevel@tonic-gate 	array[0] = NULL;
198*7c478bd9Sstevel@tonic-gate 
199*7c478bd9Sstevel@tonic-gate 	memset(&rec, 0, sizeof(rec));
200*7c478bd9Sstevel@tonic-gate 	if (F_ISSET(dblp, DB_AM_THREAD))
201*7c478bd9Sstevel@tonic-gate 		F_SET(&rec, DB_DBT_MALLOC);
202*7c478bd9Sstevel@tonic-gate 	for (n = 0, ret = log_get(dblp, &lsn, &rec, DB_FIRST);
203*7c478bd9Sstevel@tonic-gate 	    ret == 0; ret = log_get(dblp, &lsn, &rec, DB_NEXT)) {
204*7c478bd9Sstevel@tonic-gate 		if (rec.size < sizeof(rectype)) {
205*7c478bd9Sstevel@tonic-gate 			ret = EINVAL;
206*7c478bd9Sstevel@tonic-gate 			__db_err(dblp->dbenv, "log_archive: bad log record");
207*7c478bd9Sstevel@tonic-gate 			goto lg_free;
208*7c478bd9Sstevel@tonic-gate 		}
209*7c478bd9Sstevel@tonic-gate 
210*7c478bd9Sstevel@tonic-gate 		memcpy(&rectype, rec.data, sizeof(rectype));
211*7c478bd9Sstevel@tonic-gate 		if (rectype != DB_log_register) {
212*7c478bd9Sstevel@tonic-gate 			if (F_ISSET(dblp, DB_AM_THREAD)) {
213*7c478bd9Sstevel@tonic-gate 				__os_free(rec.data, rec.size);
214*7c478bd9Sstevel@tonic-gate 				rec.data = NULL;
215*7c478bd9Sstevel@tonic-gate 			}
216*7c478bd9Sstevel@tonic-gate 			continue;
217*7c478bd9Sstevel@tonic-gate 		}
218*7c478bd9Sstevel@tonic-gate 		if ((ret = __log_register_read(rec.data, &argp)) != 0) {
219*7c478bd9Sstevel@tonic-gate 			ret = EINVAL;
220*7c478bd9Sstevel@tonic-gate 			__db_err(dblp->dbenv,
221*7c478bd9Sstevel@tonic-gate 			    "log_archive: unable to read log record");
222*7c478bd9Sstevel@tonic-gate 			goto lg_free;
223*7c478bd9Sstevel@tonic-gate 		}
224*7c478bd9Sstevel@tonic-gate 
225*7c478bd9Sstevel@tonic-gate 		if (n >= array_size - 1) {
226*7c478bd9Sstevel@tonic-gate 			array_size += LIST_INCREMENT;
227*7c478bd9Sstevel@tonic-gate 			if ((ret = __os_realloc(&array,
228*7c478bd9Sstevel@tonic-gate 			    sizeof(char *) * array_size)) != 0)
229*7c478bd9Sstevel@tonic-gate 				goto lg_free;
230*7c478bd9Sstevel@tonic-gate 		}
231*7c478bd9Sstevel@tonic-gate 
232*7c478bd9Sstevel@tonic-gate 		if ((ret = __os_strdup(argp->name.data, &array[n])) != 0) {
233*7c478bd9Sstevel@tonic-gate lg_free:		if (F_ISSET(&rec, DB_DBT_MALLOC) && rec.data != NULL)
234*7c478bd9Sstevel@tonic-gate 				__os_free(rec.data, rec.size);
235*7c478bd9Sstevel@tonic-gate 			goto err1;
236*7c478bd9Sstevel@tonic-gate 		}
237*7c478bd9Sstevel@tonic-gate 
238*7c478bd9Sstevel@tonic-gate 		array[++n] = NULL;
239*7c478bd9Sstevel@tonic-gate 		__os_free(argp, 0);
240*7c478bd9Sstevel@tonic-gate 
241*7c478bd9Sstevel@tonic-gate 		if (F_ISSET(dblp, DB_AM_THREAD)) {
242*7c478bd9Sstevel@tonic-gate 			__os_free(rec.data, rec.size);
243*7c478bd9Sstevel@tonic-gate 			rec.data = NULL;
244*7c478bd9Sstevel@tonic-gate 		}
245*7c478bd9Sstevel@tonic-gate 	}
246*7c478bd9Sstevel@tonic-gate 
247*7c478bd9Sstevel@tonic-gate 	/* If there's nothing to return, we're done. */
248*7c478bd9Sstevel@tonic-gate 	if (n == 0) {
249*7c478bd9Sstevel@tonic-gate 		ret = 0;
250*7c478bd9Sstevel@tonic-gate 		*listp = NULL;
251*7c478bd9Sstevel@tonic-gate 		goto err1;
252*7c478bd9Sstevel@tonic-gate 	}
253*7c478bd9Sstevel@tonic-gate 
254*7c478bd9Sstevel@tonic-gate 	/* Sort the list. */
255*7c478bd9Sstevel@tonic-gate 	qsort(array, (size_t)n, sizeof(char *), __cmpfunc);
256*7c478bd9Sstevel@tonic-gate 
257*7c478bd9Sstevel@tonic-gate 	/*
258*7c478bd9Sstevel@tonic-gate 	 * Build the real pathnames, discarding nonexistent files and
259*7c478bd9Sstevel@tonic-gate 	 * duplicates.
260*7c478bd9Sstevel@tonic-gate 	 */
261*7c478bd9Sstevel@tonic-gate 	for (last = nxt = 0; nxt < n;) {
262*7c478bd9Sstevel@tonic-gate 		/*
263*7c478bd9Sstevel@tonic-gate 		 * Discard duplicates.  Last is the next slot we're going
264*7c478bd9Sstevel@tonic-gate 		 * to return to the user, nxt is the next slot that we're
265*7c478bd9Sstevel@tonic-gate 		 * going to consider.
266*7c478bd9Sstevel@tonic-gate 		 */
267*7c478bd9Sstevel@tonic-gate 		if (last != nxt) {
268*7c478bd9Sstevel@tonic-gate 			array[last] = array[nxt];
269*7c478bd9Sstevel@tonic-gate 			array[nxt] = NULL;
270*7c478bd9Sstevel@tonic-gate 		}
271*7c478bd9Sstevel@tonic-gate 		for (++nxt; nxt < n &&
272*7c478bd9Sstevel@tonic-gate 		    strcmp(array[last], array[nxt]) == 0; ++nxt) {
273*7c478bd9Sstevel@tonic-gate 			__os_freestr(array[nxt]);
274*7c478bd9Sstevel@tonic-gate 			array[nxt] = NULL;
275*7c478bd9Sstevel@tonic-gate 		}
276*7c478bd9Sstevel@tonic-gate 
277*7c478bd9Sstevel@tonic-gate 		/* Get the real name. */
278*7c478bd9Sstevel@tonic-gate 		if ((ret = __db_appname(dblp->dbenv,
279*7c478bd9Sstevel@tonic-gate 		    DB_APP_DATA, NULL, array[last], 0, NULL, &real_name)) != 0)
280*7c478bd9Sstevel@tonic-gate 			goto err2;
281*7c478bd9Sstevel@tonic-gate 
282*7c478bd9Sstevel@tonic-gate 		/* If the file doesn't exist, ignore it. */
283*7c478bd9Sstevel@tonic-gate 		if (__os_exists(real_name, NULL) != 0) {
284*7c478bd9Sstevel@tonic-gate 			__os_freestr(real_name);
285*7c478bd9Sstevel@tonic-gate 			__os_freestr(array[last]);
286*7c478bd9Sstevel@tonic-gate 			array[last] = NULL;
287*7c478bd9Sstevel@tonic-gate 			continue;
288*7c478bd9Sstevel@tonic-gate 		}
289*7c478bd9Sstevel@tonic-gate 
290*7c478bd9Sstevel@tonic-gate 		/* Rework the name as requested by the user. */
291*7c478bd9Sstevel@tonic-gate 		__os_freestr(array[last]);
292*7c478bd9Sstevel@tonic-gate 		array[last] = NULL;
293*7c478bd9Sstevel@tonic-gate 		if (pref != NULL) {
294*7c478bd9Sstevel@tonic-gate 			ret = __absname(pref, real_name, &array[last]);
295*7c478bd9Sstevel@tonic-gate 			__os_freestr(real_name);
296*7c478bd9Sstevel@tonic-gate 			if (ret != 0)
297*7c478bd9Sstevel@tonic-gate 				goto err2;
298*7c478bd9Sstevel@tonic-gate 		} else if ((p = __db_rpath(real_name)) != NULL) {
299*7c478bd9Sstevel@tonic-gate 			ret = __os_strdup(p + 1, &array[last]);
300*7c478bd9Sstevel@tonic-gate 			__os_freestr(real_name);
301*7c478bd9Sstevel@tonic-gate 			if (ret != 0)
302*7c478bd9Sstevel@tonic-gate 				goto err2;
303*7c478bd9Sstevel@tonic-gate 		} else
304*7c478bd9Sstevel@tonic-gate 			array[last] = real_name;
305*7c478bd9Sstevel@tonic-gate 		++last;
306*7c478bd9Sstevel@tonic-gate 	}
307*7c478bd9Sstevel@tonic-gate 
308*7c478bd9Sstevel@tonic-gate 	/* NULL-terminate the list. */
309*7c478bd9Sstevel@tonic-gate 	array[last] = NULL;
310*7c478bd9Sstevel@tonic-gate 
311*7c478bd9Sstevel@tonic-gate 	/* Rework the memory. */
312*7c478bd9Sstevel@tonic-gate 	if ((ret = __usermem(&array, db_malloc)) != 0)
313*7c478bd9Sstevel@tonic-gate 		goto err1;
314*7c478bd9Sstevel@tonic-gate 
315*7c478bd9Sstevel@tonic-gate 	*listp = array;
316*7c478bd9Sstevel@tonic-gate 	return (0);
317*7c478bd9Sstevel@tonic-gate 
318*7c478bd9Sstevel@tonic-gate err2:	/*
319*7c478bd9Sstevel@tonic-gate 	 * XXX
320*7c478bd9Sstevel@tonic-gate 	 * We've possibly inserted NULLs into the array list, so clean up a
321*7c478bd9Sstevel@tonic-gate 	 * bit so that the other error processing works.
322*7c478bd9Sstevel@tonic-gate 	 */
323*7c478bd9Sstevel@tonic-gate 	if (array != NULL)
324*7c478bd9Sstevel@tonic-gate 		for (; nxt < n; ++nxt)
325*7c478bd9Sstevel@tonic-gate 			__os_freestr(array[nxt]);
326*7c478bd9Sstevel@tonic-gate 	/* FALLTHROUGH */
327*7c478bd9Sstevel@tonic-gate 
328*7c478bd9Sstevel@tonic-gate err1:	if (array != NULL) {
329*7c478bd9Sstevel@tonic-gate 		for (arrayp = array; *arrayp != NULL; ++arrayp)
330*7c478bd9Sstevel@tonic-gate 			__os_freestr(*arrayp);
331*7c478bd9Sstevel@tonic-gate 		__os_free(array, array_size * sizeof(char *));
332*7c478bd9Sstevel@tonic-gate 	}
333*7c478bd9Sstevel@tonic-gate 	return (ret);
334*7c478bd9Sstevel@tonic-gate }
335*7c478bd9Sstevel@tonic-gate 
336*7c478bd9Sstevel@tonic-gate /*
337*7c478bd9Sstevel@tonic-gate  * __absname --
338*7c478bd9Sstevel@tonic-gate  *	Return an absolute path name for the file.
339*7c478bd9Sstevel@tonic-gate  */
340*7c478bd9Sstevel@tonic-gate static int
__absname(pref,name,newnamep)341*7c478bd9Sstevel@tonic-gate __absname(pref, name, newnamep)
342*7c478bd9Sstevel@tonic-gate 	char *pref, *name, **newnamep;
343*7c478bd9Sstevel@tonic-gate {
344*7c478bd9Sstevel@tonic-gate 	size_t l_pref, l_name;
345*7c478bd9Sstevel@tonic-gate 	int isabspath, ret;
346*7c478bd9Sstevel@tonic-gate 	char *newname;
347*7c478bd9Sstevel@tonic-gate 
348*7c478bd9Sstevel@tonic-gate 	l_name = strlen(name);
349*7c478bd9Sstevel@tonic-gate 	isabspath = __os_abspath(name);
350*7c478bd9Sstevel@tonic-gate 	l_pref = isabspath ? 0 : strlen(pref);
351*7c478bd9Sstevel@tonic-gate 
352*7c478bd9Sstevel@tonic-gate 	/* Malloc space for concatenating the two. */
353*7c478bd9Sstevel@tonic-gate 	if ((ret = __os_malloc(l_pref + l_name + 2, NULL, &newname)) != 0)
354*7c478bd9Sstevel@tonic-gate 		return (ret);
355*7c478bd9Sstevel@tonic-gate 	*newnamep = newname;
356*7c478bd9Sstevel@tonic-gate 
357*7c478bd9Sstevel@tonic-gate 	/* Build the name.  If `name' is an absolute path, ignore any prefix. */
358*7c478bd9Sstevel@tonic-gate 	if (!isabspath) {
359*7c478bd9Sstevel@tonic-gate 		memcpy(newname, pref, l_pref);
360*7c478bd9Sstevel@tonic-gate 		if (strchr(PATH_SEPARATOR, newname[l_pref - 1]) == NULL)
361*7c478bd9Sstevel@tonic-gate 			newname[l_pref++] = PATH_SEPARATOR[0];
362*7c478bd9Sstevel@tonic-gate 	}
363*7c478bd9Sstevel@tonic-gate 	memcpy(newname + l_pref, name, l_name + 1);
364*7c478bd9Sstevel@tonic-gate 
365*7c478bd9Sstevel@tonic-gate 	return (0);
366*7c478bd9Sstevel@tonic-gate }
367*7c478bd9Sstevel@tonic-gate 
368*7c478bd9Sstevel@tonic-gate /*
369*7c478bd9Sstevel@tonic-gate  * __usermem --
370*7c478bd9Sstevel@tonic-gate  *	Create a single chunk of memory that holds the returned information.
371*7c478bd9Sstevel@tonic-gate  *	If the user has their own malloc routine, use it.
372*7c478bd9Sstevel@tonic-gate  */
373*7c478bd9Sstevel@tonic-gate static int
__usermem(listp,db_malloc)374*7c478bd9Sstevel@tonic-gate __usermem(listp, db_malloc)
375*7c478bd9Sstevel@tonic-gate 	char ***listp;
376*7c478bd9Sstevel@tonic-gate 	void *(*db_malloc) __P((size_t));
377*7c478bd9Sstevel@tonic-gate {
378*7c478bd9Sstevel@tonic-gate 	size_t len;
379*7c478bd9Sstevel@tonic-gate 	int ret;
380*7c478bd9Sstevel@tonic-gate 	char **array, **arrayp, **orig, *strp;
381*7c478bd9Sstevel@tonic-gate 
382*7c478bd9Sstevel@tonic-gate 	/* Find out how much space we need. */
383*7c478bd9Sstevel@tonic-gate 	for (len = 0, orig = *listp; *orig != NULL; ++orig)
384*7c478bd9Sstevel@tonic-gate 		len += sizeof(char *) + strlen(*orig) + 1;
385*7c478bd9Sstevel@tonic-gate 	len += sizeof(char *);
386*7c478bd9Sstevel@tonic-gate 
387*7c478bd9Sstevel@tonic-gate 	/* Allocate it and set up the pointers. */
388*7c478bd9Sstevel@tonic-gate 	if ((ret = __os_malloc(len, db_malloc, &array)) != 0)
389*7c478bd9Sstevel@tonic-gate 		return (ret);
390*7c478bd9Sstevel@tonic-gate 
391*7c478bd9Sstevel@tonic-gate 	strp = (char *)(array + (orig - *listp) + 1);
392*7c478bd9Sstevel@tonic-gate 
393*7c478bd9Sstevel@tonic-gate 	/* Copy the original information into the new memory. */
394*7c478bd9Sstevel@tonic-gate 	for (orig = *listp, arrayp = array; *orig != NULL; ++orig, ++arrayp) {
395*7c478bd9Sstevel@tonic-gate 		len = strlen(*orig);
396*7c478bd9Sstevel@tonic-gate 		memcpy(strp, *orig, len + 1);
397*7c478bd9Sstevel@tonic-gate 		*arrayp = strp;
398*7c478bd9Sstevel@tonic-gate 		strp += len + 1;
399*7c478bd9Sstevel@tonic-gate 
400*7c478bd9Sstevel@tonic-gate 		__os_freestr(*orig);
401*7c478bd9Sstevel@tonic-gate 	}
402*7c478bd9Sstevel@tonic-gate 
403*7c478bd9Sstevel@tonic-gate 	/* NULL-terminate the list. */
404*7c478bd9Sstevel@tonic-gate 	*arrayp = NULL;
405*7c478bd9Sstevel@tonic-gate 
406*7c478bd9Sstevel@tonic-gate 	__os_free(*listp, 0);
407*7c478bd9Sstevel@tonic-gate 	*listp = array;
408*7c478bd9Sstevel@tonic-gate 
409*7c478bd9Sstevel@tonic-gate 	return (0);
410*7c478bd9Sstevel@tonic-gate }
411*7c478bd9Sstevel@tonic-gate 
412*7c478bd9Sstevel@tonic-gate static int
__cmpfunc(p1,p2)413*7c478bd9Sstevel@tonic-gate __cmpfunc(p1, p2)
414*7c478bd9Sstevel@tonic-gate 	const void *p1, *p2;
415*7c478bd9Sstevel@tonic-gate {
416*7c478bd9Sstevel@tonic-gate 	return (strcmp(*((char * const *)p1), *((char * const *)p2)));
417*7c478bd9Sstevel@tonic-gate }
418