xref: /illumos-gate/usr/src/lib/libpkg/common/srchcfile.c (revision 21ecdf64e1e200cd74cadf771fc7ddc3d0062080)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29 
30 
31 
32 #include <stdio.h>
33 #include <limits.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <strings.h>
37 #include <ctype.h>
38 #include <sys/types.h>
39 #include <libintl.h>
40 #include "pkglib.h"
41 #include "pkgstrct.h"
42 #include "pkglocale.h"
43 #include "pkglibmsgs.h"
44 
45 /*
46  * Forward declarations
47  */
48 
49 static void	findend(char **cp);
50 static int	getend(char **cp);
51 static int	getstr(char **cp, int n, char *str, int separator[]);
52 
53 /* from gpkgmap.c */
54 int	getnumvfp(char **cp, int base, long *d, long bad);
55 int	getlnumvfp(char **cp, int base, fsblkcnt_t *d, long bad);
56 
57 /*
58  * Module globals
59  */
60 
61 static char	lpath[PATH_MAX];	/* for ept->path */
62 static char	mylocal[PATH_MAX];	/* for ept->ainfo.local */
63 static int	decisionTableInit = 0;
64 
65 /*
66  * These arrays must be indexable by an unsigned char.
67  */
68 
69 static int	ISPKGPATHSEP[UCHAR_MAX+1];
70 static int	ISWORDSEP[UCHAR_MAX+1];
71 static int	ISPKGNAMESEP[UCHAR_MAX+1];
72 
73 /*
74  * Name:	WRITEDATA
75  * Description:	write out data to VFP_T given start and end pointers
76  * Arguments:	VFP - (VFP_T *) - [RO, *RW]
77  *			Contents file VFP to narrow search on
78  *		FIRSTPOS - (char *) - [RO, *RO]
79  *			Pointer to first byte to write out
80  *		LASTPOS - (char *) - [RO, *RO]
81  *			Pointer to last byte to write out
82  */
83 
84 #define	WRITEDATA(VFP, FIRSTPOS, LASTPOS)				\
85 	{								\
86 		ssize_t XXlenXX;					\
87 		/* compute number of bytes skipped */			\
88 		XXlenXX = (ptrdiff_t)(LASTPOS) - (ptrdiff_t)(FIRSTPOS);	\
89 		/* write the bytes out */				\
90 		vfpPutBytes((VFP), (FIRSTPOS), XXlenXX);		\
91 	}
92 
93 /*
94  * Name:	COPYPATH
95  * Description:	copy path limiting size to destination capacity
96  * Arguments:	DEST - (char []) - [RW]
97  *		SRC - (char *) - [RO, *RO]
98  *			Pointer to first byte of path to copy
99  *		LEN - (int) - [RO]
100  *			Number of bytes to copy
101  */
102 
103 #define	COPYPATH(DEST, SRC, LEN)					\
104 	{								\
105 		/* assure return path does not overflow */		\
106 		if ((LEN) > sizeof ((DEST))) {				\
107 			(LEN) = sizeof ((DEST))-1;			\
108 		}							\
109 		/* copy return path to local storage */			\
110 		(void) memcpy((DEST), (SRC), (LEN));			\
111 		(DEST)[(LEN)] = '\0';					\
112 	}
113 
114 /*
115  * Name:	narrowSearch
116  * Description:	narrow the search location for a specified path
117  *		The contents and package map files are always sorted by path.
118  *		This function is given a target path to search for given the
119  *		current location in a contents file. It is assured that the
120  *		target path has not been searched for yet in the contents file
121  *		so the current location in the contents file is guaranteed to
122  *		be less than the location of the target path (if present).
123  *		Given this employ a binary search to speed up the search for
124  *		the path nearest to a specified target path.
125  * Arguments:	a_vfp - (VFP_T *) - [RO, *RW]
126  *			Contents file VFP to narrow search on
127  *		a_path - (char *) - [RO, *RO]
128  *			Pointer to path to search for
129  *		a_pathLen - (size_t) - [RO]
130  *			Length of string (a_path)
131  * Returns:	char *	- pointer to first byte of entry in contents file that
132  *			is guaranteed to be the closest match to the specified
133  *			a_path without being "greater than" the path.
134  *			== (char *)NULL if no entry found
135  */
136 
137 static char *
138 narrowSearch(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 
370 int
371 srchcfile(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 
1178 static int
1179 getstr(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 
1229 static int
1230 getend(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 
1256 static void
1257 findend(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