srchcfile.c revision 9781:ccf49524d5dc
1193326Sed/*
2193326Sed * CDDL HEADER START
3193326Sed *
4193326Sed * The contents of this file are subject to the terms of the
5193326Sed * Common Development and Distribution License (the "License").
6193326Sed * You may not use this file except in compliance with the License.
7193326Sed *
8193326Sed * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9239462Sdim * or http://www.opensolaris.org/os/licensing.
10239462Sdim * See the License for the specific language governing permissions
11239462Sdim * and limitations under the License.
12239462Sdim *
13193326Sed * When distributing Covered Code, include this CDDL HEADER in each
14193326Sed * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15193326Sed * If applicable, add the following below this CDDL HEADER, with the
16193326Sed * fields enclosed by brackets "[]" replaced with your own identifying
17193326Sed * information: Portions Copyright [yyyy] [name of copyright owner]
18239462Sdim *
19193326Sed * CDDL HEADER END
20193326Sed */
21193326Sed
22193326Sed/*
23193326Sed * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24193326Sed * Use is subject to license terms.
25193326Sed */
26193326Sed
27193326Sed/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28193326Sed/* All Rights Reserved */
29193326Sed
30239462Sdim
31193326Sed
32198092Srdivacky#include <stdio.h>
33239462Sdim#include <limits.h>
34193326Sed#include <stdlib.h>
35198092Srdivacky#include <string.h>
36239462Sdim#include <strings.h>
37239462Sdim#include <ctype.h>
38193326Sed#include <sys/types.h>
39198092Srdivacky#include <libintl.h>
40239462Sdim#include "pkglib.h"
41193326Sed#include "pkgstrct.h"
42198092Srdivacky#include "pkglocale.h"
43239462Sdim#include "pkglibmsgs.h"
44239462Sdim
45239462Sdim/*
46239462Sdim * Forward declarations
47193326Sed */
48198092Srdivacky
49193326Sedstatic void	findend(char **cp);
50193326Sedstatic int	getend(char **cp);
51193326Sedstatic int	getstr(char **cp, int n, char *str, int separator[]);
52193326Sed
53193326Sed/* from gpkgmap.c */
54193326Sedint	getnumvfp(char **cp, int base, long *d, long bad);
55193326Sedint	getlnumvfp(char **cp, int base, fsblkcnt_t *d, long bad);
56193326Sed
57193326Sed/*
58193326Sed * Module globals
59193326Sed */
60193326Sed
61193326Sedstatic char	lpath[PATH_MAX];	/* for ept->path */
62193326Sedstatic char	mylocal[PATH_MAX];	/* for ept->ainfo.local */
63193326Sedstatic int	decisionTableInit = 0;
64193326Sed
65193326Sed/*
66193326Sed * These arrays must be indexable by an unsigned char.
67193326Sed */
68193326Sed
69193326Sedstatic int	ISPKGPATHSEP[UCHAR_MAX+1];
70193326Sedstatic int	ISWORDSEP[UCHAR_MAX+1];
71193326Sedstatic int	ISPKGNAMESEP[UCHAR_MAX+1];
72193326Sed
73193326Sed/*
74193326Sed * Name:	WRITEDATA
75198092Srdivacky * Description:	write out data to VFP_T given start and end pointers
76239462Sdim * Arguments:	VFP - (VFP_T *) - [RO, *RW]
77193326Sed *			Contents file VFP to narrow search on
78239462Sdim *		FIRSTPOS - (char *) - [RO, *RO]
79239462Sdim *			Pointer to first byte to write out
80239462Sdim *		LASTPOS - (char *) - [RO, *RO]
81193326Sed *			Pointer to last byte to write out
82193326Sed */
83193326Sed
84193326Sed#define	WRITEDATA(VFP, FIRSTPOS, LASTPOS)				\
85193326Sed	{								\
86198092Srdivacky		ssize_t XXlenXX;					\
87239462Sdim		/* compute number of bytes skipped */			\
88239462Sdim		XXlenXX = (ptrdiff_t)(LASTPOS) - (ptrdiff_t)(FIRSTPOS);	\
89239462Sdim		/* write the bytes out */				\
90193326Sed		vfpPutBytes((VFP), (FIRSTPOS), XXlenXX);		\
91193326Sed	}
92193326Sed
93198092Srdivacky/*
94193326Sed * Name:	COPYPATH
95193326Sed * Description:	copy path limiting size to destination capacity
96193326Sed * Arguments:	DEST - (char []) - [RW]
97193326Sed *		SRC - (char *) - [RO, *RO]
98193326Sed *			Pointer to first byte of path to copy
99198092Srdivacky *		LEN - (int) - [RO]
100193326Sed *			Number of bytes to copy
101198092Srdivacky */
102226633Sdim
103193326Sed#define	COPYPATH(DEST, SRC, LEN)					\
104193326Sed	{								\
105193326Sed		/* assure return path does not overflow */		\
106193326Sed		if ((LEN) > sizeof ((DEST))) {				\
107193326Sed			(LEN) = sizeof ((DEST))-1;			\
108193326Sed		}							\
109239462Sdim		/* copy return path to local storage */			\
110193326Sed		(void) memcpy((DEST), (SRC), (LEN));			\
111239462Sdim		(DEST)[(LEN)] = '\0';					\
112193326Sed	}
113193326Sed
114193326Sed/*
115198092Srdivacky * Name:	narrowSearch
116239462Sdim * Description:	narrow the search location for a specified path
117239462Sdim *		The contents and package map files are always sorted by path.
118239462Sdim *		This function is given a target path to search for given the
119239462Sdim *		current location in a contents file. It is assured that the
120193326Sed *		target path has not been searched for yet in the contents file
121193326Sed *		so the current location in the contents file is guaranteed to
122239462Sdim *		be less than the location of the target path (if present).
123193326Sed *		Given this employ a binary search to speed up the search for
124193326Sed *		the path nearest to a specified target path.
125193326Sed * Arguments:	a_vfp - (VFP_T *) - [RO, *RW]
126193326Sed *			Contents file VFP to narrow search on
127193326Sed *		a_path - (char *) - [RO, *RO]
128239462Sdim *			Pointer to path to search for
129193326Sed *		a_pathLen - (size_t) - [RO]
130193326Sed *			Length of string (a_path)
131193326Sed * Returns:	char *	- pointer to first byte of entry in contents file that
132193326Sed *			is guaranteed to be the closest match to the specified
133193326Sed *			a_path without being "greater than" the path.
134 *			== (char *)NULL if no entry found
135 */
136
137static char *
138narrowSearch(VFP_T *a_vfp, char *a_path, size_t a_pathLen)
139{
140	char	*phigh;
141	char	*plow;
142	char	*pmid;
143	int	n;
144	size_t	plen;
145
146	/* if no path to compare, start at beginning */
147
148	if ((a_path == (char *)NULL) || (*a_path == '\0')) {
149		return ((char *)NULL);
150	}
151
152	/* if the contents file is empty, resort to sequential search */
153
154	if (vfpGetBytesRemaining(a_vfp) <= 1) {
155		return ((char *)NULL);
156	}
157
158	/*
159	 * test against first path - if the path specified is less than the
160	 * first path in the contents file, then the path can be inserted
161	 * before the first entry in the contents file.
162	 */
163
164	/* locate start of first line */
165
166	plow = vfpGetCurrCharPtr(a_vfp);
167	pmid = plow;
168
169	/* if first path not absolute, resort to sequential search */
170
171	if (*pmid != '/') {
172		return ((char *)NULL);
173	}
174
175	/* find end of path */
176
177	while (ISPKGPATHSEP[(int)*pmid] == 0) {
178		pmid++;
179	}
180
181	/* determine length of path */
182
183	plen = (ptrdiff_t)pmid - (ptrdiff_t)plow;
184
185	/* compare target path with current path */
186
187	n = strncmp(a_path, plow, plen);
188	if (n == 0) {
189		/* if lengths same exact match return position found */
190		if (a_pathLen == plen) {
191			return (plow);
192		}
193		/* not exact match - a_path > pm */
194		n = a_pathLen;
195	}
196
197	/* return if target is less than or equal to first entry */
198
199	if (n <= 0) {
200		return (plow);
201	}
202
203	/*
204	 * test against last path - if the path specified is greater than the
205	 * last path in the contents file, then the path can be appended after
206	 * the last entry in the contents file.
207	 */
208
209	/* locate start of last line */
210
211	plow = vfpGetCurrCharPtr(a_vfp);
212	pmid = vfpGetLastCharPtr(a_vfp);
213
214	while ((pmid > plow) && (!((pmid[0] == '/') && (pmid[-1] == '\n')))) {
215		pmid--;
216	}
217
218	/* if absolute path, do comparison */
219
220	if ((pmid > plow) && (*pmid == '/')) {
221		plow = pmid;
222
223		/* find end of path */
224
225		while (ISPKGPATHSEP[(int)*pmid] == 0) {
226			pmid++;
227		}
228
229		/* determine length of path */
230
231		plen = (ptrdiff_t)pmid - (ptrdiff_t)plow;
232
233		/* compare target path with current path */
234
235		n = strncmp(a_path, plow, plen);
236		if (n == 0) {
237			/* if lengths same exact match return position found */
238			if (a_pathLen == plen) {
239				return (plow);
240			}
241			/* not exact match - a_path > pm */
242			n = a_pathLen;
243		}
244
245		/* return if target is greater than or equal to entry */
246
247		if (n >= 0) {
248			return (plow);
249		}
250	}
251	/*
252	 * firstPath < targetpath < lastPath:
253	 * binary search looking for closest "less than" match
254	 */
255
256	plow = vfpGetCurrCharPtr(a_vfp);
257	phigh = vfpGetLastCharPtr(a_vfp);
258
259	for (;;) {
260		char	*pm;
261
262		/* determine number of bytes left in search area */
263
264		plen = (ptrdiff_t)phigh - (ptrdiff_t)plow;
265
266		/* calculate mid point between current low and high points */
267
268		pmid = plow + (plen >> 1);
269
270		/* backup and find first "\n/" -or- start of buffer */
271
272		while ((pmid > plow) &&
273				(!((pmid[0] == '/') && (pmid[-1] == '\n')))) {
274			pmid--;
275		}
276
277		/* return lowest line found if current line not past that */
278
279		if (pmid <= plow) {
280			return (plow);
281		}
282
283		/* remember start of this line */
284
285		pm = pmid;
286
287		/* find end of path */
288
289		while (ISPKGPATHSEP[(int)*pmid] == 0) {
290			pmid++;
291		}
292
293		/* determine length of path */
294
295		plen = (ptrdiff_t)pmid - (ptrdiff_t)pm;
296
297		/* compare target path with current path */
298
299		n = strncmp(a_path, pm, plen);
300
301		if (n == 0) {
302			/* if lengths same exact match return position found */
303			if (a_pathLen == plen) {
304				return (pm);
305			}
306			/* not exact match - a_path > pm */
307			n = a_pathLen;
308		}
309
310
311		/* not exact match - determine which watermark to split */
312
313		if (n > 0) {	/* a_path > pm */
314			plow = pm;
315		} else {	/* a_path < pm */
316			phigh = pm;
317		}
318	}
319	/*NOTREACHED*/
320}
321
322/*
323 * Name:	srchcfile
324 * Description:	search contents file looking for closest match to entry,
325 *		creating a new contents file if output contents file specified
326 * Arguments:	ept - (struct cfent *) - [RO, *RW]
327 *			- contents file entry, describing last item found
328 *		path - (char *) - [RO, *RO]
329 *			- path to search for in contents file
330 *			- If path is "*", then the next entry is returned;
331 *				the next entry always matches this path
332 *			- If the path is (char *)NULL or "", then all remaining
333 *				entries are processed and copied out to the
334 *				file specified by cfTmpVFp
335 *		cfVfp - (VFP_T *) - [RO, *RW]
336 *			- VFP_T open on contents file to search
337 *		cfTmpVfp - (VFP_T *) - [RO, *RW]
338 *			- VFP_T open on temporary contents file to populate
339 * Returns:	int
340 *		< 0 - error occurred
341 *			- Use getErrstr to retrieve character-string describing
342 *			  the reason for failure
343 *		== 0 - no match found
344 *			- specified path not in the contents file
345 *			- all contents of cfVfp copied to cfTmpVfp
346 *			- current character of cfVfp is at end of file
347 *		== 1 - exact match found
348 *			- specified path found in contents file
349 *			- contents of cfVfp up to entry found copied to cfTmpVfp
350 *			- current character of cfVfp is first character of
351 *				entry found
352 *			- this value is always returned if path is "*" and the
353 *			  next entry is returned - -1 is returned when no more
354 *			  entries are left to process
355 *		== 2 - entry found which is GREATER than path specified
356 *			- specified path would fit BEFORE entry found
357 *			- contents of cfVfp up to entry found copied to cfTmpVfp
358 *			- current character of cfVfp is first character of
359 *				entry found
360 * Side Effects:
361 *		- The ept structure supplied is filled in with a description of
362 *		  the item that caused the search to terminate, except in the
363 *		  case of '0' in which case the contents of 'ept' is undefined.
364 *		- NOTE: the ept->path item points to a path that is statically
365 *		  allocated and will be overwritten on the next call.
366 *		- NOTE: the ept->ainfo.local item points to a path that is
367 *		  statically allocated and will be overwritten on the next call.
368 */
369
370int
371srchcfile(struct cfent *ept, char *path, VFP_T *cfVfp, VFP_T *cfTmpVfp)
372{
373	char		*cpath_start = (char *)NULL;
374	char		*firstPos = vfpGetCurrCharPtr(cfVfp);
375	char		*lastPos = NULL;
376	char		*pos;
377	char		classname[CLSSIZ+1];
378	char		pkgname[PKGSIZ+1];
379	int		anypath = 0;
380	int		c;
381	int		dataSkipped = 0;
382	int		n;
383	int		rdpath;
384	size_t		cpath_len = 0;
385	size_t		pathLength;
386	struct pinfo	*lastpinfo;
387	struct pinfo	*pinfo;
388
389	/*
390	 * this code does not use nested subroutines because execution time
391	 * of this routine is especially critical to installation and upgrade
392	 */
393
394	/* initialize local variables */
395
396	setErrstr(NULL);	/* no error message currently cached */
397	pathLength = (path == (char *)NULL ? 0 : strlen(path));
398	lpath[0] = '\0';
399	lpath[sizeof (lpath)-1] = '\0';
400
401	/* initialize ept structure values */
402
403	(void) strlcpy(ept->ainfo.group, BADGROUP, sizeof (ept->ainfo.group));
404	(void) strlcpy(ept->ainfo.owner, BADOWNER, sizeof (ept->ainfo.owner));
405	(void) strlcpy(ept->pkg_class, BADCLASS,  sizeof (ept->pkg_class));
406	ept->ainfo.local = (char *)NULL;
407	ept->ainfo.mode = BADMODE;
408	ept->cinfo.cksum = BADCONT;
409	ept->cinfo.modtime = BADCONT;
410	ept->cinfo.size = (fsblkcnt_t)BADCONT;
411	ept->ftype = BADFTYPE;
412	ept->npkgs = 0;
413	ept->path = (char *)NULL;
414	ept->pinfo = (struct pinfo *)NULL;
415	ept->pkg_class_idx = -1;
416	ept->volno = 0;
417
418	/*
419	 * populate decision tables that implement fast character checking;
420	 * this is much faster than the equivalent strpbrk() call or a
421	 * while() loop checking for the characters. It is only faster if
422	 * there are at least 3 characters to scan for - when checking for
423	 * one or two characters (such as '\n' or '\0') its faster to do
424	 * a simple while() loop.
425	 */
426
427	if (decisionTableInit == 0) {
428		/*
429		 * any chars listed stop scan;
430		 * scan stops on first byte found that is set to '1' below
431		 */
432
433		/*
434		 * Separators for path names, normal space and =
435		 * for linked filenames
436		 */
437		bzero(ISPKGPATHSEP, sizeof (ISPKGPATHSEP));
438		ISPKGPATHSEP['='] = 1;		/* = */
439		ISPKGPATHSEP[' '] = 1;		/* space */
440		ISPKGPATHSEP['\t'] = 1;		/* horizontal-tab */
441		ISPKGPATHSEP['\n'] = 1;		/* new-line */
442		ISPKGPATHSEP['\0'] = 1;		/* NULL character */
443
444		/*
445		 * Separators for normal words
446		 */
447		bzero(ISWORDSEP, sizeof (ISWORDSEP));
448		ISWORDSEP[' '] = 1;
449		ISWORDSEP['\t'] = 1;
450		ISWORDSEP['\n'] = 1;
451		ISWORDSEP['\0'] = 1;
452
453		/*
454		 * Separators for list of packages, includes \\ for
455		 * alternate ftype and : for classname
456		 */
457		bzero(ISPKGNAMESEP, sizeof (ISPKGNAMESEP));
458		ISPKGNAMESEP[' '] = 1;
459		ISPKGNAMESEP['\t'] = 1;
460		ISPKGNAMESEP['\n'] = 1;
461		ISPKGNAMESEP[':'] = 1;
462		ISPKGNAMESEP['\\'] = 1;
463		ISPKGNAMESEP['\0'] = 1;
464
465		decisionTableInit = 1;
466	}
467
468	/* if no bytes in contents file, return 0 */
469
470	if (vfpGetBytesRemaining(cfVfp) <= 1) {
471		return (0);
472	}
473
474	/* if the path to scan for is empty, act like no path was specified */
475
476	if ((path != (char *)NULL) && (*path == '\0')) {
477		path = (char *)NULL;
478	}
479
480	/*
481	 * if path to search for is "*", then we will return the first path
482	 * we encounter as a match, otherwise we return an error
483	 */
484
485	if ((path != (char *)NULL) && (path[0] != '/')) {
486		if (strcmp(path, "*") != 0) {
487			setErrstr(pkg_gt(ERR_ILLEGAL_SEARCH_PATH));
488			return (-1);
489		}
490		anypath = 1;
491	}
492
493	/* attempt to narrow down the search for the specified path */
494
495	if (anypath == 0) {
496		char	*np;
497
498		np = narrowSearch(cfVfp, path, pathLength);
499		if (np != (char *)NULL) {
500			dataSkipped = 1;
501			lastPos = np;
502			vfpSetCurrCharPtr(cfVfp, np);
503		}
504	}
505
506	/*
507	 * If the path to search for in the source contents file is NULL, then
508	 * this is a request to scan to the end of the source contents file. If
509	 * there is a temporary contents file to copy entries to, all that needs
510	 * to be done is to copy the data remaining from the current location in
511	 * the source contents file to the end of the temporary contents file.
512	 * if there is no temporary contents file to copy to, then all that
513	 * needs to be done is to seek to the end of the source contents file.
514	 */
515
516	if ((anypath == 0) && (path == (char *)NULL)) {
517		if (cfTmpVfp != (VFP_T *)NULL) {
518			if (vfpGetBytesRemaining(cfVfp) > 0) {
519				WRITEDATA(cfTmpVfp, firstPos,
520					vfpGetLastCharPtr(cfVfp)+1);
521			}
522			*vfpGetLastCharPtr(cfTmpVfp) = '\0';
523		}
524		vfpSeekToEnd(cfVfp);
525		return (0);
526	}
527
528	/*
529	 * *********************************************************************
530	 * main loop processing entries from the contents file looking for
531	 * the specified path
532	 * *********************************************************************
533	 */
534
535	for (;;) {
536		char	*p;
537
538		/* not reading old style entry */
539
540		rdpath = 0;
541
542		/* determine first character of the next entry */
543
544		if (vfpGetBytesRemaining(cfVfp) <= 0) {
545			/* no bytes in contents file current char is NULL */
546
547			c = '\0';
548		} else {
549			/* grab path from first entry */
550
551			c = vfpGetcNoInc(cfVfp);
552		}
553
554		/* save current position in file */
555
556		pos = vfpGetCurrCharPtr(cfVfp);
557
558		/*
559		 * =============================================================
560		 * at the first character of the next entry in the contents file
561		 * if not absolute path check for exceptions and old style entry
562		 * --> if end of contents file write out skipped data and return
563		 * --> if comment character skip to end of line and restart loop
564		 * --> else process "old style entry: ftype class path"
565		 * =============================================================
566		 */
567
568		if (c != '/') {
569			/* if NULL character then end of contents file found */
570
571			if (c == '\0') {
572				/* write out skipped data before returning */
573				if (dataSkipped &&
574						(cfTmpVfp != (VFP_T *)NULL)) {
575					WRITEDATA(cfTmpVfp, firstPos, lastPos);
576					*vfpGetLastCharPtr(cfTmpVfp) = '\0';
577				}
578
579				return (0); /* no more entries */
580			}
581
582			/* ignore lines that begin with #, : or a "space" */
583
584			if ((isspace(c) != 0) || (c == '#') || (c == ':')) {
585				/* line is a comment */
586				findend(&vfpGetCurrCharPtr(cfVfp));
587				continue;
588			}
589
590			/*
591			 * old style entry - format is:
592			 *	ftype class path
593			 * set ept->ftype to the type
594			 * set ept->class to the class
595			 * set ept->path to point to lpath
596			 * set cpath_start/cpath_len to point to the file name
597			 * set rdpath to '1' to indicate old style entry parsed
598			 */
599
600			while (isspace((c = vfpGetc(cfVfp))))
601				;
602
603			switch (c) {
604			case '?': case 'f': case 'v': case 'e': case 'l':
605			case 's': case 'p': case 'c': case 'b': case 'd':
606			case 'x':
607				/* save ftype */
608				ept->ftype = (char)c;
609
610				/* save class */
611				if (getstr(&vfpGetCurrCharPtr(cfVfp), CLSSIZ,
612						ept->pkg_class, ISWORDSEP)) {
613					setErrstr(ERR_CANNOT_READ_CLASS_TOKEN);
614					findend(&vfpGetCurrCharPtr(cfVfp));
615					return (-1);
616				}
617
618				/*
619				 * locate file name up to "=", set cpath_start
620				 * and cpath_len to point to the file name
621				 */
622				cpath_start = vfpGetCurrCharPtr(cfVfp);
623				p = vfpGetCurrCharPtr(cfVfp);
624
625				/*
626				 * skip past all bytes until first '= \t\n\0':
627				 */
628				while (ISPKGPATHSEP[(int)*p] == 0) {
629					p++;
630				}
631
632				cpath_len = vfpGetCurrPtrDelta(cfVfp, p);
633
634				/*
635				 * if the path is zero bytes, line is corrupted
636				 */
637
638				if (cpath_len < 1) {
639					setErrstr(ERR_CANNOT_READ_PATHNAME_FLD);
640					findend(&vfpGetCurrCharPtr(cfVfp));
641					return (-1);
642				}
643
644				vfpIncCurrPtrBy(cfVfp, cpath_len);
645
646				/* set path to point to local path cache */
647				ept->path = lpath;
648
649				/* set flag indicating path already parsed */
650				rdpath = 1;
651				break;
652
653			case '\0':
654				/* end of line before new-line seen */
655				vfpDecCurrPtr(cfVfp);
656				setErrstr(ERR_INCOMPLETE_ENTRY);
657				return (-1);
658
659			case '0': case '1': case '2': case '3': case '4':
660			case '5': case '6': case '7': case '8': case '9':
661				/* volume number seen */
662				setErrstr(ERR_VOLUMENO_UNEXPECTED);
663				findend(&vfpGetCurrCharPtr(cfVfp));
664				return (-1);
665
666			case 'i':
667				/* type i files are not cataloged */
668				setErrstr(ERR_FTYPE_I_UNEXPECTED);
669				findend(&vfpGetCurrCharPtr(cfVfp));
670				return (-1);
671
672			default:
673				/* unknown ftype */
674				setErrstr(ERR_UNKNOWN_FTYPE);
675				findend(&vfpGetCurrCharPtr(cfVfp));
676				return (-1);
677			}
678		} else {
679			/*
680			 * current entry DOES start with absolute path
681			 * set ept->path to point to lpath
682			 * set cpath_start/cpath_len to point to the file name
683			 */
684		/* copy first token into path element of passed structure */
685
686			cpath_start = vfpGetCurrCharPtr(cfVfp);
687
688			p = cpath_start;
689
690			/*
691			 * skip past all bytes until first from '= \t\n\0':
692			 */
693
694			while (ISPKGPATHSEP[(int)*p] == 0) {
695				p++;
696			}
697
698			cpath_len = vfpGetCurrPtrDelta(cfVfp, p);
699
700			vfpIncCurrPtrBy(cfVfp, cpath_len);
701
702			if (vfpGetcNoInc(cfVfp) == '\0') {
703				setErrstr(ERR_INCOMPLETE_ENTRY);
704				findend(&vfpGetCurrCharPtr(cfVfp));
705				return (-1);
706			}
707
708			ept->path = lpath;
709		}
710
711		/*
712		 * =============================================================
713		 * if absolute path then the path is collected and we are at the
714		 * first byte following the absolute path name;
715		 * if not an absolute path then an old style entry, ept has been
716		 * filled with the type and class and path name.
717		 * determine if we have read the pathname which identifies
718		 * the entry we are searching for
719		 * =============================================================
720		 */
721
722		if (anypath != 0) {
723			n = 0;	/* next entry is "equal to" */
724		} else if (path == (char *)NULL) {
725			n = 1;	/* next entry is "greater than" */
726		} else {
727			n = strncmp(path, cpath_start, cpath_len);
728			if ((n == 0) && (cpath_len != pathLength)) {
729				n = cpath_len;
730			}
731		}
732
733		/* get first character following the end of the path */
734
735		c = vfpGetc(cfVfp);
736
737		/*
738		 * if an exact match, always parse out the local path
739		 */
740
741		if (n == 0) {
742			/*
743			 * we want to return information about this path in
744			 * the structure provided, so parse any local path
745			 * and jump to code which parses rest of the input line
746			 */
747			if (c == '=') {
748				/* parse local path specification */
749				if (getstr(&vfpGetCurrCharPtr(cfVfp), PATH_MAX,
750						mylocal, ISWORDSEP)) {
751
752					/* copy path found to 'lpath' */
753					COPYPATH(lpath, cpath_start, cpath_len);
754
755					setErrstr(ERR_CANNOT_READ_LL_PATH);
756					findend(&vfpGetCurrCharPtr(cfVfp));
757					return (-1);
758				}
759				ept->ainfo.local = mylocal;
760			}
761		}
762
763		/*
764		 * if an exact match and processing a new style entry, read the
765		 * remaining information from the new style entry - if this is
766		 * an old style entry (rdpath != 0) then the existing info has
767		 * already been processed as it exists before the pathname and
768		 * not after like a new style entry
769		 */
770
771		if (n == 0 && rdpath == 0) {
772			while (isspace((c = vfpGetc(cfVfp))))
773				;
774
775			switch (c) {
776			case '?': case 'f': case 'v': case 'e': case 'l':
777			case 's': case 'p': case 'c': case 'b': case 'd':
778			case 'x':
779				/* save ftype */
780				ept->ftype = (char)c;
781
782				/* save class */
783				if (getstr(&vfpGetCurrCharPtr(cfVfp), CLSSIZ,
784						ept->pkg_class, ISWORDSEP)) {
785
786					/* copy path found to 'lpath' */
787					COPYPATH(lpath, cpath_start, cpath_len);
788
789					setErrstr(ERR_CANNOT_READ_CLASS_TOKEN);
790					findend(&vfpGetCurrCharPtr(cfVfp));
791					return (-1);
792				}
793				break; /* we already read the pathname */
794
795			case '\0':
796				/* end of line before new-line seen */
797				vfpDecCurrPtr(cfVfp);
798
799				/* copy path found to 'lpath' */
800				COPYPATH(lpath, cpath_start, cpath_len);
801
802				setErrstr(ERR_INCOMPLETE_ENTRY);
803				return (-1);
804
805			case '0': case '1': case '2': case '3': case '4':
806			case '5': case '6': case '7': case '8': case '9':
807
808				/* copy path found to 'lpath' */
809				COPYPATH(lpath, cpath_start, cpath_len);
810
811				setErrstr(ERR_VOLUMENO_UNEXPECTED);
812				findend(&vfpGetCurrCharPtr(cfVfp));
813				return (-1);
814
815			case 'i':
816
817				/* copy path found to 'lpath' */
818				COPYPATH(lpath, cpath_start, cpath_len);
819
820				setErrstr(ERR_FTYPE_I_UNEXPECTED);
821				findend(&vfpGetCurrCharPtr(cfVfp));
822				return (-1);
823
824			default:
825				/* unknown ftype */
826
827				/* copy path found to 'lpath' */
828				COPYPATH(lpath, cpath_start, cpath_len);
829
830				setErrstr(ERR_UNKNOWN_FTYPE);
831				findend(&vfpGetCurrCharPtr(cfVfp));
832				return (-1);
833			}
834		}
835
836		/*
837		 * if an exact match all processing is completed; break out of
838		 * the main processing loop and finish processing this entry
839		 * prior to returning to the caller.
840		 */
841
842		if (n == 0) {
843			break;
844		}
845
846		/*
847		 * this entry is not an exact match for the path being searched
848		 * for - if this entry is GREATER THAN the path being searched
849		 * for then finish processing and return GREATER THAN result
850		 * to the caller so the entry for the path being searched for
851		 * can be added to the contents file.
852		 */
853
854		if (n < 0) {
855			/*
856			 * the entry we want would fit BEFORE the one we just
857			 * read, so we need to unread what we've read by
858			 * seeking back to the start of this entry
859			 */
860
861			vfpSetCurrCharPtr(cfVfp, pos);
862
863			/* copy path found to 'lpath' */
864			COPYPATH(lpath, cpath_start, cpath_len);
865
866			/* write out any skipped data before returning */
867			if (dataSkipped && (cfTmpVfp != (VFP_T *)NULL)) {
868				WRITEDATA(cfTmpVfp, firstPos, lastPos);
869			}
870
871			return (2); /* path would insert here */
872		}
873
874		/*
875		 * This entry is "LESS THAN" the specified path to search for
876		 * need to process the next entry from the contents file. First,
877		 * if writing to new contents file, update new contents file if
878		 * processing old style entry; otherwise, update skipped data
879		 * information to remember current last byte of skipped data.
880		 */
881
882		if (cfTmpVfp != (VFP_T *)NULL) {
883			char	*px;
884			ssize_t	len;
885
886			if (rdpath != 0) {
887				/* modify record: write out any skipped data */
888				if (dataSkipped) {
889					WRITEDATA(cfTmpVfp, firstPos, lastPos);
890				}
891
892				/*
893				 * copy what we've read and the rest of this
894				 * line onto the specified output stream
895				 */
896				vfpPutBytes(cfTmpVfp, cpath_start, cpath_len);
897				vfpPutc(cfTmpVfp, c);
898				vfpPutc(cfTmpVfp, ept->ftype);
899				vfpPutc(cfTmpVfp, ' ');
900				vfpPuts(cfTmpVfp, ept->pkg_class);
901
902				px = strchr(vfpGetCurrCharPtr(cfVfp), '\n');
903
904				if (px == (char *)NULL) {
905					len = vfpGetBytesRemaining(cfVfp);
906					vfpPutBytes(cfTmpVfp,
907						vfpGetCurrCharPtr(cfVfp), len);
908					vfpPutc(cfTmpVfp, '\n');
909					vfpSeekToEnd(cfVfp);
910				} else {
911					len = vfpGetCurrPtrDelta(cfVfp, px);
912					vfpPutBytes(cfTmpVfp,
913						vfpGetCurrCharPtr(cfVfp), len);
914					vfpIncCurrPtrBy(cfVfp, len);
915				}
916
917				/* reset skiped bytes if any data skipped */
918				if (dataSkipped) {
919					dataSkipped = 0;
920					lastPos = (char *)NULL;
921					firstPos = vfpGetCurrCharPtr(cfVfp);
922				}
923			} else {
924				/* skip data */
925				dataSkipped = 1;
926
927				px = strchr(vfpGetCurrCharPtr(cfVfp), '\n');
928
929				if (px == (char *)NULL) {
930					vfpSeekToEnd(cfVfp);
931				} else {
932					len = vfpGetCurrPtrDelta(cfVfp, px)+1;
933					vfpIncCurrPtrBy(cfVfp, len);
934				}
935				lastPos = vfpGetCurrCharPtr(cfVfp);
936			}
937		} else {
938			/*
939			 * since this isn't the entry we want, just read the
940			 * stream until we find the end of this entry and
941			 * then start this search loop again
942			 */
943			char	*px;
944
945			px = strchr(vfpGetCurrCharPtr(cfVfp), '\n');
946
947			if (px == (char *)NULL) {
948				vfpSeekToEnd(cfVfp);
949
950				/* copy path found to 'lpath' */
951				COPYPATH(lpath, cpath_start, cpath_len);
952
953				setErrstr(pkg_gt(ERR_MISSING_NEWLINE));
954				findend(&vfpGetCurrCharPtr(cfVfp));
955				return (-1);
956			} else {
957				ssize_t	len;
958
959				len = vfpGetCurrPtrDelta(cfVfp, px)+1;
960				vfpIncCurrPtrBy(cfVfp, len);
961			}
962		}
963	}
964
965	/*
966	 * *********************************************************************
967	 * end of main loop processing entries from contents file
968	 * the loop is broken out of when an exact match for the
969	 * path being searched for has been found and the type is one of:
970	 *   - ?fvelspcbdx
971	 * at this point parsing is at the first character past the full path
972	 * name on an exact match for the path being looked for - parse the
973	 * remainder of the entries information into the ept structure.
974	 * *********************************************************************
975	 */
976
977	/* link/symbolic link must have link destination */
978
979	if (((ept->ftype == 's') || (ept->ftype == 'l')) &&
980					(ept->ainfo.local == NULL)) {
981		/* copy path found to 'lpath' */
982		COPYPATH(lpath, cpath_start, cpath_len);
983
984		setErrstr(ERR_NO_LINK_SOURCE_SPECIFIED);
985		findend(&vfpGetCurrCharPtr(cfVfp));
986		return (-1);
987	}
988
989	/* character/block devices have major/minor device numbers */
990
991	if (((ept->ftype == 'c') || (ept->ftype == 'b'))) {
992		ept->ainfo.major = BADMAJOR;
993		ept->ainfo.minor = BADMINOR;
994		if (getnumvfp(&vfpGetCurrCharPtr(cfVfp), 10,
995				(long *)&ept->ainfo.major, BADMAJOR) ||
996		    getnumvfp(&vfpGetCurrCharPtr(cfVfp), 10,
997				(long *)&ept->ainfo.minor, BADMINOR)) {
998			/* copy path found to 'lpath' */
999			COPYPATH(lpath, cpath_start, cpath_len);
1000
1001			setErrstr(pkg_gt(ERR_CANNOT_READ_MM_NUMS));
1002			findend(&vfpGetCurrCharPtr(cfVfp));
1003			return (-1);
1004		}
1005	}
1006
1007	/* most types have mode, owner, group identification components */
1008
1009	if ((ept->ftype == 'd') || (ept->ftype == 'x') || (ept->ftype == 'c') ||
1010		(ept->ftype == 'b') || (ept->ftype == 'p') ||
1011		(ept->ftype == 'f') || (ept->ftype == 'v') ||
1012		(ept->ftype == 'e')) {
1013		/* mode, owner, group should be here */
1014		if (getnumvfp(&vfpGetCurrCharPtr(cfVfp), 8,
1015				(long *)&ept->ainfo.mode, BADMODE) ||
1016		    getstr(&vfpGetCurrCharPtr(cfVfp), sizeof (ept->ainfo.owner),
1017				ept->ainfo.owner, ISWORDSEP) ||
1018		    getstr(&vfpGetCurrCharPtr(cfVfp), sizeof (ept->ainfo.group),
1019				ept->ainfo.group, ISWORDSEP)) {
1020			/* copy path found to 'lpath' */
1021			COPYPATH(lpath, cpath_start, cpath_len);
1022
1023			setErrstr(ERR_CANNOT_READ_MOG);
1024			findend(&vfpGetCurrCharPtr(cfVfp));
1025			return (-1);
1026		}
1027	}
1028
1029	/* i/f/v/e have size, checksum, modification time components */
1030
1031	if ((ept->ftype == 'i') || (ept->ftype == 'f') ||
1032			(ept->ftype == 'v') || (ept->ftype == 'e')) {
1033		/* look for content description */
1034		if (getlnumvfp(&vfpGetCurrCharPtr(cfVfp), 10,
1035				(fsblkcnt_t *)&ept->cinfo.size, BADCONT) ||
1036		    getnumvfp(&vfpGetCurrCharPtr(cfVfp), 10,
1037				(long *)&ept->cinfo.cksum, BADCONT) ||
1038		    getnumvfp(&vfpGetCurrCharPtr(cfVfp), 10,
1039				(long *)&ept->cinfo.modtime, BADCONT)) {
1040			/* copy path found to 'lpath' */
1041			COPYPATH(lpath, cpath_start, cpath_len);
1042
1043			setErrstr(ERR_CANNOT_READ_CONTENT_INFO);
1044			findend(&vfpGetCurrCharPtr(cfVfp));
1045			return (-1);
1046		}
1047	}
1048
1049	/* i files processing is completed - return 'exact match found' */
1050
1051	if (ept->ftype == 'i') {
1052		/* copy path found to 'lpath' */
1053		COPYPATH(lpath, cpath_start, cpath_len);
1054
1055		if (getend(&vfpGetCurrCharPtr(cfVfp))) {
1056			/* copy path found to 'lpath' */
1057			COPYPATH(lpath, cpath_start, cpath_len);
1058
1059			setErrstr(ERR_EXTRA_TOKENS);
1060			return (-1);
1061		}
1062
1063		/* write out any skipped data before returning */
1064		if (dataSkipped && (cfTmpVfp != (VFP_T *)NULL)) {
1065			WRITEDATA(cfTmpVfp, firstPos, lastPos);
1066		}
1067
1068		return (1);
1069	}
1070
1071	/*
1072	 * determine list of packages which reference this entry
1073	 */
1074
1075	lastpinfo = (struct pinfo *)NULL;
1076	while ((c = getstr(&vfpGetCurrCharPtr(cfVfp), sizeof (pkgname),
1077						pkgname, ISPKGNAMESEP)) <= 0) {
1078		/* if c < 0 the string was too long to fix in the buffer */
1079
1080		if (c < 0) {
1081			/* copy path found to 'lpath' */
1082			COPYPATH(lpath, cpath_start, cpath_len);
1083
1084			setErrstr(ERR_PACKAGE_NAME_TOO_LONG);
1085			findend(&vfpGetCurrCharPtr(cfVfp));
1086			return (-1);
1087		}
1088
1089		/* a package is present - create and populate pinfo structure */
1090
1091		pinfo = (struct pinfo *)calloc(1, sizeof (struct pinfo));
1092		if (!pinfo) {
1093			/* copy path found to 'lpath' */
1094			COPYPATH(lpath, cpath_start, cpath_len);
1095
1096			setErrstr(ERR_NO_MEMORY);
1097			findend(&vfpGetCurrCharPtr(cfVfp));
1098			return (-1);
1099		}
1100		if (!lastpinfo) {
1101			ept->pinfo = pinfo; /* first one */
1102		} else {
1103			lastpinfo->next = pinfo; /* link list */
1104		}
1105		lastpinfo = pinfo;
1106
1107		if ((pkgname[0] == '-') || (pkgname[0] == '+') ||
1108			(pkgname[0] == '*') || (pkgname[0] == '~') ||
1109			(pkgname[0] == '!') || (pkgname[0] == '%')) {
1110			pinfo->status = pkgname[0];
1111			(void) strlcpy(pinfo->pkg, pkgname+1,
1112							sizeof (pinfo->pkg));
1113		} else {
1114			(void) strlcpy(pinfo->pkg, pkgname,
1115							sizeof (pinfo->pkg));
1116		}
1117
1118		/* pkg/[:[ftype][:class] */
1119		c = (vfpGetc(cfVfp));
1120		if (c == '\\') {
1121			/* get alternate ftype */
1122			pinfo->editflag++;
1123			c = (vfpGetc(cfVfp));
1124		}
1125
1126		if (c == ':') {
1127			/* get special classname */
1128			(void) getstr(&vfpGetCurrCharPtr(cfVfp),
1129				sizeof (classname), classname, ISWORDSEP);
1130			(void) strlcpy(pinfo->aclass, classname,
1131							sizeof (pinfo->aclass));
1132			c = (vfpGetc(cfVfp));
1133		}
1134		ept->npkgs++;
1135
1136		/* break out of while if at end of entry */
1137
1138		if ((c == '\n') || (c == '\0')) {
1139			break;
1140		}
1141
1142		/* if package not separated by a space return an error */
1143
1144		if (!isspace(c)) {
1145			/* copy path found to 'lpath' */
1146			COPYPATH(lpath, cpath_start, cpath_len);
1147
1148			setErrstr(ERR_BAD_ENTRY_END);
1149			findend(&vfpGetCurrCharPtr(cfVfp));
1150			return (-1);
1151		}
1152	}
1153
1154	/*
1155	 * parsing of the entry is complete
1156	 */
1157
1158	/* copy path found to 'lpath' */
1159	COPYPATH(lpath, cpath_start, cpath_len);
1160
1161	/* write out any skipped data before returning */
1162	if (dataSkipped && (cfTmpVfp != (VFP_T *)NULL)) {
1163		WRITEDATA(cfTmpVfp, firstPos, lastPos);
1164	}
1165
1166	/* if not at the end of the entry, make it so */
1167
1168	if ((c != '\n') && (c != '\0')) {
1169		if (getend(&vfpGetCurrCharPtr(cfVfp)) && ept->pinfo) {
1170			setErrstr(ERR_EXTRA_TOKENS);
1171			return (-1);
1172		}
1173	}
1174
1175	return (1);
1176}
1177
1178static int
1179getstr(char **cp, int n, char *str, int separator[])
1180{
1181	int	c;
1182	char	*p = *cp;
1183	char	*p1;
1184	size_t	len;
1185
1186	if (*p == '\0') {
1187		return (1);
1188	}
1189
1190	/* leading white space ignored */
1191
1192	while (((c = *p) != '\0') && (isspace(*p++)))
1193		;
1194	if ((c == '\0') || (c == '\n')) {
1195		p--;
1196		*cp = p;
1197		return (1); /* nothing there */
1198	}
1199
1200	p--;
1201
1202	/* compute length based on delimiter found or not */
1203
1204	p1 = p;
1205	while (separator[(int)*p1] == 0) {
1206		p1++;
1207	}
1208
1209	len = (ptrdiff_t)p1 - (ptrdiff_t)p;
1210
1211	/* if string will fit in result buffer copy string and return success */
1212
1213	if (len < n) {
1214		(void) memcpy(str, p, len);
1215		str[len] = '\0';
1216		p += len;
1217		*cp = p;
1218		return (0);
1219	}
1220
1221	/* result buffer too small; copy partial string, return error */
1222	(void) memcpy(str, p, n-1);
1223	str[n-1] = '\0';
1224	p += n;
1225	*cp = p;
1226	return (-1);
1227}
1228
1229static int
1230getend(char **cp)
1231{
1232	int	n;
1233	char	*p = *cp;
1234
1235	n = 0;
1236
1237	/* if at end of buffer return no more characters left */
1238
1239	if (*p == '\0') {
1240		return (0);
1241	}
1242
1243	while ((*p != '\0') && (*p != '\n')) {
1244		if (n == 0) {
1245			if (!isspace(*p)) {
1246				n++;
1247			}
1248		}
1249		p++;
1250	}
1251
1252	*cp = ++p;
1253	return (n);
1254}
1255
1256static void
1257findend(char **cp)
1258{
1259	char	*p1;
1260	char	*p = *cp;
1261
1262	/* if at end of buffer return no more characters left */
1263
1264	if (*p == '\0') {
1265		return;
1266	}
1267
1268	/* find the end of the line */
1269
1270	p1 = strchr(p, '\n');
1271
1272	if (p1 != (char *)NULL) {
1273		*cp = ++p1;
1274		return;
1275	}
1276
1277	*cp = strchr(p, '\0');
1278}
1279