xref: /illumos-gate/usr/src/lib/libpkg/common/gpkgmap.c (revision 93a18d6d401e844455263f926578e9d2aa6b47ec)
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 <unistd.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <fcntl.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <errno.h>
42 #include "pkgstrct.h"
43 #include "pkglib.h"
44 #include "pkglibmsgs.h"
45 #include "pkglocale.h"
46 
47 #define	ERR_CANT_READ_LCLPATH		"unable to read local pathname"
48 #define	ERR_BAD_VOLUME_NUMBER		"bad volume number"
49 #define	ERR_CANNOT_READ_PATHNAME_FIELD	"unable to read pathname field"
50 #define	ERR_CANNOT_READ_CONTENT_INFO	"unable to read content info"
51 #define	ERR_EXTRA_TOKENS_PRESENT	"extra tokens on input line"
52 #define	ERR_CANNOT_READ_CLASS_TOKEN	"unable to read class token"
53 #define	ERR_BAD_LINK_SPEC		"missing or invalid link specification"
54 #define	ERR_UNKNOWN_FTYPE		"unknown ftype"
55 #define	ERR_NO_LINKSOURCE		"no link source specified"
56 #define	ERR_CANNOT_READ_MM_DEVNUMS	"unable to read major/minor "\
57 					"device numbers"
58 static int	eatwhite(FILE *fp);
59 static int	getend(FILE *fp);
60 static int	getstr(FILE *fp, char *sep, int n, char *str);
61 static int	getnum(FILE *fp, int base, long *d, long bad);
62 static int	getlnum(FILE *fp, int base, fsblkcnt_t *d, long bad);
63 static int	getvalmode(FILE *fp, mode_t *d, long bad, int map);
64 
65 static int	getendvfp(char **cp);
66 static void	findendvfp(char **cp);
67 static int	getstrvfp(char **cp, char *sep, int n, char *str);
68 static int	getvalmodevfp(char **cp, mode_t *d, long bad, int map);
69 int		getnumvfp(char **cp, int base, long *d, long bad);
70 int		getlnumvfp(char **cp, int base, fsblkcnt_t *d, long bad);
71 
72 static char	mypath[PATH_MAX];
73 static char	mylocal[PATH_MAX];
74 static int	mapmode = MAPNONE;
75 static char	*maptype = "";
76 static mode_t	d_mode = BADMODE;
77 static char 	*d_owner = BADOWNER;
78 static char	*d_group = BADGROUP;
79 
80 /*
81  * These determine how gpkgmap() deals with mode, owner and group defaults.
82  * It is assumed that the owner and group arguments represent static fields
83  * which will persist until attrdefault() is called.
84  */
85 void
86 attrpreset(int mode, char *owner, char *group)
87 {
88 	d_mode = mode;
89 	d_owner = owner;
90 	d_group = group;
91 }
92 
93 void
94 attrdefault()
95 {
96 	d_mode = NOMODE;
97 	d_owner = NOOWNER;
98 	d_group = NOGROUP;
99 }
100 
101 /*
102  * This determines how gpkgmap() deals with environment variables in the
103  * mode, owner and group. Path is evaluated at a higher level based upon
104  * other circumstances.
105  */
106 void
107 setmapmode(int mode)
108 {
109 	if (mode >= 0 || mode <= 3) {
110 		mapmode = mode;
111 		if (mode == MAPBUILD)
112 			maptype = " build";
113 		else if (mode == MAPINSTALL)
114 			maptype = " install";
115 		else
116 			maptype = "";
117 	}
118 }
119 
120 /* This is the external query interface for mapmode. */
121 int
122 getmapmode(void)
123 {
124 	return (mapmode);
125 }
126 
127 /*
128  * Unpack the pkgmap or the contents file or whatever file is in that format.
129  * Based upon mapmode, environment parameters will be resolved for mode,
130  * owner and group.
131  */
132 
133 int
134 gpkgmap(struct cfent *ept, FILE *fp)
135 {
136 	int		c;
137 	boolean_t	first_char = B_TRUE;
138 
139 	setErrstr(NULL);
140 	ept->volno = 0;
141 	ept->ftype = BADFTYPE;
142 	(void) strcpy(ept->pkg_class, BADCLASS);
143 	ept->pkg_class_idx = -1;
144 	ept->path = NULL;
145 	ept->ainfo.local = NULL;
146 	/* default attributes were supplied, so don't reset */
147 	ept->ainfo.mode = d_mode;
148 	(void) strcpy(ept->ainfo.owner, d_owner);
149 	(void) strcpy(ept->ainfo.group, d_group);
150 #ifdef SUNOS41
151 	ept->ainfo.xmajor = BADMAJOR;
152 	ept->ainfo.xminor = BADMINOR;
153 #else
154 	ept->ainfo.major = BADMAJOR;
155 	ept->ainfo.minor = BADMINOR;
156 #endif
157 	ept->cinfo.cksum = ept->cinfo.modtime = ept->cinfo.size = (-1L);
158 
159 	ept->npkgs = 0;
160 
161 	if (!fp)
162 		return (-1);
163 readline:
164 	c = eatwhite(fp);
165 
166 	/*
167 	 * If the first character is not a digit, we assume that the
168 	 * volume number is 1.
169 	 */
170 	if (first_char && !isdigit(c)) {
171 		ept->volno = 1;
172 	}
173 	first_char = B_FALSE;
174 
175 	switch (c) {
176 	    case EOF:
177 		return (0);
178 
179 	    case '0':
180 	    case '1':
181 	    case '2':
182 	    case '3':
183 	    case '4':
184 	    case '5':
185 	    case '6':
186 	    case '7':
187 	    case '8':
188 	    case '9':
189 		if (ept->volno) {
190 			setErrstr(pkg_gt(ERR_BAD_VOLUME_NUMBER));
191 			goto error;
192 		}
193 		do {
194 			ept->volno = (ept->volno*10)+c-'0';
195 			c = getc(fp);
196 		} while (isdigit(c));
197 		if (ept->volno == 0)
198 			ept->volno = 1;
199 
200 		goto readline;
201 
202 	    case ':':
203 	    case '#':
204 		(void) getend(fp);
205 		/*FALLTHRU*/
206 	    case '\n':
207 		/*
208 		 * Since we are going to scan the next line,
209 		 * we need to reset volume number and first_char.
210 		 */
211 		ept->volno = 0;
212 		first_char = B_TRUE;
213 		goto readline;
214 
215 	    case 'i':
216 		ept->ftype = (char)c;
217 		c = eatwhite(fp);
218 		/*FALLTHRU*/
219 	    case '.':
220 	    case '/':
221 		(void) ungetc(c, fp);
222 
223 		if (getstr(fp, "=", PATH_MAX, mypath)) {
224 			setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
225 			goto error;
226 		}
227 		ept->path = mypath;
228 		c = getc(fp);
229 		if (c == '=') {
230 			if (getstr(fp, NULL, PATH_MAX, mylocal)) {
231 				setErrstr(pkg_gt(ERR_CANT_READ_LCLPATH));
232 				goto error;
233 			}
234 			ept->ainfo.local = mylocal;
235 		} else
236 			(void) ungetc(c, fp);
237 
238 		if (ept->ftype == 'i') {
239 			/* content info might exist */
240 			if (!getlnum(fp, 10, (fsblkcnt_t *)&ept->cinfo.size,
241 			    BADCONT) &&
242 			    (getnum(fp, 10, (long *)&ept->cinfo.cksum,
243 			    BADCONT) ||
244 			    getnum(fp, 10, (long *)&ept->cinfo.modtime,
245 			    BADCONT))) {
246 				setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
247 				goto error;
248 			}
249 		}
250 		if (getend(fp)) {
251 			setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
252 			return (-1);
253 		}
254 		return (1);
255 
256 	    case '?':
257 	    case 'f':
258 	    case 'v':
259 	    case 'e':
260 	    case 'l':
261 	    case 's':
262 	    case 'p':
263 	    case 'c':
264 	    case 'b':
265 	    case 'd':
266 	    case 'x':
267 		ept->ftype = (char)c;
268 		if (getstr(fp, NULL, CLSSIZ, ept->pkg_class)) {
269 			setErrstr(pkg_gt(ERR_CANNOT_READ_CLASS_TOKEN));
270 			goto error;
271 		}
272 		if (getstr(fp, "=", PATH_MAX, mypath)) {
273 			setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
274 			goto error;
275 		}
276 		ept->path = mypath;
277 
278 		c = getc(fp);
279 		if (c == '=') {
280 			/* local path */
281 			if (getstr(fp, NULL, PATH_MAX, mylocal)) {
282 				if (ept->ftype == 's' || ept->ftype == 'l') {
283 					setErrstr(pkg_gt(ERR_READLINK));
284 				} else {
285 					setErrstr(
286 						pkg_gt(ERR_CANT_READ_LCLPATH));
287 				}
288 				goto error;
289 			}
290 			ept->ainfo.local = mylocal;
291 		} else if (strchr("sl", ept->ftype)) {
292 			if ((c != EOF) && (c != '\n'))
293 				(void) getend(fp);
294 			setErrstr(pkg_gt(ERR_BAD_LINK_SPEC));
295 			return (-1);
296 		} else
297 			(void) ungetc(c, fp);
298 		break;
299 
300 	    default:
301 		setErrstr(pkg_gt(ERR_UNKNOWN_FTYPE));
302 error:
303 		(void) getend(fp);
304 		return (-1);
305 	}
306 
307 	if (strchr("sl", ept->ftype) && (ept->ainfo.local == NULL)) {
308 		setErrstr(pkg_gt(ERR_NO_LINKSOURCE));
309 		goto error;
310 	}
311 
312 	if (strchr("cb", ept->ftype)) {
313 #ifdef SUNOS41
314 		ept->ainfo.xmajor = BADMAJOR;
315 		ept->ainfo.xminor = BADMINOR;
316 		if (getnum(fp, 10, (long *)&ept->ainfo.xmajor, BADMAJOR) ||
317 		    getnum(fp, 10, (long *)&ept->ainfo.xminor, BADMINOR))
318 #else
319 		ept->ainfo.major = BADMAJOR;
320 		ept->ainfo.minor = BADMINOR;
321 		if (getnum(fp, 10, (long *)&ept->ainfo.major, BADMAJOR) ||
322 		    getnum(fp, 10, (long *)&ept->ainfo.minor, BADMINOR))
323 #endif
324 		{
325 			setErrstr(pkg_gt(ERR_CANNOT_READ_MM_DEVNUMS));
326 			goto error;
327 		}
328 	}
329 
330 	/*
331 	 * Links and information files don't have attributes associated with
332 	 * them. The following either resolves potential variables or passes
333 	 * them through. Mode is tested for validity to some degree. BAD???
334 	 * is returned to indicate that no meaningful mode was provided. A
335 	 * higher authority will decide if that's OK or not. CUR??? means that
336 	 * the prototype file specifically requires a wildcard ('?') for
337 	 * that entry. We issue an error if attributes were entered wrong.
338 	 * We just return BAD??? if there was no entry at all.
339 	 */
340 	if (strchr("cbdxpfve", ept->ftype)) {
341 		int retval;
342 
343 		if ((retval = getvalmode(fp, &(ept->ainfo.mode), CURMODE,
344 		    (mapmode != MAPNONE))) == 1)
345 			goto end;	/* nothing else on the line */
346 		else if (retval == 2)
347 			goto error;	/* mode is too no good */
348 
349 		/* owner & group should be here */
350 		if ((retval = getstr(fp, NULL, ATRSIZ,
351 		    ept->ainfo.owner)) == 1)
352 			goto end;	/* no owner or group - warning */
353 		if (retval == -1) {
354 			setErrstr(pkg_gt(ERR_OWNTOOLONG));
355 			goto error;
356 		}
357 
358 		if ((retval = getstr(fp, NULL, ATRSIZ,
359 		    ept->ainfo.group)) == 1)
360 			goto end;	/* no group - warning */
361 		if (retval == -1) {
362 			setErrstr(pkg_gt(ERR_GRPTOOLONG));
363 			goto error;
364 		}
365 
366 		/* Resolve the parameters if required. */
367 		if (mapmode != MAPNONE) {
368 			if (mapvar(mapmode, ept->ainfo.owner)) {
369 				(void) snprintf(getErrbufAddr(),
370 					getErrbufSize(),
371 					pkg_gt(ERR_NOVAR),
372 					maptype, ept->ainfo.owner);
373 				setErrstr(getErrbufAddr());
374 				goto error;
375 			}
376 			if (mapvar(mapmode, ept->ainfo.group)) {
377 				(void) snprintf(getErrbufAddr(),
378 					getErrbufSize(), pkg_gt(ERR_NOVAR),
379 					maptype, ept->ainfo.group);
380 				setErrstr(getErrbufAddr());
381 				goto error;
382 			}
383 		}
384 	}
385 
386 	if (strchr("ifve", ept->ftype)) {
387 		/* look for content description */
388 		if (!getlnum(fp, 10, (fsblkcnt_t *)&ept->cinfo.size, BADCONT) &&
389 		(getnum(fp, 10, (long *)&ept->cinfo.cksum, BADCONT) ||
390 		getnum(fp, 10, (long *)&ept->cinfo.modtime, BADCONT))) {
391 			setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
392 			goto error;
393 		}
394 	}
395 
396 	if (ept->ftype == 'i')
397 		goto end;
398 
399 end:
400 	if (getend(fp) && ept->pinfo) {
401 		setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
402 		return (-1);
403 	}
404 
405 done:
406 	return (1);
407 }
408 
409 /*
410  * Get and validate the mode attribute. This returns an error if
411  *	1. the mode string is too long
412  *	2. the mode string includes alpha characters
413  *	3. the mode string is not octal
414  *	4. mode string is an install parameter
415  *	5. mode is an unresolved build parameter and MAPBUILD is
416  *	   in effect.
417  * If the mode is a build parameter, it is
418  *	1. returned as is if MAPNONE is in effect
419  *	2. evaluated if MAPBUILD is in effect
420  *
421  * NOTE : We use "mapmode!=MAPBUILD" to gather that it is install
422  * time. At install time we just fix a mode with bad bits set by
423  * setting it to CURMODE. This should be an error in a few releases
424  * (2.8 maybe) but faulty modes are so common in existing packages
425  * that this is a reasonable exception. -- JST 1994-11-9
426  *
427  * RETURNS
428  *	0 if mode is being returned as a valid value
429  *	1 if no attributes are present on the line
430  *	2 if there was a fundamental error
431  */
432 static int
433 getvalmode(FILE *fp, mode_t *d, long bad, int map)
434 {
435 	char tempmode[20];
436 	mode_t tempmode_t;
437 	int retval;
438 
439 	if ((retval = getstr(fp, NULL, ATRSIZ, tempmode)) == 1)
440 		return (1);
441 	else if (retval == -1) {
442 		setErrstr(pkg_gt(ERR_MODELONG));
443 		return (2);
444 	} else {
445 		/*
446 		 * If it isn't a '?' (meaning go with whatever mode is
447 		 * there), validate the mode and convert it to a mode_t. The
448 		 * "bad" variable here is a misnomer. It doesn't necessarily
449 		 * mean bad.
450 		 */
451 		if (tempmode[0] == '?') {
452 			*d = WILDCARD;
453 		} else {
454 			/*
455 			 * Mode may not be an install parameter or a
456 			 * non-build parameter.
457 			 */
458 			if (tempmode[0] == '$' &&
459 			    (isupper(tempmode[1]) || !islower(tempmode[1]))) {
460 				setErrstr(pkg_gt(ERR_IMODE));
461 				return (2);
462 			}
463 
464 			if ((map) && (mapvar(mapmode, tempmode))) {
465 				(void) snprintf(getErrbufAddr(),
466 						getErrbufSize(),
467 						pkg_gt(ERR_NOVAR),
468 						maptype, tempmode);
469 				setErrstr(getErrbufAddr());
470 				return (2);
471 			}
472 
473 
474 			if (tempmode[0] == '$') {
475 				*d = BADMODE;	/* may be a problem */
476 			} else {
477 				/*
478 				 * At this point it's supposed to be
479 				 * something we can convert to a number.
480 				 */
481 				int n = 0;
482 
483 				/*
484 				 * We reject it if it contains nonnumbers or
485 				 * it's not octal.
486 				 */
487 				while (tempmode[n] && !isspace(tempmode[n])) {
488 					if (!isdigit(tempmode[n])) {
489 						setErrstr(
490 							pkg_gt(ERR_MODEALPHA));
491 						return (2);
492 					}
493 
494 					if (strchr("89abcdefABCDEF",
495 					    tempmode[n])) {
496 						setErrstr(
497 							pkg_gt(ERR_BASEINVAL));
498 						return (2);
499 					}
500 					n++;
501 				}
502 
503 				tempmode_t = strtol(tempmode, NULL, 8);
504 
505 				/*
506 				 * We reject it if it contains inappropriate
507 				 * bits.
508 				 */
509 				if (tempmode_t & ~(S_IAMB |
510 				    S_ISUID | S_ISGID | S_ISVTX)) {
511 					if (mapmode != MAPBUILD) {
512 						tempmode_t = bad;
513 					} else {
514 						setErrstr(pkg_gt(ERR_MODEBITS));
515 						return (2);
516 					}
517 				}
518 				*d = tempmode_t;
519 			}
520 		}
521 		return (0);
522 	}
523 }
524 
525 static int
526 getnum(FILE *fp, int base, long *d, long bad)
527 {
528 	int c, b;
529 
530 	/* leading white space ignored */
531 	c = eatwhite(fp);
532 	if (c == '?') {
533 		*d = bad;
534 		return (0);
535 	}
536 
537 	if ((c == EOF) || (c == '\n') || !isdigit(c)) {
538 		(void) ungetc(c, fp);
539 		return (1);
540 	}
541 
542 	*d = 0;
543 	while (isdigit(c)) {
544 		b = (c & 017);
545 		if (b >= base)
546 			return (2);
547 		*d = (*d * base) + b;
548 		c = getc(fp);
549 	}
550 	(void) ungetc(c, fp);
551 	return (0);
552 }
553 
554 static int
555 getlnum(FILE *fp, int base, fsblkcnt_t *d, long bad)
556 {
557 	int c, b;
558 
559 	/* leading white space ignored */
560 	c = eatwhite(fp);
561 	if (c == '?') {
562 		*d = bad;
563 		return (0);
564 	}
565 
566 	if ((c == EOF) || (c == '\n') || !isdigit(c)) {
567 		(void) ungetc(c, fp);
568 		return (1);
569 	}
570 
571 	*d = 0;
572 	while (isdigit(c)) {
573 		b = (c & 017);
574 		if (b >= base)
575 			return (2);
576 		*d = (*d * base) + b;
577 		c = getc(fp);
578 	}
579 	(void) ungetc(c, fp);
580 	return (0);
581 }
582 
583 /*
584  *  Get a string from the file. Returns
585  *	0 if all OK
586  *	1 if nothing there
587  *	-1 if string is too long
588  */
589 static int
590 getstr(FILE *fp, char *sep, int n, char *str)
591 {
592 	int c;
593 
594 	/* leading white space ignored */
595 	c = eatwhite(fp);
596 	if ((c == EOF) || (c == '\n')) {
597 		(void) ungetc(c, fp);
598 		return (1); /* nothing there */
599 	}
600 
601 	/* fill up string until space, tab, or separator */
602 	while (!strchr(" \t", c) && (!sep || !strchr(sep, c))) {
603 		if (n-- < 1) {
604 			*str = '\0';
605 			return (-1); /* too long */
606 		}
607 		*str++ = (char)c;
608 		c = getc(fp);
609 		if ((c == EOF) || (c == '\n'))
610 			break; /* no more on this line */
611 	}
612 	*str = '\0';
613 	(void) ungetc(c, fp);
614 
615 	return (0);
616 }
617 
618 static int
619 getend(FILE *fp)
620 {
621 	int c;
622 	int n;
623 
624 	n = 0;
625 	do {
626 		if ((c = getc(fp)) == EOF)
627 			return (n);
628 		if (!isspace(c))
629 			n++;
630 	} while (c != '\n');
631 	return (n);
632 }
633 
634 static int
635 eatwhite(FILE *fp)
636 {
637 	int c;
638 
639 	/* this test works around a side effect of getc() */
640 	if (feof(fp))
641 		return (EOF);
642 	do
643 		c = getc(fp);
644 	while ((c == ' ') || (c == '\t'));
645 	return (c);
646 }
647 
648 int
649 gpkgmapvfp(struct cfent *ept, VFP_T *vfp)
650 {
651 	int		c;
652 	boolean_t	first_char = B_TRUE;
653 	(void) strlcpy(ept->pkg_class, BADCLASS, sizeof (ept->pkg_class));
654 	(void) strlcpy(ept->ainfo.owner, d_owner, sizeof (ept->ainfo.owner));
655 	(void) strlcpy(ept->ainfo.group, d_group, sizeof (ept->ainfo.group));
656 
657 	setErrstr(NULL);
658 	ept->volno = 0;
659 	ept->ftype = BADFTYPE;
660 	ept->pkg_class_idx = -1;
661 	ept->path = NULL;
662 	ept->ainfo.local = NULL;
663 	ept->ainfo.mode = d_mode;
664 	ept->ainfo.major = BADMAJOR;
665 	ept->ainfo.minor = BADMINOR;
666 	ept->cinfo.cksum = (-1L);
667 	ept->cinfo.modtime = (-1L);
668 	ept->cinfo.size = (-1L);
669 
670 	ept->npkgs = 0;
671 
672 	/* return error if no vfp specified */
673 
674 	if (vfp == (VFP_T *)NULL) {
675 		return (-1);
676 	}
677 
678 readline:
679 	while (((c = vfpGetcNoInc(vfp)) != '\0') && (isspace(vfpGetc(vfp))))
680 		;
681 
682 	/*
683 	 * If the first character is not a digit, we assume that the
684 	 * volume number is 1.
685 	 */
686 	if (first_char && !isdigit(c)) {
687 		ept->volno = 1;
688 	}
689 	first_char = B_FALSE;
690 
691 	/*
692 	 * In case of hsfs the zero-padding of partial pages
693 	 * returned by mmap is not done properly. A separate bug has been filed
694 	 * on this.
695 	 */
696 
697 	if (vfp->_vfpCurr && (vfp->_vfpCurr > vfp->_vfpEnd)) {
698 		return (0);
699 	}
700 
701 	switch (c) {
702 	    case '\0':
703 		return (0);
704 
705 	    case '0':
706 	    case '1':
707 	    case '2':
708 	    case '3':
709 	    case '4':
710 	    case '5':
711 	    case '6':
712 	    case '7':
713 	    case '8':
714 	    case '9':
715 		if (ept->volno) {
716 			setErrstr(pkg_gt(ERR_BAD_VOLUME_NUMBER));
717 			goto error;
718 		}
719 		do {
720 			ept->volno = (ept->volno*10)+c-'0';
721 			c = vfpGetc(vfp);
722 		} while (isdigit(c));
723 		if (ept->volno == 0) {
724 			ept->volno = 1;
725 		}
726 
727 		goto readline;
728 
729 	    case ':':
730 	    case '#':
731 		(void) findendvfp(&vfpGetCurrCharPtr(vfp));
732 		/*FALLTHRU*/
733 	    case '\n':
734 		/*
735 		 * Since we are going to scan the next line,
736 		 * we need to reset volume number and first_char.
737 		 */
738 		ept->volno = 0;
739 		first_char = B_TRUE;
740 		goto readline;
741 
742 	    case 'i':
743 		ept->ftype = (char)c;
744 		while (((c = vfpGetcNoInc(vfp)) != '\0') &&
745 						(isspace(vfpGetc(vfp))))
746 			;
747 		/*FALLTHRU*/
748 	    case '.':
749 	    case '/':
750 		vfpDecCurrPtr(vfp);
751 
752 		if (getstrvfp(&vfpGetCurrCharPtr(vfp), "=", PATH_MAX, mypath)) {
753 			setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
754 			goto error;
755 		}
756 		ept->path = mypath;
757 		c = vfpGetc(vfp);
758 		if (c == '=') {
759 			if (getstrvfp(&vfpGetCurrCharPtr(vfp), NULL, PATH_MAX,
760 							mylocal)) {
761 				setErrstr(pkg_gt(ERR_CANT_READ_LCLPATH));
762 				goto error;
763 			}
764 			ept->ainfo.local = mylocal;
765 		} else {
766 			vfpDecCurrPtr(vfp);
767 		}
768 
769 		if (ept->ftype == 'i') {
770 			/* content info might exist */
771 			if (!getlnumvfp(&vfpGetCurrCharPtr(vfp), 10,
772 				(fsblkcnt_t *)&ept->cinfo.size, BADCONT) &&
773 			    (getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
774 				(long *)&ept->cinfo.cksum, BADCONT) ||
775 			    getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
776 				(long *)&ept->cinfo.modtime, BADCONT))) {
777 				setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
778 				goto error;
779 			}
780 		}
781 
782 		if (getendvfp(&vfpGetCurrCharPtr(vfp))) {
783 			setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
784 			return (-1);
785 		}
786 		return (1);
787 
788 	    case '?':
789 	    case 'f':
790 	    case 'v':
791 	    case 'e':
792 	    case 'l':
793 	    case 's':
794 	    case 'p':
795 	    case 'c':
796 	    case 'b':
797 	    case 'd':
798 	    case 'x':
799 		ept->ftype = (char)c;
800 		if (getstrvfp(&vfpGetCurrCharPtr(vfp), NULL,
801 						CLSSIZ, ept->pkg_class)) {
802 			setErrstr(pkg_gt(ERR_CANNOT_READ_CLASS_TOKEN));
803 			goto error;
804 		}
805 		if (getstrvfp(&vfpGetCurrCharPtr(vfp), "=", PATH_MAX, mypath)) {
806 			setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
807 			goto error;
808 		}
809 		ept->path = mypath;
810 
811 		c = vfpGetc(vfp);
812 		if (c == '=') {
813 			/* local path */
814 			if (getstrvfp(&vfpGetCurrCharPtr(vfp), NULL,
815 							PATH_MAX, mylocal)) {
816 				if (ept->ftype == 's' || ept->ftype == 'l') {
817 					setErrstr(pkg_gt(ERR_READLINK));
818 				} else {
819 					setErrstr(
820 						pkg_gt(ERR_CANT_READ_LCLPATH));
821 				}
822 				goto error;
823 			}
824 			ept->ainfo.local = mylocal;
825 		} else if ((ept->ftype == 's') || (ept->ftype == 'l')) {
826 			if ((c != '\0') && (c != '\n'))
827 				(void) findendvfp(&vfpGetCurrCharPtr(vfp));
828 			setErrstr(pkg_gt(ERR_BAD_LINK_SPEC));
829 			return (-1);
830 		} else {
831 			vfpDecCurrPtr(vfp);
832 		}
833 		break;
834 
835 	    default:
836 		setErrstr(pkg_gt(ERR_UNKNOWN_FTYPE));
837 error:
838 		(void) findendvfp(&vfpGetCurrCharPtr(vfp));
839 		return (-1);
840 	}
841 
842 	if (((ept->ftype == 's') || (ept->ftype == 'l')) &&
843 					(ept->ainfo.local == NULL)) {
844 		setErrstr(pkg_gt(ERR_NO_LINKSOURCE));
845 		goto error;
846 	}
847 
848 	if (((ept->ftype == 'c') || (ept->ftype == 'b'))) {
849 		ept->ainfo.major = BADMAJOR;
850 		ept->ainfo.minor = BADMINOR;
851 
852 		if (getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
853 				(long *)&ept->ainfo.major, BADMAJOR) ||
854 		    getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
855 				(long *)&ept->ainfo.minor, BADMINOR)) {
856 			setErrstr(pkg_gt(ERR_CANNOT_READ_MM_DEVNUMS));
857 			goto error;
858 		}
859 	}
860 
861 	/*
862 	 * Links and information files don't have attributes associated with
863 	 * them. The following either resolves potential variables or passes
864 	 * them through. Mode is tested for validity to some degree. BAD???
865 	 * is returned to indicate that no meaningful mode was provided. A
866 	 * higher authority will decide if that's OK or not. CUR??? means that
867 	 * the prototype file specifically requires a wildcard ('?') for
868 	 * that entry. We issue an error if attributes were entered wrong.
869 	 * We just return BAD??? if there was no entry at all.
870 	 */
871 	if ((ept->ftype == 'd') || (ept->ftype == 'x') || (ept->ftype == 'c') ||
872 		(ept->ftype == 'b') || (ept->ftype == 'p') ||
873 		(ept->ftype == 'f') || (ept->ftype == 'v') ||
874 		(ept->ftype == 'e')) {
875 		int retval;
876 
877 		retval = getvalmodevfp(&vfpGetCurrCharPtr(vfp),
878 				&(ept->ainfo.mode),
879 				CURMODE, (mapmode != MAPNONE));
880 
881 		if (retval == 1) {
882 			goto end;	/* nothing else on the line */
883 		} else if (retval == 2) {
884 			goto error;	/* mode is too no good */
885 		}
886 
887 		/* owner & group should be here */
888 		if ((retval = getstrvfp(&vfpGetCurrCharPtr(vfp), NULL, ATRSIZ,
889 		    ept->ainfo.owner)) == 1)
890 			goto end;	/* no owner or group - warning */
891 		if (retval == -1) {
892 			setErrstr(pkg_gt(ERR_OWNTOOLONG));
893 			goto error;
894 		}
895 
896 		if ((retval = getstrvfp(&vfpGetCurrCharPtr(vfp), NULL, ATRSIZ,
897 		    ept->ainfo.group)) == 1)
898 			goto end;	/* no group - warning */
899 		if (retval == -1) {
900 			setErrstr(pkg_gt(ERR_GRPTOOLONG));
901 			goto error;
902 		}
903 
904 		/* Resolve the parameters if required. */
905 		if (mapmode != MAPNONE) {
906 			if (mapvar(mapmode, ept->ainfo.owner)) {
907 				(void) snprintf(getErrbufAddr(),
908 					getErrbufSize(), pkg_gt(ERR_NOVAR),
909 					maptype, ept->ainfo.owner);
910 				setErrstr(getErrbufAddr());
911 				goto error;
912 			}
913 			if (mapvar(mapmode, ept->ainfo.group)) {
914 				(void) snprintf(getErrbufAddr(),
915 					getErrbufSize(), pkg_gt(ERR_NOVAR),
916 					maptype, ept->ainfo.group);
917 				setErrstr(getErrbufAddr());
918 				goto error;
919 			}
920 		}
921 	}
922 
923 	if ((ept->ftype == 'i') || (ept->ftype == 'f') ||
924 			(ept->ftype == 'v') || (ept->ftype == 'e')) {
925 		/* look for content description */
926 		if (!getlnumvfp(&vfpGetCurrCharPtr(vfp), 10,
927 				(fsblkcnt_t *)&ept->cinfo.size, BADCONT) &&
928 		(getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
929 				(long *)&ept->cinfo.cksum, BADCONT) ||
930 		getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
931 				(long *)&ept->cinfo.modtime, BADCONT))) {
932 			setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
933 			goto error;
934 		}
935 	}
936 
937 	if (ept->ftype == 'i')
938 		goto end;
939 
940 end:
941 	if (getendvfp(&vfpGetCurrCharPtr(vfp)) && ept->pinfo) {
942 		setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
943 		return (-1);
944 	}
945 
946 done:
947 	return (1);
948 }
949 
950 /*
951  * Get and validate the mode attribute. This returns an error if
952  *	1. the mode string is too long
953  *	2. the mode string includes alpha characters
954  *	3. the mode string is not octal
955  *	4. mode string is an install parameter
956  *	5. mode is an unresolved build parameter and MAPBUILD is
957  *	   in effect.
958  * If the mode is a build parameter, it is
959  *	1. returned as is if MAPNONE is in effect
960  *	2. evaluated if MAPBUILD is in effect
961  *
962  * NOTE : We use "mapmode!=MAPBUILD" to gather that it is install
963  * time. At install time we just fix a mode with bad bits set by
964  * setting it to CURMODE. This should be an error in a few releases
965  * (2.8 maybe) but faulty modes are so common in existing packages
966  * that this is a reasonable exception. -- JST 1994-11-9
967  *
968  * RETURNS
969  *	0 if mode is being returned as a valid value
970  *	1 if no attributes are present on the line
971  *	2 if there was a fundamental error
972  */
973 static int
974 getvalmodevfp(char **cp, mode_t *d, long bad, int map)
975 {
976 	char	tempmode[ATRSIZ+1];
977 	mode_t	tempmode_t;
978 	int	retval;
979 	int	n;
980 
981 	if ((retval = getstrvfp(cp, NULL, sizeof (tempmode), tempmode)) == 1) {
982 		return (1);
983 	} else if (retval == -1) {
984 		setErrstr(pkg_gt(ERR_MODELONG));
985 		return (2);
986 	}
987 
988 	/*
989 	 * If it isn't a '?' (meaning go with whatever mode is
990 	 * there), validate the mode and convert it to a mode_t. The
991 	 * "bad" variable here is a misnomer. It doesn't necessarily
992 	 * mean bad.
993 	 */
994 	if (tempmode[0] == '?') {
995 		*d = WILDCARD;
996 		return (0);
997 	}
998 
999 	/*
1000 	 * Mode may not be an install parameter or a
1001 	 * non-build parameter.
1002 	 */
1003 
1004 	if (tempmode[0] == '$' &&
1005 	    (isupper(tempmode[1]) || !islower(tempmode[1]))) {
1006 		setErrstr(pkg_gt(ERR_IMODE));
1007 		return (2);
1008 	}
1009 
1010 	if ((map) && (mapvar(mapmode, tempmode))) {
1011 		(void) snprintf(getErrbufAddr(), getErrbufSize(),
1012 				pkg_gt(ERR_NOVAR), maptype, tempmode);
1013 		setErrstr(getErrbufAddr());
1014 		return (2);
1015 	}
1016 
1017 	if (tempmode[0] == '$') {
1018 		*d = BADMODE;	/* may be a problem */
1019 		return (0);
1020 	}
1021 
1022 	/* it's supposed to be something we can convert to a number */
1023 
1024 	n = 0;
1025 
1026 	/* reject it if it contains nonnumbers or it's not octal */
1027 
1028 	while (tempmode[n] && !isspace(tempmode[n])) {
1029 		if (!isdigit(tempmode[n])) {
1030 			setErrstr(pkg_gt(ERR_MODEALPHA));
1031 			return (2);
1032 		}
1033 
1034 		if (strchr("89abcdefABCDEF", tempmode[n])) {
1035 			setErrstr(pkg_gt(ERR_BASEINVAL));
1036 			return (2);
1037 		}
1038 		n++;
1039 	}
1040 
1041 	tempmode_t = strtol(tempmode, NULL, 8);
1042 
1043 	/*
1044 	 * We reject it if it contains inappropriate
1045 	 * bits.
1046 	 */
1047 	if (tempmode_t & (~(S_IAMB | S_ISUID | S_ISGID | S_ISVTX))) {
1048 		if (mapmode == MAPBUILD) {
1049 			setErrstr(pkg_gt(ERR_MODEBITS));
1050 			return (2);
1051 		}
1052 		tempmode_t = bad;
1053 	}
1054 
1055 	*d = tempmode_t;
1056 
1057 	return (0);
1058 }
1059 
1060 int
1061 getnumvfp(char **cp, int base, long *d, long bad)
1062 {
1063 	int c;
1064 	char	*p = *cp;
1065 
1066 	if (*p == '\0') {
1067 		return (0);
1068 	}
1069 
1070 	/* leading white space ignored */
1071 	while (((c = *p) != '\0') && (isspace(*p++)))
1072 		;
1073 	if (c == '?') {
1074 		*d = bad;
1075 		*cp = p;
1076 		return (0);
1077 	}
1078 
1079 	if ((c == '\0') || (c == '\n') || !isdigit(c)) {
1080 		p--;
1081 		*cp = p;
1082 		return (1);
1083 	}
1084 
1085 	*d = 0;
1086 	while (isdigit(c)) {
1087 		*d = (*d * base) + (c & 017);
1088 		c = *p++;
1089 	}
1090 	p--;
1091 	*cp = p;
1092 	return (0);
1093 }
1094 
1095 int
1096 getlnumvfp(char **cp, int base, fsblkcnt_t *d, long bad)
1097 {
1098 	int c;
1099 	char	*p = *cp;
1100 
1101 	if (*p == '\0') {
1102 		return (0);
1103 	}
1104 
1105 	/* leading white space ignored */
1106 	while (((c = *p) != '\0') && (isspace(*p++)))
1107 		;
1108 	if (c == '?') {
1109 		*d = bad;
1110 		*cp = p;
1111 		return (0);
1112 	}
1113 
1114 	if ((c == '\0') || (c == '\n') || !isdigit(c)) {
1115 		p--;
1116 		*cp = p;
1117 		return (1);
1118 	}
1119 
1120 	*d = 0;
1121 	while (isdigit(c)) {
1122 		*d = (*d * base) + (c & 017);
1123 		c = *p++;
1124 	}
1125 	p--;
1126 	*cp = p;
1127 	return (0);
1128 }
1129 
1130 static int
1131 getstrvfp(char **cp, char *sep, int n, char *str)
1132 {
1133 	char	delims[256];
1134 	int	c;
1135 	char	*p = *cp;
1136 	char	*p1;
1137 	size_t	len;
1138 
1139 	if (*p == '\0') {
1140 		return (1);
1141 	}
1142 
1143 	/* leading white space ignored */
1144 
1145 	while (((c = *p) != '\0') && (isspace(*p++)))
1146 		;
1147 	if ((c == '\0') || (c == '\n')) {
1148 		p--;
1149 		*cp = p;
1150 		return (1); /* nothing there */
1151 	}
1152 
1153 	p--;
1154 
1155 	/* generate complete list of delimiters to scan for */
1156 
1157 	(void) strlcpy(delims, " \t\n", sizeof (delims));
1158 	if ((sep != (char *)NULL) && (*sep != '\0')) {
1159 		(void) strlcat(delims, sep, sizeof (delims));
1160 	}
1161 
1162 	/* compute length based on delimiter found or not */
1163 
1164 	p1 = strpbrk(p, delims);
1165 	if (p1 == (char *)NULL) {
1166 		len = strlen(p);
1167 	} else {
1168 		len = (ptrdiff_t)p1 - (ptrdiff_t)p;
1169 	}
1170 
1171 	/* if string will fit in result buffer copy string and return success */
1172 
1173 	if (len < n) {
1174 		(void) memcpy(str, p, len);
1175 		str[len] = '\0';
1176 		p += len;
1177 		*cp = p;
1178 		return (0);
1179 	}
1180 
1181 	/* result buffer too small; copy partial string, return error */
1182 	(void) memcpy(str, p, n-1);
1183 	str[n-1] = '\0';
1184 	p += n;
1185 	*cp = p;
1186 	return (-1);
1187 }
1188 
1189 /*
1190  * Name:	getendvfp
1191  * Description:	Locate the end of the current line given a pointer into a buffer
1192  *		containing characters that is null terminated.
1193  * Arguments:	char **cp - pointer to pointer to null-terminated string buffer
1194  * Returns:	int == 0 -- no non-space characters preceeded the newline
1195  *		    != 0 -- one or more non-space characters preceeded newline
1196  * Effects:	cp is updated to point to the first character PAST the first new
1197  *		line character found. If no newline character is found, cp is
1198  *		updated to point to the '\0' at the end of the buffer.
1199  */
1200 
1201 static int
1202 getendvfp(char **cp)
1203 {
1204 	int	n;
1205 	char	*p = *cp;
1206 
1207 	n = 0;
1208 
1209 	/* if at end of buffer return no more characters left */
1210 
1211 	if (*p == '\0') {
1212 		return (0);
1213 	}
1214 
1215 	/* find the first null or end of line character */
1216 
1217 	while ((*p != '\0') && (*p != '\n')) {
1218 		if (n == 0) {
1219 			if (!isspace(*p)) {
1220 				n++;
1221 			}
1222 		}
1223 		p++;
1224 	}
1225 
1226 	/* if at newline, increment pointer to first character past newline */
1227 
1228 	if (*p == '\n') {
1229 		p++;
1230 	}
1231 
1232 	/* set return pointer to null or first character past newline */
1233 
1234 	*cp = p;
1235 
1236 	/* return space/nospace indicator */
1237 
1238 	return (n);
1239 }
1240 
1241 /*
1242  * Name:	findendvfp
1243  * Description:	Locate the end of the current line given a pointer into a buffer
1244  *		containing characters that is null terminated.
1245  * Arguments:	char **cp - pointer to pointer to null-terminated string buffer
1246  * Returns:	none
1247  * Effects:	cp is updated to point to the first character PAST the first new
1248  *		line character found. If no newline character is found, cp is
1249  *		updated to point to the '\0' at the end of the buffer.
1250  */
1251 
1252 static void
1253 findendvfp(char **cp)
1254 {
1255 	char	*p1;
1256 	char	*p = *cp;
1257 
1258 	/* if at end of buffer return no more characters left */
1259 
1260 	if (*p == '\0') {
1261 		return;
1262 	}
1263 
1264 	/* find the end of the line */
1265 
1266 	p1 = strchr(p, '\n');
1267 	if (p1 != (char *)NULL) {
1268 		*cp = ++p1;
1269 		return;
1270 	}
1271 
1272 	/* no newline found - point to null terminator */
1273 
1274 	*cp = strchr(p, '\0');
1275 }
1276