xref: /titanic_41/usr/src/cmd/svr4pkg/pkgremove/special.c (revision 5c51f1241dbbdf2656d0e10011981411ed0c9673)
1*5c51f124SMoriah Waterland /*
2*5c51f124SMoriah Waterland  * CDDL HEADER START
3*5c51f124SMoriah Waterland  *
4*5c51f124SMoriah Waterland  * The contents of this file are subject to the terms of the
5*5c51f124SMoriah Waterland  * Common Development and Distribution License (the "License").
6*5c51f124SMoriah Waterland  * You may not use this file except in compliance with the License.
7*5c51f124SMoriah Waterland  *
8*5c51f124SMoriah Waterland  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*5c51f124SMoriah Waterland  * or http://www.opensolaris.org/os/licensing.
10*5c51f124SMoriah Waterland  * See the License for the specific language governing permissions
11*5c51f124SMoriah Waterland  * and limitations under the License.
12*5c51f124SMoriah Waterland  *
13*5c51f124SMoriah Waterland  * When distributing Covered Code, include this CDDL HEADER in each
14*5c51f124SMoriah Waterland  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*5c51f124SMoriah Waterland  * If applicable, add the following below this CDDL HEADER, with the
16*5c51f124SMoriah Waterland  * fields enclosed by brackets "[]" replaced with your own identifying
17*5c51f124SMoriah Waterland  * information: Portions Copyright [yyyy] [name of copyright owner]
18*5c51f124SMoriah Waterland  *
19*5c51f124SMoriah Waterland  * CDDL HEADER END
20*5c51f124SMoriah Waterland  */
21*5c51f124SMoriah Waterland 
22*5c51f124SMoriah Waterland /*
23*5c51f124SMoriah Waterland  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24*5c51f124SMoriah Waterland  * Use is subject to license terms.
25*5c51f124SMoriah Waterland  */
26*5c51f124SMoriah Waterland 
27*5c51f124SMoriah Waterland 
28*5c51f124SMoriah Waterland /*
29*5c51f124SMoriah Waterland  * special.c
30*5c51f124SMoriah Waterland  *
31*5c51f124SMoriah Waterland  * This module contains code required to remove special contents from
32*5c51f124SMoriah Waterland  * the contents file when a pkgrm is done on a system upgraded to use
33*5c51f124SMoriah Waterland  * the new database.
34*5c51f124SMoriah Waterland  */
35*5c51f124SMoriah Waterland 
36*5c51f124SMoriah Waterland #include <stdio.h>
37*5c51f124SMoriah Waterland #include <stdlib.h>
38*5c51f124SMoriah Waterland #include <assert.h>
39*5c51f124SMoriah Waterland #include <errno.h>
40*5c51f124SMoriah Waterland #include <unistd.h>
41*5c51f124SMoriah Waterland #include <string.h>
42*5c51f124SMoriah Waterland #include <time.h>
43*5c51f124SMoriah Waterland #include <limits.h>
44*5c51f124SMoriah Waterland #include <fnmatch.h>
45*5c51f124SMoriah Waterland #include <sys/types.h>
46*5c51f124SMoriah Waterland #include <sys/stat.h>
47*5c51f124SMoriah Waterland #include <pkgstrct.h>
48*5c51f124SMoriah Waterland #include "pkglib.h"
49*5c51f124SMoriah Waterland #include <libintl.h>
50*5c51f124SMoriah Waterland 
51*5c51f124SMoriah Waterland /* This specifies the maximum length of a contents file line read in. */
52*5c51f124SMoriah Waterland #define	LINESZ	8192
53*5c51f124SMoriah Waterland 
54*5c51f124SMoriah Waterland #define	SPECIAL_MALLOC	"unable to maintain package contents text due to "\
55*5c51f124SMoriah Waterland 			"insufficient memory."
56*5c51f124SMoriah Waterland #define	SPECIAL_ACCESS	"unable to maintain package contents text due to "\
57*5c51f124SMoriah Waterland 			"an access failure."
58*5c51f124SMoriah Waterland #define	SPECIAL_INPUT	"unable to maintain package contents text: alternate "\
59*5c51f124SMoriah Waterland 			"root path too long"
60*5c51f124SMoriah Waterland 
61*5c51f124SMoriah Waterland /*
62*5c51f124SMoriah Waterland  * strcompare
63*5c51f124SMoriah Waterland  *
64*5c51f124SMoriah Waterland  * This function is used by qsort to sort an array of special contents
65*5c51f124SMoriah Waterland  * rule strings.  This array must be sorted to facilitate efficient
66*5c51f124SMoriah Waterland  * rule processing.  See qsort(3c) regarding qsort compare functions.
67*5c51f124SMoriah Waterland  */
68*5c51f124SMoriah Waterland static int
strcompare(const void * pv1,const void * pv2)69*5c51f124SMoriah Waterland strcompare(const void *pv1, const void *pv2)
70*5c51f124SMoriah Waterland {
71*5c51f124SMoriah Waterland 	char **ppc1 = (char **) pv1;
72*5c51f124SMoriah Waterland 	char **ppc2 = (char **) pv2;
73*5c51f124SMoriah Waterland 	int i = strcmp(*ppc1, *ppc2);
74*5c51f124SMoriah Waterland 	if (i < 0)
75*5c51f124SMoriah Waterland 		return (-1);
76*5c51f124SMoriah Waterland 	if (i > 0)
77*5c51f124SMoriah Waterland 		return (1);
78*5c51f124SMoriah Waterland 	return (0);
79*5c51f124SMoriah Waterland }
80*5c51f124SMoriah Waterland 
81*5c51f124SMoriah Waterland /*
82*5c51f124SMoriah Waterland  * match
83*5c51f124SMoriah Waterland  *
84*5c51f124SMoriah Waterland  * This function determines whether a file name (pc) matches a rule
85*5c51f124SMoriah Waterland  * from the special contents file (pcrule).  We assume that neither
86*5c51f124SMoriah Waterland  * string is ever NULL.
87*5c51f124SMoriah Waterland  *
88*5c51f124SMoriah Waterland  * Return: 1 on match, 0 on no match.
89*5c51f124SMoriah Waterland  * Side effects: none.
90*5c51f124SMoriah Waterland  */
91*5c51f124SMoriah Waterland static int
match(const char * pc,char * pcrule)92*5c51f124SMoriah Waterland match(const char *pc, char *pcrule)
93*5c51f124SMoriah Waterland {
94*5c51f124SMoriah Waterland 	int n = strlen(pcrule);
95*5c51f124SMoriah Waterland 	int wild = 0;
96*5c51f124SMoriah Waterland 	if (pcrule[n - 1] == '*') {
97*5c51f124SMoriah Waterland 		wild = 1;
98*5c51f124SMoriah Waterland 		pcrule[n - 1] = '\0';
99*5c51f124SMoriah Waterland 	}
100*5c51f124SMoriah Waterland 
101*5c51f124SMoriah Waterland 	if (!wild) {
102*5c51f124SMoriah Waterland 		if (fnmatch(pc, pcrule, FNM_PATHNAME) == 0 ||
103*5c51f124SMoriah Waterland 		    fnmatch(pc, pcrule, 0) == 0)
104*5c51f124SMoriah Waterland 		return (1);
105*5c51f124SMoriah Waterland 	} else {
106*5c51f124SMoriah Waterland 		int j;
107*5c51f124SMoriah Waterland 		j = strncmp(pc, pcrule, n - 1);
108*5c51f124SMoriah Waterland 		pcrule[n - 1] = '*';
109*5c51f124SMoriah Waterland 		if (j == 0)
110*5c51f124SMoriah Waterland 			return (1);
111*5c51f124SMoriah Waterland 	}
112*5c51f124SMoriah Waterland 	return (0);
113*5c51f124SMoriah Waterland }
114*5c51f124SMoriah Waterland 
115*5c51f124SMoriah Waterland /*
116*5c51f124SMoriah Waterland  * search_special_contents
117*5c51f124SMoriah Waterland  *
118*5c51f124SMoriah Waterland  * This function assumes that a series of calls will be made requesting
119*5c51f124SMoriah Waterland  * whether a given path matches the special contents rules or not.  We
120*5c51f124SMoriah Waterland  * assume that
121*5c51f124SMoriah Waterland  *
122*5c51f124SMoriah Waterland  *   a) the special_contents array is sorted
123*5c51f124SMoriah Waterland  *   b) the calls will be made with paths in a sorted order
124*5c51f124SMoriah Waterland  *
125*5c51f124SMoriah Waterland  * Given that, we can keep track of where the last search ended and
126*5c51f124SMoriah Waterland  * begin the new search at that point.  This reduces the cost of a
127*5c51f124SMoriah Waterland  * special contents matching search to O(n) from O(n^2).
128*5c51f124SMoriah Waterland  *
129*5c51f124SMoriah Waterland  *   ppcSC  A pointer to an array of special contents obtained via
130*5c51f124SMoriah Waterland  *	  get_special_contents().
131*5c51f124SMoriah Waterland  *   path   A path: determine whether it matches the special
132*5c51f124SMoriah Waterland  *	  contents rules or not.
133*5c51f124SMoriah Waterland  *   piX    The position in the special_contents array we have already
134*5c51f124SMoriah Waterland  *	  arrived at through searching.  This must be initialized to
135*5c51f124SMoriah Waterland  *	  zero before initiating a series of search_special_contents
136*5c51f124SMoriah Waterland  *	  operations.
137*5c51f124SMoriah Waterland  *
138*5c51f124SMoriah Waterland  * Example:
139*5c51f124SMoriah Waterland  * {
140*5c51f124SMoriah Waterland  *	int i = 0, j, max;
141*5c51f124SMoriah Waterland  *	char **ppSC = NULL;
142*5c51f124SMoriah Waterland  *	if (get_special_contents(NULL, &ppcSC, &max) != 0) exit(1);
143*5c51f124SMoriah Waterland  *	for (j = 0; paths != NULL && paths[j] != NULL; j++) {
144*5c51f124SMoriah Waterland  *		if (search_special_contents(ppcSC, path[j], &i)) {
145*5c51f124SMoriah Waterland  *			do_something_with_special_path(path[j]);
146*5c51f124SMoriah Waterland  *		}
147*5c51f124SMoriah Waterland  *	}
148*5c51f124SMoriah Waterland  * }
149*5c51f124SMoriah Waterland  *
150*5c51f124SMoriah Waterland  * Return: 1 if there is a match, 0 otherwise.
151*5c51f124SMoriah Waterland  * Side effects: The value of *piX will be set between calls to this
152*5c51f124SMoriah Waterland  *    function.  To make this function thread safe, use search arrays.
153*5c51f124SMoriah Waterland  *    Also:  Nonmatching entries are eliminated, set to NULL.
154*5c51f124SMoriah Waterland  */
155*5c51f124SMoriah Waterland static int
search_special_contents(char ** ppcSC,const char * pcpath,int * piX,int max)156*5c51f124SMoriah Waterland search_special_contents(char **ppcSC, const char *pcpath, int *piX, int max)
157*5c51f124SMoriah Waterland {
158*5c51f124SMoriah Waterland 	int wild;
159*5c51f124SMoriah Waterland 	if (ppcSC == NULL || *piX == max)
160*5c51f124SMoriah Waterland 		return (0);
161*5c51f124SMoriah Waterland 
162*5c51f124SMoriah Waterland 	while (*piX < max) {
163*5c51f124SMoriah Waterland 
164*5c51f124SMoriah Waterland 		int j, k;
165*5c51f124SMoriah Waterland 		if (ppcSC[*piX] == NULL) {
166*5c51f124SMoriah Waterland 			(*piX)++;
167*5c51f124SMoriah Waterland 			continue;
168*5c51f124SMoriah Waterland 		}
169*5c51f124SMoriah Waterland 
170*5c51f124SMoriah Waterland 		j = strlen(ppcSC[*piX]);
171*5c51f124SMoriah Waterland 		k = strcmp(pcpath, ppcSC[*piX]);
172*5c51f124SMoriah Waterland 		wild = (ppcSC[*piX][j - 1] == '*');
173*5c51f124SMoriah Waterland 
174*5c51f124SMoriah Waterland 		/*
175*5c51f124SMoriah Waterland 		 * Depending on whether the path string compared with the
176*5c51f124SMoriah Waterland 		 * rule, we take different actions.  If the path is less
177*5c51f124SMoriah Waterland 		 * than the rule, we keep the rule.  If the path equals
178*5c51f124SMoriah Waterland 		 * the rule, we advance the rule (as long as the rule is
179*5c51f124SMoriah Waterland 		 * not a wild card).  If the path is greater than the rule,
180*5c51f124SMoriah Waterland 		 * we have to advance the rule list until we are less or equal
181*5c51f124SMoriah Waterland 		 * again.  This way we only have to make one pass through the
182*5c51f124SMoriah Waterland 		 * rules, as we make one pass through the path strings.  We
183*5c51f124SMoriah Waterland 		 * assume that the rules and the path strings are sorted.
184*5c51f124SMoriah Waterland 		 */
185*5c51f124SMoriah Waterland 		if (k < 0) {
186*5c51f124SMoriah Waterland 
187*5c51f124SMoriah Waterland 			if (wild == 0)
188*5c51f124SMoriah Waterland 				return (0);
189*5c51f124SMoriah Waterland 
190*5c51f124SMoriah Waterland 			if (match(pcpath, ppcSC[*piX]))
191*5c51f124SMoriah Waterland 				return (1);
192*5c51f124SMoriah Waterland 			break;
193*5c51f124SMoriah Waterland 
194*5c51f124SMoriah Waterland 		} else if (k == 0) {
195*5c51f124SMoriah Waterland 
196*5c51f124SMoriah Waterland 			int x = match(pcpath, ppcSC[*piX]);
197*5c51f124SMoriah Waterland 			if (wild == 0) (*piX)++;
198*5c51f124SMoriah Waterland 			return (x);
199*5c51f124SMoriah Waterland 
200*5c51f124SMoriah Waterland 		} else {
201*5c51f124SMoriah Waterland 			/* One last try. */
202*5c51f124SMoriah Waterland 			if (match(pcpath, ppcSC[*piX]))
203*5c51f124SMoriah Waterland 				return (1);
204*5c51f124SMoriah Waterland 
205*5c51f124SMoriah Waterland 			/*
206*5c51f124SMoriah Waterland 			 * As pcpath > ppcSC[*piX] we have passed up this
207*5c51f124SMoriah Waterland 			 * rule - it cannot apply.  Therefore, we do not
208*5c51f124SMoriah Waterland 			 * need to retain it.  Removing the rule will make
209*5c51f124SMoriah Waterland 			 * subsequent searching more efficient.
210*5c51f124SMoriah Waterland 			 */
211*5c51f124SMoriah Waterland 			free(ppcSC[*piX]);
212*5c51f124SMoriah Waterland 			ppcSC[*piX] = NULL;
213*5c51f124SMoriah Waterland 
214*5c51f124SMoriah Waterland 			(*piX)++;
215*5c51f124SMoriah Waterland 		}
216*5c51f124SMoriah Waterland 	}
217*5c51f124SMoriah Waterland 	return (0);
218*5c51f124SMoriah Waterland }
219*5c51f124SMoriah Waterland 
220*5c51f124SMoriah Waterland /*
221*5c51f124SMoriah Waterland  * get_special_contents
222*5c51f124SMoriah Waterland  *
223*5c51f124SMoriah Waterland  * Retrieves the special contents file entries, if they exist.  These
224*5c51f124SMoriah Waterland  * are sorted.  We do not assume the special_contents file is in sorted
225*5c51f124SMoriah Waterland  * order.
226*5c51f124SMoriah Waterland  *
227*5c51f124SMoriah Waterland  *   pcroot   The root of the install database.  If NULL assume '/'.
228*5c51f124SMoriah Waterland  *   pppcSC   A pointer to a char **.  This pointer will be set to
229*5c51f124SMoriah Waterland  *		point at NULL if there is no special_contents file or
230*5c51f124SMoriah Waterland  *		to a sorted array of strings, NULL terminated, otherwise.
231*5c51f124SMoriah Waterland  *   piMax    The # of entries in the special contents result.
232*5c51f124SMoriah Waterland  *
233*5c51f124SMoriah Waterland  * Returns:  0 on no error, nonzero on error.
234*5c51f124SMoriah Waterland  * Side effects:  the pppcSC pointer is set to point at a newly
235*5c51f124SMoriah Waterland  *   allocated array of pointers to strings..  The caller must
236*5c51f124SMoriah Waterland  *   free this buffer.  The value of *piMax is set to the # of
237*5c51f124SMoriah Waterland  *   entries in ppcSC.
238*5c51f124SMoriah Waterland  */
239*5c51f124SMoriah Waterland static int
get_special_contents(const char * pcroot,char *** pppcSC,int * piMax)240*5c51f124SMoriah Waterland get_special_contents(const char *pcroot, char ***pppcSC, int *piMax)
241*5c51f124SMoriah Waterland {
242*5c51f124SMoriah Waterland 	int e, i;
243*5c51f124SMoriah Waterland 	FILE *fp;
244*5c51f124SMoriah Waterland 	char line[2048];
245*5c51f124SMoriah Waterland 	char **ppc;
246*5c51f124SMoriah Waterland 	char *pc = "var/sadm/install/special_contents";
247*5c51f124SMoriah Waterland 	char path[PATH_MAX];
248*5c51f124SMoriah Waterland 	struct stat s;
249*5c51f124SMoriah Waterland 
250*5c51f124SMoriah Waterland 	/* Initialize the return values. */
251*5c51f124SMoriah Waterland 	*piMax = 0;
252*5c51f124SMoriah Waterland 	*pppcSC = NULL;
253*5c51f124SMoriah Waterland 
254*5c51f124SMoriah Waterland 	if (pcroot == NULL) {
255*5c51f124SMoriah Waterland 		pcroot = "/";
256*5c51f124SMoriah Waterland 	}
257*5c51f124SMoriah Waterland 
258*5c51f124SMoriah Waterland 	if (pcroot[strlen(pcroot) - 1] == '/') {
259*5c51f124SMoriah Waterland 		if (snprintf(path, PATH_MAX, "%s%s", pcroot, pc) >= PATH_MAX) {
260*5c51f124SMoriah Waterland 			progerr(gettext(SPECIAL_INPUT));
261*5c51f124SMoriah Waterland 			return (1);
262*5c51f124SMoriah Waterland 		}
263*5c51f124SMoriah Waterland 	} else {
264*5c51f124SMoriah Waterland 		if (snprintf(path, PATH_MAX, "%s/%s", pcroot, pc)
265*5c51f124SMoriah Waterland 		    >= PATH_MAX) {
266*5c51f124SMoriah Waterland 			progerr(gettext(SPECIAL_INPUT));
267*5c51f124SMoriah Waterland 			return (1);
268*5c51f124SMoriah Waterland 		}
269*5c51f124SMoriah Waterland 	}
270*5c51f124SMoriah Waterland 
271*5c51f124SMoriah Waterland 	errno = 0;
272*5c51f124SMoriah Waterland 	e = stat(path, &s);
273*5c51f124SMoriah Waterland 	if (e != 0 && errno == ENOENT)
274*5c51f124SMoriah Waterland 		return (0); /* No special contents file.  Do nothing. */
275*5c51f124SMoriah Waterland 
276*5c51f124SMoriah Waterland 	if (access(path, R_OK) != 0 || (fp = fopen(path, "r")) == NULL) {
277*5c51f124SMoriah Waterland 		/* Could not open special contents which exists */
278*5c51f124SMoriah Waterland 		progerr(gettext(SPECIAL_ACCESS));
279*5c51f124SMoriah Waterland 		return (1);
280*5c51f124SMoriah Waterland 	}
281*5c51f124SMoriah Waterland 
282*5c51f124SMoriah Waterland 	for (i = 0; fgets(line, 2048, fp) != NULL; i++);
283*5c51f124SMoriah Waterland 	rewind(fp);
284*5c51f124SMoriah Waterland 	if ((ppc = (char **) calloc(i + 1, sizeof (char *))) == NULL) {
285*5c51f124SMoriah Waterland 		progerr(gettext(SPECIAL_MALLOC));
286*5c51f124SMoriah Waterland 		return (1);
287*5c51f124SMoriah Waterland 	}
288*5c51f124SMoriah Waterland 
289*5c51f124SMoriah Waterland 	for (i = 0; fgets(line, 2048, fp) != NULL; ) {
290*5c51f124SMoriah Waterland 		int n;
291*5c51f124SMoriah Waterland 		if (line[0] == '#' || line[0] == ' ' || line[0] == '\n' ||
292*5c51f124SMoriah Waterland 		    line[0] == '\t' || line[0] == '\r')
293*5c51f124SMoriah Waterland 			continue;
294*5c51f124SMoriah Waterland 		n = strlen(line);
295*5c51f124SMoriah Waterland 		if (line[n - 1] == '\n')
296*5c51f124SMoriah Waterland 			line[n - 1] = '\0';
297*5c51f124SMoriah Waterland 		ppc[i++] = strdup(line);
298*5c51f124SMoriah Waterland 	}
299*5c51f124SMoriah Waterland 
300*5c51f124SMoriah Waterland 	qsort(ppc, i, sizeof (char *), strcompare);
301*5c51f124SMoriah Waterland 
302*5c51f124SMoriah Waterland 	*pppcSC = ppc;
303*5c51f124SMoriah Waterland 	*piMax = i;
304*5c51f124SMoriah Waterland 	return (0);
305*5c51f124SMoriah Waterland }
306*5c51f124SMoriah Waterland 
307*5c51f124SMoriah Waterland /*
308*5c51f124SMoriah Waterland  * free_special_contents
309*5c51f124SMoriah Waterland  *
310*5c51f124SMoriah Waterland  * This function frees special_contents which have been allocated using
311*5c51f124SMoriah Waterland  * get_special_contents.
312*5c51f124SMoriah Waterland  *
313*5c51f124SMoriah Waterland  *   pppcSC    A pointer to a buffer allocated using get_special_contents.
314*5c51f124SMoriah Waterland  *   max       The number of entries allocated.
315*5c51f124SMoriah Waterland  *
316*5c51f124SMoriah Waterland  * Result: None.
317*5c51f124SMoriah Waterland  * Side effects: Frees memory allocated using get_special_contents and
318*5c51f124SMoriah Waterland  *    sets the pointer passed in to NULL.
319*5c51f124SMoriah Waterland  */
320*5c51f124SMoriah Waterland static void
free_special_contents(char *** pppcSC,int max)321*5c51f124SMoriah Waterland free_special_contents(char ***pppcSC, int max)
322*5c51f124SMoriah Waterland {
323*5c51f124SMoriah Waterland 	int i;
324*5c51f124SMoriah Waterland 	char **ppc = NULL;
325*5c51f124SMoriah Waterland 	if (*pppcSC == NULL)
326*5c51f124SMoriah Waterland 		return;
327*5c51f124SMoriah Waterland 
328*5c51f124SMoriah Waterland 	ppc = *pppcSC;
329*5c51f124SMoriah Waterland 	for (i = 0; ppc != NULL && i < max; i++)
330*5c51f124SMoriah Waterland 		if (ppc[i] == NULL)
331*5c51f124SMoriah Waterland 			free(ppc[i]);
332*5c51f124SMoriah Waterland 
333*5c51f124SMoriah Waterland 	if (ppc != NULL)
334*5c51f124SMoriah Waterland 		free(ppc);
335*5c51f124SMoriah Waterland 
336*5c51f124SMoriah Waterland 	*pppcSC = NULL;
337*5c51f124SMoriah Waterland }
338*5c51f124SMoriah Waterland 
339*5c51f124SMoriah Waterland /*
340*5c51f124SMoriah Waterland  * get_path
341*5c51f124SMoriah Waterland  *
342*5c51f124SMoriah Waterland  * Return the first field of a string delimited by a space.
343*5c51f124SMoriah Waterland  *
344*5c51f124SMoriah Waterland  *   pcline	A line from the contents file.
345*5c51f124SMoriah Waterland  *
346*5c51f124SMoriah Waterland  * Return: NULL if an error.  Otherwise a string allocated by this
347*5c51f124SMoriah Waterland  *   function.  The caller must free the string.
348*5c51f124SMoriah Waterland  * Side effects: none.
349*5c51f124SMoriah Waterland  */
350*5c51f124SMoriah Waterland static char *
get_path(const char * pcline)351*5c51f124SMoriah Waterland get_path(const char *pcline)
352*5c51f124SMoriah Waterland {
353*5c51f124SMoriah Waterland 	int i = strcspn(pcline, " ");
354*5c51f124SMoriah Waterland 	char *pc = NULL;
355*5c51f124SMoriah Waterland 	if (i <= 1 || (pc = (char *) calloc(i + 1, 1)) == NULL)
356*5c51f124SMoriah Waterland 		return (NULL);
357*5c51f124SMoriah Waterland 	(void) memcpy(pc, pcline, i);
358*5c51f124SMoriah Waterland 	return (pc);
359*5c51f124SMoriah Waterland }
360*5c51f124SMoriah Waterland 
361*5c51f124SMoriah Waterland /*
362*5c51f124SMoriah Waterland  * generate_special_contents_rules
363*5c51f124SMoriah Waterland  *
364*5c51f124SMoriah Waterland  * This procedure will generate an array of integers which will be a mask
365*5c51f124SMoriah Waterland  * to apply to the ppcfextra array.  If set to 1, then the content must be
366*5c51f124SMoriah Waterland  * added to the contents file.  Otherwise it will not be:  The old contents
367*5c51f124SMoriah Waterland  * file will be used for this path value, if one even exists.
368*5c51f124SMoriah Waterland  *
369*5c51f124SMoriah Waterland  *    ient	The number of ppcfextra contents installed.
370*5c51f124SMoriah Waterland  *    ppcfent	The contents installed.
371*5c51f124SMoriah Waterland  *    ppcSC	The rules (special contents)
372*5c51f124SMoriah Waterland  *    max	The number of special contents rules.
373*5c51f124SMoriah Waterland  *    ppiIndex	The array of integer values, determining whether
374*5c51f124SMoriah Waterland  *		individual ppcfextra items match special contents rules.
375*5c51f124SMoriah Waterland  *		This array will be created and set in this function and
376*5c51f124SMoriah Waterland  *		returned.
377*5c51f124SMoriah Waterland  *
378*5c51f124SMoriah Waterland  * Return: 0 success, nonzero failure
379*5c51f124SMoriah Waterland  * Side effects: allocates an array of integers that the caller must free.
380*5c51f124SMoriah Waterland  */
381*5c51f124SMoriah Waterland static int
generate_special_contents_rules(int ient,struct cfent ** ppcfent,char ** ppcSC,int max,int ** ppiIndex)382*5c51f124SMoriah Waterland generate_special_contents_rules(int ient, struct cfent **ppcfent,
383*5c51f124SMoriah Waterland     char **ppcSC, int max, int **ppiIndex)
384*5c51f124SMoriah Waterland {
385*5c51f124SMoriah Waterland 	int i, j;
386*5c51f124SMoriah Waterland 	int *pi = (int *) calloc(ient, sizeof (int));
387*5c51f124SMoriah Waterland 	if (pi == NULL) {
388*5c51f124SMoriah Waterland 		progerr(gettext(SPECIAL_MALLOC));
389*5c51f124SMoriah Waterland 		return (1);
390*5c51f124SMoriah Waterland 	}
391*5c51f124SMoriah Waterland 
392*5c51f124SMoriah Waterland 	/*
393*5c51f124SMoriah Waterland 	 * For each entry in ppcfextra, check if it matches a rule.
394*5c51f124SMoriah Waterland 	 * If it does not, set the entry in the index to -1.
395*5c51f124SMoriah Waterland 	 */
396*5c51f124SMoriah Waterland 	for (i = 0, j = 0; i < ient && j < max; i++) {
397*5c51f124SMoriah Waterland 		if (search_special_contents(ppcSC, ppcfent[i]->path,
398*5c51f124SMoriah Waterland 		    &j, max) == 1) {
399*5c51f124SMoriah Waterland 			pi[i] = 1;
400*5c51f124SMoriah Waterland 
401*5c51f124SMoriah Waterland 		} else {
402*5c51f124SMoriah Waterland 			pi[i] = 0;
403*5c51f124SMoriah Waterland 		}
404*5c51f124SMoriah Waterland 	}
405*5c51f124SMoriah Waterland 
406*5c51f124SMoriah Waterland 	/*
407*5c51f124SMoriah Waterland 	 * In case we ran out of rules before contents, we will not use
408*5c51f124SMoriah Waterland 	 * those contents.  Make sure these contents are set to 0 and
409*5c51f124SMoriah Waterland 	 * will not be copied from the ppcfent array into the contents
410*5c51f124SMoriah Waterland 	 * file.
411*5c51f124SMoriah Waterland 	 */
412*5c51f124SMoriah Waterland 	for (i = i; i < ient; i++)
413*5c51f124SMoriah Waterland 		pi[i] = 0;
414*5c51f124SMoriah Waterland 
415*5c51f124SMoriah Waterland 	*ppiIndex = pi;
416*5c51f124SMoriah Waterland 	return (0);
417*5c51f124SMoriah Waterland }
418*5c51f124SMoriah Waterland 
419*5c51f124SMoriah Waterland 
420*5c51f124SMoriah Waterland /*
421*5c51f124SMoriah Waterland  * pathcmp
422*5c51f124SMoriah Waterland  *
423*5c51f124SMoriah Waterland  * Compare a path to a cfent.  It will match either if the path is
424*5c51f124SMoriah Waterland  * equal to the cfent path, or if the cfent is a symbolic or link
425*5c51f124SMoriah Waterland  * and *that* matches.
426*5c51f124SMoriah Waterland  *
427*5c51f124SMoriah Waterland  *    path	a path
428*5c51f124SMoriah Waterland  *    pent      a contents entry
429*5c51f124SMoriah Waterland  *
430*5c51f124SMoriah Waterland  * Returns: as per strcmp
431*5c51f124SMoriah Waterland  * Side effects: none.
432*5c51f124SMoriah Waterland  */
433*5c51f124SMoriah Waterland static int
pathcmp(const char * pc,const struct cfent * pent)434*5c51f124SMoriah Waterland pathcmp(const char *pc, const struct cfent *pent)
435*5c51f124SMoriah Waterland {
436*5c51f124SMoriah Waterland 	int i;
437*5c51f124SMoriah Waterland 	if ((pent->ftype == 's' || pent->ftype == 'l') &&
438*5c51f124SMoriah Waterland 	    pent->ainfo.local) {
439*5c51f124SMoriah Waterland 		char *p, *q;
440*5c51f124SMoriah Waterland 		if ((p = strstr(pc, "=")) == NULL) {
441*5c51f124SMoriah Waterland 
442*5c51f124SMoriah Waterland 			i = strcmp(pc, pent->path);
443*5c51f124SMoriah Waterland 
444*5c51f124SMoriah Waterland 			/* A path without additional chars strcmp's to less */
445*5c51f124SMoriah Waterland 			if (i == 0)
446*5c51f124SMoriah Waterland 				i = -1;
447*5c51f124SMoriah Waterland 
448*5c51f124SMoriah Waterland 		} else {
449*5c51f124SMoriah Waterland 			/* Break the link path into two pieces. */
450*5c51f124SMoriah Waterland 			*p = '\0';
451*5c51f124SMoriah Waterland 
452*5c51f124SMoriah Waterland 			/* Compare the first piece. */
453*5c51f124SMoriah Waterland 			i = strcmp(pc, pent->path);
454*5c51f124SMoriah Waterland 
455*5c51f124SMoriah Waterland 			/* If equal we must compare the second piece. */
456*5c51f124SMoriah Waterland 			if (i == 0) {
457*5c51f124SMoriah Waterland 				q = p + 1;
458*5c51f124SMoriah Waterland 				i = strcmp(q, pent->ainfo.local);
459*5c51f124SMoriah Waterland 			}
460*5c51f124SMoriah Waterland 
461*5c51f124SMoriah Waterland 			/* Restore the link path. */
462*5c51f124SMoriah Waterland 			*p = '=';
463*5c51f124SMoriah Waterland 		}
464*5c51f124SMoriah Waterland 	} else {
465*5c51f124SMoriah Waterland 		i = strcmp(pc, pent->path);
466*5c51f124SMoriah Waterland 	}
467*5c51f124SMoriah Waterland 
468*5c51f124SMoriah Waterland 	return (i);
469*5c51f124SMoriah Waterland }
470*5c51f124SMoriah Waterland 
471*5c51f124SMoriah Waterland /*
472*5c51f124SMoriah Waterland  * -----------------------------------------------------------------------
473*5c51f124SMoriah Waterland  * Externally visible function.
474*5c51f124SMoriah Waterland  */
475*5c51f124SMoriah Waterland 
476*5c51f124SMoriah Waterland /*
477*5c51f124SMoriah Waterland  * special_contents_remove
478*5c51f124SMoriah Waterland  *
479*5c51f124SMoriah Waterland  * Given a set of entries to remove and an alternate root, this function
480*5c51f124SMoriah Waterland  * will do everything required to ensure that the entries are removed
481*5c51f124SMoriah Waterland  * from the contents file if they are listed in the special_contents
482*5c51f124SMoriah Waterland  * file.  The contents file will get changed only in the case that the
483*5c51f124SMoriah Waterland  * entire operation has succeeded.
484*5c51f124SMoriah Waterland  *
485*5c51f124SMoriah Waterland  *  ient	The number of entries.
486*5c51f124SMoriah Waterland  *  ppcfent	The entries to remove.
487*5c51f124SMoriah Waterland  *  pcroot	The alternate install root.  Could be NULL.  In this
488*5c51f124SMoriah Waterland  *		case, assume root is '/'
489*5c51f124SMoriah Waterland  *
490*5c51f124SMoriah Waterland  * Result: 0 on success, nonzero on failure.  If an error occurs, an
491*5c51f124SMoriah Waterland  *    error string will get output to standard error alerting the user.
492*5c51f124SMoriah Waterland  * Side effects: The contents file may change as a result of this call,
493*5c51f124SMoriah Waterland  *    such that lines in the in the file will be changed or removed.
494*5c51f124SMoriah Waterland  *    If the call fails, a t.contents file may be left behind.  This
495*5c51f124SMoriah Waterland  *    temporary file should be removed subsequently.
496*5c51f124SMoriah Waterland  */
497*5c51f124SMoriah Waterland int
special_contents_remove(int ient,struct cfent ** ppcfent,const char * pcroot)498*5c51f124SMoriah Waterland special_contents_remove(int ient, struct cfent **ppcfent, const char *pcroot)
499*5c51f124SMoriah Waterland {
500*5c51f124SMoriah Waterland 	int result = 0;		/* Assume we will succeed.  Return result. */
501*5c51f124SMoriah Waterland 	char **ppcSC = NULL;	/* The special contents rules, sorted. */
502*5c51f124SMoriah Waterland 	int i, j;		/* Indexes into contents & special contents */
503*5c51f124SMoriah Waterland 	FILE *fpi = NULL,	/* Input of contents file */
504*5c51f124SMoriah Waterland 	    *fpo = NULL;	/* Output to temp contents file */
505*5c51f124SMoriah Waterland 	char cpath[PATH_MAX],	/* Contents file path */
506*5c51f124SMoriah Waterland 	    tcpath[PATH_MAX];	/* Temp contents file path */
507*5c51f124SMoriah Waterland 	const char *pccontents = "var/sadm/install/contents";
508*5c51f124SMoriah Waterland 	const char *pctcontents = "var/sadm/install/t.contents";
509*5c51f124SMoriah Waterland 	char line[LINESZ];	/* Reads in and writes out contents lines. */
510*5c51f124SMoriah Waterland 	time_t t;		/* Used to create a timestamp comment. */
511*5c51f124SMoriah Waterland 	int max;		/* Max number of special contents entries. */
512*5c51f124SMoriah Waterland 	int *piIndex;		/* An index to ppcfents to remove from cfile */
513*5c51f124SMoriah Waterland 
514*5c51f124SMoriah Waterland 	cpath[0] = tcpath[0] = '\0';
515*5c51f124SMoriah Waterland 
516*5c51f124SMoriah Waterland 	if (ient == 0 || ppcfent == NULL || ppcfent[0] == NULL) {
517*5c51f124SMoriah Waterland 		goto remove_done;
518*5c51f124SMoriah Waterland 	}
519*5c51f124SMoriah Waterland 
520*5c51f124SMoriah Waterland 	if ((get_special_contents(pcroot, &ppcSC, &max)) != 0) {
521*5c51f124SMoriah Waterland 		result = 1;
522*5c51f124SMoriah Waterland 		goto remove_done;
523*5c51f124SMoriah Waterland 	}
524*5c51f124SMoriah Waterland 
525*5c51f124SMoriah Waterland 	/* Check if there are no special contents actions to take. */
526*5c51f124SMoriah Waterland 	if (ppcSC == NULL) {
527*5c51f124SMoriah Waterland 		goto remove_done;
528*5c51f124SMoriah Waterland 	}
529*5c51f124SMoriah Waterland 
530*5c51f124SMoriah Waterland 	if (pcroot == NULL) pcroot = "/";
531*5c51f124SMoriah Waterland 	if (pcroot[strlen(pcroot) - 1] == '/') {
532*5c51f124SMoriah Waterland 		if (snprintf(cpath, PATH_MAX, "%s%s", pcroot, pccontents)
533*5c51f124SMoriah Waterland 		    >= PATH_MAX ||
534*5c51f124SMoriah Waterland 		    snprintf(tcpath, PATH_MAX, "%s%s", pcroot, pctcontents)
535*5c51f124SMoriah Waterland 		    >= PATH_MAX) {
536*5c51f124SMoriah Waterland 			progerr(gettext(SPECIAL_INPUT));
537*5c51f124SMoriah Waterland 			result = -1;
538*5c51f124SMoriah Waterland 			goto remove_done;
539*5c51f124SMoriah Waterland 		}
540*5c51f124SMoriah Waterland 	} else {
541*5c51f124SMoriah Waterland 		if (snprintf(cpath, PATH_MAX, "%s/%s", pcroot, pccontents)
542*5c51f124SMoriah Waterland 		    >= PATH_MAX ||
543*5c51f124SMoriah Waterland 		    snprintf(tcpath, PATH_MAX, "%s/%s", pcroot, pctcontents)
544*5c51f124SMoriah Waterland 		    >= PATH_MAX) {
545*5c51f124SMoriah Waterland 			progerr(gettext(SPECIAL_INPUT));
546*5c51f124SMoriah Waterland 			result = -1;
547*5c51f124SMoriah Waterland 			goto remove_done;
548*5c51f124SMoriah Waterland 		}
549*5c51f124SMoriah Waterland 	}
550*5c51f124SMoriah Waterland 
551*5c51f124SMoriah Waterland 	/* Open the temporary contents file to write, contents to read. */
552*5c51f124SMoriah Waterland 	if (access(cpath, F_OK | R_OK) != 0) {
553*5c51f124SMoriah Waterland 		/*
554*5c51f124SMoriah Waterland 		 * This is not a problem since no contents means nothing
555*5c51f124SMoriah Waterland 		 * to remove due to special contents rules.
556*5c51f124SMoriah Waterland 		 */
557*5c51f124SMoriah Waterland 		result = 0;
558*5c51f124SMoriah Waterland 		cpath[0] = '\0'; /* This signals omission of 'rename cleanup' */
559*5c51f124SMoriah Waterland 		goto remove_done;
560*5c51f124SMoriah Waterland 	}
561*5c51f124SMoriah Waterland 
562*5c51f124SMoriah Waterland 	if (access(cpath, W_OK) != 0) {
563*5c51f124SMoriah Waterland 		/* can't write contents file, something is wrong. */
564*5c51f124SMoriah Waterland 		progerr(gettext(SPECIAL_ACCESS));
565*5c51f124SMoriah Waterland 		result = 1;
566*5c51f124SMoriah Waterland 		goto remove_done;
567*5c51f124SMoriah Waterland 
568*5c51f124SMoriah Waterland 	}
569*5c51f124SMoriah Waterland 
570*5c51f124SMoriah Waterland 	if ((fpi = fopen(cpath, "r")) == NULL) {
571*5c51f124SMoriah Waterland 		/* Given the access test above, this should not happen. */
572*5c51f124SMoriah Waterland 		progerr(gettext(SPECIAL_ACCESS));
573*5c51f124SMoriah Waterland 		result = 1;
574*5c51f124SMoriah Waterland 		goto remove_done;
575*5c51f124SMoriah Waterland 	}
576*5c51f124SMoriah Waterland 
577*5c51f124SMoriah Waterland 	if ((fpo = fopen(tcpath, "w")) == NULL) {
578*5c51f124SMoriah Waterland 		/* open t.contents failed */
579*5c51f124SMoriah Waterland 		progerr(gettext(SPECIAL_ACCESS));
580*5c51f124SMoriah Waterland 		result = 1;
581*5c51f124SMoriah Waterland 		goto remove_done;
582*5c51f124SMoriah Waterland 	}
583*5c51f124SMoriah Waterland 
584*5c51f124SMoriah Waterland 	if (generate_special_contents_rules(ient, ppcfent, ppcSC, max, &piIndex)
585*5c51f124SMoriah Waterland 	    != 0) {
586*5c51f124SMoriah Waterland 		result = 1;
587*5c51f124SMoriah Waterland 		goto remove_done;
588*5c51f124SMoriah Waterland 	}
589*5c51f124SMoriah Waterland 
590*5c51f124SMoriah Waterland 	/*
591*5c51f124SMoriah Waterland 	 * Copy contents to t.contents unless there is an entry in
592*5c51f124SMoriah Waterland 	 * the ppcfent array which corresponds to an index set to 1.
593*5c51f124SMoriah Waterland 	 *
594*5c51f124SMoriah Waterland 	 * These items are the removed package contents which matche an
595*5c51f124SMoriah Waterland 	 * entry in ppcSC (the special_contents rules).
596*5c51f124SMoriah Waterland 	 *
597*5c51f124SMoriah Waterland 	 * Since both the contents and rules are sorted, we can
598*5c51f124SMoriah Waterland 	 * make a single efficient pass.
599*5c51f124SMoriah Waterland 	 */
600*5c51f124SMoriah Waterland 	(void) memset(line, 0, LINESZ);
601*5c51f124SMoriah Waterland 
602*5c51f124SMoriah Waterland 	for (i = 0, j = 0; fgets(line, LINESZ, fpi) != NULL; ) {
603*5c51f124SMoriah Waterland 
604*5c51f124SMoriah Waterland 		char *pcpath = NULL;
605*5c51f124SMoriah Waterland 
606*5c51f124SMoriah Waterland 		/*
607*5c51f124SMoriah Waterland 		 * Note:  This could be done better:  We should figure out
608*5c51f124SMoriah Waterland 		 * which are the last 2 lines and only trim those off.
609*5c51f124SMoriah Waterland 		 * This will suffice to do this and will only be done as
610*5c51f124SMoriah Waterland 		 * part of special_contents handling.
611*5c51f124SMoriah Waterland 		 */
612*5c51f124SMoriah Waterland 		if (line[0] == '#')
613*5c51f124SMoriah Waterland 			continue; /* Do not copy the final 2 comment lines */
614*5c51f124SMoriah Waterland 
615*5c51f124SMoriah Waterland 		pcpath = get_path(line);
616*5c51f124SMoriah Waterland 
617*5c51f124SMoriah Waterland 		if (pcpath != NULL && i < ient) {
618*5c51f124SMoriah Waterland 			int k;
619*5c51f124SMoriah Waterland 			while (piIndex[i] == 0)
620*5c51f124SMoriah Waterland 				i++;
621*5c51f124SMoriah Waterland 
622*5c51f124SMoriah Waterland 			if (i < ient)
623*5c51f124SMoriah Waterland 				k = pathcmp(pcpath, ppcfent[i]);
624*5c51f124SMoriah Waterland 
625*5c51f124SMoriah Waterland 			if (k < 0 || i >= ient) {
626*5c51f124SMoriah Waterland 				/* Just copy contents -> t.contents */
627*5c51f124SMoriah Waterland 				/*EMPTY*/
628*5c51f124SMoriah Waterland 			} else if (k == 0) {
629*5c51f124SMoriah Waterland 				/* We have a match.  Do not copy the content. */
630*5c51f124SMoriah Waterland 				i++;
631*5c51f124SMoriah Waterland 				free(pcpath);
632*5c51f124SMoriah Waterland 				(void) memset(line, 0, LINESZ);
633*5c51f124SMoriah Waterland 				continue;
634*5c51f124SMoriah Waterland 			} else while (i < ient) {
635*5c51f124SMoriah Waterland 
636*5c51f124SMoriah Waterland 				/*
637*5c51f124SMoriah Waterland 				 * This is a complex case:  The content
638*5c51f124SMoriah Waterland 				 * entry is further along alphabetically
639*5c51f124SMoriah Waterland 				 * than the rule.  Skip over all rules which
640*5c51f124SMoriah Waterland 				 * apply until we come to a rule which is
641*5c51f124SMoriah Waterland 				 * greater than the current entry, or equal
642*5c51f124SMoriah Waterland 				 * to it.  If equal, do not copy, otherwise
643*5c51f124SMoriah Waterland 				 * do copy the entry.
644*5c51f124SMoriah Waterland 				 */
645*5c51f124SMoriah Waterland 				if (piIndex[i] == 0) {
646*5c51f124SMoriah Waterland 					i++;
647*5c51f124SMoriah Waterland 					continue;
648*5c51f124SMoriah Waterland 				} else if ((k = pathcmp(pcpath, ppcfent[i]))
649*5c51f124SMoriah Waterland 				    >= 0) {
650*5c51f124SMoriah Waterland 					i++;
651*5c51f124SMoriah Waterland 					if (k == 0) {
652*5c51f124SMoriah Waterland 						free(pcpath);
653*5c51f124SMoriah Waterland 						(void) memset(line, 0, LINESZ);
654*5c51f124SMoriah Waterland 						break;
655*5c51f124SMoriah Waterland 					}
656*5c51f124SMoriah Waterland 				} else {
657*5c51f124SMoriah Waterland 					/* path < rule, end special case */
658*5c51f124SMoriah Waterland 					break;
659*5c51f124SMoriah Waterland 				}
660*5c51f124SMoriah Waterland 			}
661*5c51f124SMoriah Waterland 
662*5c51f124SMoriah Waterland 			/*
663*5c51f124SMoriah Waterland 			 * Avoid copying the old content when path == rule
664*5c51f124SMoriah Waterland 			 * This occurs when the complex case ends on a match.
665*5c51f124SMoriah Waterland 			 */
666*5c51f124SMoriah Waterland 			if (k == 0)
667*5c51f124SMoriah Waterland 				continue;
668*5c51f124SMoriah Waterland 		}
669*5c51f124SMoriah Waterland 
670*5c51f124SMoriah Waterland 		if (fprintf(fpo, "%s", line) < 0) {
671*5c51f124SMoriah Waterland 			/* Failing to write output would be catastrophic. */
672*5c51f124SMoriah Waterland 			progerr(gettext(SPECIAL_ACCESS));
673*5c51f124SMoriah Waterland 			result = 1;
674*5c51f124SMoriah Waterland 			break;
675*5c51f124SMoriah Waterland 		}
676*5c51f124SMoriah Waterland 		(void) memset(line, 0, LINESZ);
677*5c51f124SMoriah Waterland 	}
678*5c51f124SMoriah Waterland 
679*5c51f124SMoriah Waterland 	t = time(NULL);
680*5c51f124SMoriah Waterland 	(void) fprintf(fpo, "# Last modified by pkgremove\n");
681*5c51f124SMoriah Waterland 	(void) fprintf(fpo, "# %s", ctime(&t));
682*5c51f124SMoriah Waterland 
683*5c51f124SMoriah Waterland remove_done:
684*5c51f124SMoriah Waterland 	free_special_contents(&ppcSC, max);
685*5c51f124SMoriah Waterland 
686*5c51f124SMoriah Waterland 	if (fpi != NULL)
687*5c51f124SMoriah Waterland 		(void) fclose(fpi);
688*5c51f124SMoriah Waterland 
689*5c51f124SMoriah Waterland 	if (fpo != NULL)
690*5c51f124SMoriah Waterland 		(void) fclose(fpo);
691*5c51f124SMoriah Waterland 
692*5c51f124SMoriah Waterland 	if (result == 0) {
693*5c51f124SMoriah Waterland 		if (tcpath[0] != '\0' && cpath[0] != '\0' &&
694*5c51f124SMoriah Waterland 		    rename(tcpath, cpath) != 0) {
695*5c51f124SMoriah Waterland 			progerr(gettext(SPECIAL_ACCESS));
696*5c51f124SMoriah Waterland 			result = 1;
697*5c51f124SMoriah Waterland 		}
698*5c51f124SMoriah Waterland 	} else {
699*5c51f124SMoriah Waterland 		if (tcpath[0] != '\0' && remove(tcpath) != 0) {
700*5c51f124SMoriah Waterland 			/*
701*5c51f124SMoriah Waterland 			 * Do not output a diagnostic message.  This condition
702*5c51f124SMoriah Waterland 			 * occurs only when we are unable to clean up after
703*5c51f124SMoriah Waterland 			 * a failure.  A temporary file will linger.
704*5c51f124SMoriah Waterland 			 */
705*5c51f124SMoriah Waterland 			result = 1;
706*5c51f124SMoriah Waterland 		}
707*5c51f124SMoriah Waterland 	}
708*5c51f124SMoriah Waterland 
709*5c51f124SMoriah Waterland 	return (result);
710*5c51f124SMoriah Waterland }
711