OpenSolaris_b135/lib/libpkg/common/gpkgmap.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */



#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include "pkgstrct.h"
#include "pkglib.h"
#include "pkglibmsgs.h"
#include "pkglocale.h"

#define	ERR_CANT_READ_LCLPATH		"unable to read local pathname"
#define	ERR_BAD_VOLUME_NUMBER		"bad volume number"
#define	ERR_CANNOT_READ_PATHNAME_FIELD	"unable to read pathname field"
#define	ERR_CANNOT_READ_CONTENT_INFO	"unable to read content info"
#define	ERR_EXTRA_TOKENS_PRESENT	"extra tokens on input line"
#define	ERR_CANNOT_READ_CLASS_TOKEN	"unable to read class token"
#define	ERR_BAD_LINK_SPEC		"missing or invalid link specification"
#define	ERR_UNKNOWN_FTYPE		"unknown ftype"
#define	ERR_NO_LINKSOURCE		"no link source specified"
#define	ERR_CANNOT_READ_MM_DEVNUMS	"unable to read major/minor "\
					"device numbers"
static int	eatwhite(FILE *fp);
static int	getend(FILE *fp);
static int	getstr(FILE *fp, char *sep, int n, char *str);
static int	getnum(FILE *fp, int base, long *d, long bad);
static int	getlnum(FILE *fp, int base, fsblkcnt_t *d, long bad);
static int	getvalmode(FILE *fp, mode_t *d, long bad, int map);

static int	getendvfp(char **cp);
static void	findendvfp(char **cp);
static int	getstrvfp(char **cp, char *sep, int n, char *str);
static int	getvalmodevfp(char **cp, mode_t *d, long bad, int map);
int		getnumvfp(char **cp, int base, long *d, long bad);
int		getlnumvfp(char **cp, int base, fsblkcnt_t *d, long bad);

static char	mypath[PATH_MAX];
static char	mylocal[PATH_MAX];
static int	mapmode = MAPNONE;
static char	*maptype = "";
static mode_t	d_mode = BADMODE;
static char 	*d_owner = BADOWNER;
static char	*d_group = BADGROUP;

/*
 * These determine how gpkgmap() deals with mode, owner and group defaults.
 * It is assumed that the owner and group arguments represent static fields
 * which will persist until attrdefault() is called.
 */
void
attrpreset(int mode, char *owner, char *group)
{
	d_mode = mode;
	d_owner = owner;
	d_group = group;
}

void
attrdefault()
{
	d_mode = NOMODE;
	d_owner = NOOWNER;
	d_group = NOGROUP;
}

/*
 * This determines how gpkgmap() deals with environment variables in the
 * mode, owner and group. Path is evaluated at a higher level based upon
 * other circumstances.
 */
void
setmapmode(int mode)
{
	if (mode >= 0 || mode <= 3) {
		mapmode = mode;
		if (mode == MAPBUILD)
			maptype = " build";
		else if (mode == MAPINSTALL)
			maptype = " install";
		else
			maptype = "";
	}
}

/* This is the external query interface for mapmode. */
int
getmapmode(void)
{
	return (mapmode);
}

/*
 * Unpack the pkgmap or the contents file or whatever file is in that format.
 * Based upon mapmode, environment parameters will be resolved for mode,
 * owner and group.
 */

int
gpkgmap(struct cfent *ept, FILE *fp)
{
	int		c;
	boolean_t	first_char = B_TRUE;

	setErrstr(NULL);
	ept->volno = 0;
	ept->ftype = BADFTYPE;
	(void) strcpy(ept->pkg_class, BADCLASS);
	ept->pkg_class_idx = -1;
	ept->path = NULL;
	ept->ainfo.local = NULL;
	/* default attributes were supplied, so don't reset */
	ept->ainfo.mode = d_mode;
	(void) strcpy(ept->ainfo.owner, d_owner);
	(void) strcpy(ept->ainfo.group, d_group);
#ifdef SUNOS41
	ept->ainfo.xmajor = BADMAJOR;
	ept->ainfo.xminor = BADMINOR;
#else
	ept->ainfo.major = BADMAJOR;
	ept->ainfo.minor = BADMINOR;
#endif
	ept->cinfo.cksum = ept->cinfo.modtime = ept->cinfo.size = (-1L);

	ept->npkgs = 0;

	if (!fp)
		return (-1);
readline:
	c = eatwhite(fp);

	/*
	 * If the first character is not a digit, we assume that the
	 * volume number is 1.
	 */
	if (first_char && !isdigit(c)) {
		ept->volno = 1;
	}
	first_char = B_FALSE;

	switch (c) {
	    case EOF:
		return (0);

	    case '0':
	    case '1':
	    case '2':
	    case '3':
	    case '4':
	    case '5':
	    case '6':
	    case '7':
	    case '8':
	    case '9':
		if (ept->volno) {
			setErrstr(pkg_gt(ERR_BAD_VOLUME_NUMBER));
			goto error;
		}
		do {
			ept->volno = (ept->volno*10)+c-'0';
			c = getc(fp);
		} while (isdigit(c));
		if (ept->volno == 0)
			ept->volno = 1;

		goto readline;

	    case ':':
	    case '#':
		(void) getend(fp);
		/*FALLTHRU*/
	    case '\n':
		/*
		 * Since we are going to scan the next line,
		 * we need to reset volume number and first_char.
		 */
		ept->volno = 0;
		first_char = B_TRUE;
		goto readline;

	    case 'i':
		ept->ftype = (char)c;
		c = eatwhite(fp);
		/*FALLTHRU*/
	    case '.':
	    case '/':
		(void) ungetc(c, fp);

		if (getstr(fp, "=", PATH_MAX, mypath)) {
			setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
			goto error;
		}
		ept->path = mypath;
		c = getc(fp);
		if (c == '=') {
			if (getstr(fp, NULL, PATH_MAX, mylocal)) {
				setErrstr(pkg_gt(ERR_CANT_READ_LCLPATH));
				goto error;
			}
			ept->ainfo.local = mylocal;
		} else
			(void) ungetc(c, fp);

		if (ept->ftype == 'i') {
			/* content info might exist */
			if (!getlnum(fp, 10, (fsblkcnt_t *)&ept->cinfo.size,
			    BADCONT) &&
			    (getnum(fp, 10, (long *)&ept->cinfo.cksum,
			    BADCONT) ||
			    getnum(fp, 10, (long *)&ept->cinfo.modtime,
			    BADCONT))) {
				setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
				goto error;
			}
		}
		if (getend(fp)) {
			setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
			return (-1);
		}
		return (1);

	    case '?':
	    case 'f':
	    case 'v':
	    case 'e':
	    case 'l':
	    case 's':
	    case 'p':
	    case 'c':
	    case 'b':
	    case 'd':
	    case 'x':
		ept->ftype = (char)c;
		if (getstr(fp, NULL, CLSSIZ, ept->pkg_class)) {
			setErrstr(pkg_gt(ERR_CANNOT_READ_CLASS_TOKEN));
			goto error;
		}
		if (getstr(fp, "=", PATH_MAX, mypath)) {
			setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
			goto error;
		}
		ept->path = mypath;

		c = getc(fp);
		if (c == '=') {
			/* local path */
			if (getstr(fp, NULL, PATH_MAX, mylocal)) {
				if (ept->ftype == 's' || ept->ftype == 'l') {
					setErrstr(pkg_gt(ERR_READLINK));
				} else {
					setErrstr(
						pkg_gt(ERR_CANT_READ_LCLPATH));
				}
				goto error;
			}
			ept->ainfo.local = mylocal;
		} else if (strchr("sl", ept->ftype)) {
			if ((c != EOF) && (c != '\n'))
				(void) getend(fp);
			setErrstr(pkg_gt(ERR_BAD_LINK_SPEC));
			return (-1);
		} else
			(void) ungetc(c, fp);
		break;

	    default:
		setErrstr(pkg_gt(ERR_UNKNOWN_FTYPE));
error:
		(void) getend(fp);
		return (-1);
	}

	if (strchr("sl", ept->ftype) && (ept->ainfo.local == NULL)) {
		setErrstr(pkg_gt(ERR_NO_LINKSOURCE));
		goto error;
	}

	if (strchr("cb", ept->ftype)) {
#ifdef SUNOS41
		ept->ainfo.xmajor = BADMAJOR;
		ept->ainfo.xminor = BADMINOR;
		if (getnum(fp, 10, (long *)&ept->ainfo.xmajor, BADMAJOR) ||
		    getnum(fp, 10, (long *)&ept->ainfo.xminor, BADMINOR))
#else
		ept->ainfo.major = BADMAJOR;
		ept->ainfo.minor = BADMINOR;
		if (getnum(fp, 10, (long *)&ept->ainfo.major, BADMAJOR) ||
		    getnum(fp, 10, (long *)&ept->ainfo.minor, BADMINOR))
#endif
		{
			setErrstr(pkg_gt(ERR_CANNOT_READ_MM_DEVNUMS));
			goto error;
		}
	}

	/*
	 * Links and information files don't have attributes associated with
	 * them. The following either resolves potential variables or passes
	 * them through. Mode is tested for validity to some degree. BAD???
	 * is returned to indicate that no meaningful mode was provided. A
	 * higher authority will decide if that's OK or not. CUR??? means that
	 * the prototype file specifically requires a wildcard ('?') for
	 * that entry. We issue an error if attributes were entered wrong.
	 * We just return BAD??? if there was no entry at all.
	 */
	if (strchr("cbdxpfve", ept->ftype)) {
		int retval;

		if ((retval = getvalmode(fp, &(ept->ainfo.mode), CURMODE,
		    (mapmode != MAPNONE))) == 1)
			goto end;	/* nothing else on the line */
		else if (retval == 2)
			goto error;	/* mode is too no good */

		/* owner & group should be here */
		if ((retval = getstr(fp, NULL, ATRSIZ,
		    ept->ainfo.owner)) == 1)
			goto end;	/* no owner or group - warning */
		if (retval == -1) {
			setErrstr(pkg_gt(ERR_OWNTOOLONG));
			goto error;
		}

		if ((retval = getstr(fp, NULL, ATRSIZ,
		    ept->ainfo.group)) == 1)
			goto end;	/* no group - warning */
		if (retval == -1) {
			setErrstr(pkg_gt(ERR_GRPTOOLONG));
			goto error;
		}

		/* Resolve the parameters if required. */
		if (mapmode != MAPNONE) {
			if (mapvar(mapmode, ept->ainfo.owner)) {
				(void) snprintf(getErrbufAddr(),
					getErrbufSize(),
					pkg_gt(ERR_NOVAR),
					maptype, ept->ainfo.owner);
				setErrstr(getErrbufAddr());
				goto error;
			}
			if (mapvar(mapmode, ept->ainfo.group)) {
				(void) snprintf(getErrbufAddr(),
					getErrbufSize(), pkg_gt(ERR_NOVAR),
					maptype, ept->ainfo.group);
				setErrstr(getErrbufAddr());
				goto error;
			}
		}
	}

	if (strchr("ifve", ept->ftype)) {
		/* look for content description */
		if (!getlnum(fp, 10, (fsblkcnt_t *)&ept->cinfo.size, BADCONT) &&
		(getnum(fp, 10, (long *)&ept->cinfo.cksum, BADCONT) ||
		getnum(fp, 10, (long *)&ept->cinfo.modtime, BADCONT))) {
			setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
			goto error;
		}
	}

	if (ept->ftype == 'i')
		goto end;

end:
	if (getend(fp) && ept->pinfo) {
		setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
		return (-1);
	}

done:
	return (1);
}

/*
 * Get and validate the mode attribute. This returns an error if
 *	1. the mode string is too long
 *	2. the mode string includes alpha characters
 *	3. the mode string is not octal
 *	4. mode string is an install parameter
 *	5. mode is an unresolved build parameter and MAPBUILD is
 *	   in effect.
 * If the mode is a build parameter, it is
 *	1. returned as is if MAPNONE is in effect
 *	2. evaluated if MAPBUILD is in effect
 *
 * NOTE : We use "mapmode!=MAPBUILD" to gather that it is install
 * time. At install time we just fix a mode with bad bits set by
 * setting it to CURMODE. This should be an error in a few releases
 * (2.8 maybe) but faulty modes are so common in existing packages
 * that this is a reasonable exception. -- JST 1994-11-9
 *
 * RETURNS
 *	0 if mode is being returned as a valid value
 *	1 if no attributes are present on the line
 *	2 if there was a fundamental error
 */
static int
getvalmode(FILE *fp, mode_t *d, long bad, int map)
{
	char tempmode[20];
	mode_t tempmode_t;
	int retval;

	if ((retval = getstr(fp, NULL, ATRSIZ, tempmode)) == 1)
		return (1);
	else if (retval == -1) {
		setErrstr(pkg_gt(ERR_MODELONG));
		return (2);
	} else {
		/*
		 * If it isn't a '?' (meaning go with whatever mode is
		 * there), validate the mode and convert it to a mode_t. The
		 * "bad" variable here is a misnomer. It doesn't necessarily
		 * mean bad.
		 */
		if (tempmode[0] == '?') {
			*d = WILDCARD;
		} else {
			/*
			 * Mode may not be an install parameter or a
			 * non-build parameter.
			 */
			if (tempmode[0] == '$' &&
			    (isupper(tempmode[1]) || !islower(tempmode[1]))) {
				setErrstr(pkg_gt(ERR_IMODE));
				return (2);
			}

			if ((map) && (mapvar(mapmode, tempmode))) {
				(void) snprintf(getErrbufAddr(),
						getErrbufSize(),
						pkg_gt(ERR_NOVAR),
						maptype, tempmode);
				setErrstr(getErrbufAddr());
				return (2);
			}


			if (tempmode[0] == '$') {
				*d = BADMODE;	/* may be a problem */
			} else {
				/*
				 * At this point it's supposed to be
				 * something we can convert to a number.
				 */
				int n = 0;

				/*
				 * We reject it if it contains nonnumbers or
				 * it's not octal.
				 */
				while (tempmode[n] && !isspace(tempmode[n])) {
					if (!isdigit(tempmode[n])) {
						setErrstr(
							pkg_gt(ERR_MODEALPHA));
						return (2);
					}

					if (strchr("89abcdefABCDEF",
					    tempmode[n])) {
						setErrstr(
							pkg_gt(ERR_BASEINVAL));
						return (2);
					}
					n++;
				}

				tempmode_t = strtol(tempmode, NULL, 8);

				/*
				 * We reject it if it contains inappropriate
				 * bits.
				 */
				if (tempmode_t & ~(S_IAMB |
				    S_ISUID | S_ISGID | S_ISVTX)) {
					if (mapmode != MAPBUILD) {
						tempmode_t = bad;
					} else {
						setErrstr(pkg_gt(ERR_MODEBITS));
						return (2);
					}
				}
				*d = tempmode_t;
			}
		}
		return (0);
	}
}

static int
getnum(FILE *fp, int base, long *d, long bad)
{
	int c, b;

	/* leading white space ignored */
	c = eatwhite(fp);
	if (c == '?') {
		*d = bad;
		return (0);
	}

	if ((c == EOF) || (c == '\n') || !isdigit(c)) {
		(void) ungetc(c, fp);
		return (1);
	}

	*d = 0;
	while (isdigit(c)) {
		b = (c & 017);
		if (b >= base)
			return (2);
		*d = (*d * base) + b;
		c = getc(fp);
	}
	(void) ungetc(c, fp);
	return (0);
}

static int
getlnum(FILE *fp, int base, fsblkcnt_t *d, long bad)
{
	int c, b;

	/* leading white space ignored */
	c = eatwhite(fp);
	if (c == '?') {
		*d = bad;
		return (0);
	}

	if ((c == EOF) || (c == '\n') || !isdigit(c)) {
		(void) ungetc(c, fp);
		return (1);
	}

	*d = 0;
	while (isdigit(c)) {
		b = (c & 017);
		if (b >= base)
			return (2);
		*d = (*d * base) + b;
		c = getc(fp);
	}
	(void) ungetc(c, fp);
	return (0);
}

/*
 *  Get a string from the file. Returns
 *	0 if all OK
 *	1 if nothing there
 *	-1 if string is too long
 */
static int
getstr(FILE *fp, char *sep, int n, char *str)
{
	int c;

	/* leading white space ignored */
	c = eatwhite(fp);
	if ((c == EOF) || (c == '\n')) {
		(void) ungetc(c, fp);
		return (1); /* nothing there */
	}

	/* fill up string until space, tab, or separator */
	while (!strchr(" \t", c) && (!sep || !strchr(sep, c))) {
		if (n-- < 1) {
			*str = '\0';
			return (-1); /* too long */
		}
		*str++ = (char)c;
		c = getc(fp);
		if ((c == EOF) || (c == '\n'))
			break; /* no more on this line */
	}
	*str = '\0';
	(void) ungetc(c, fp);

	return (0);
}

static int
getend(FILE *fp)
{
	int c;
	int n;

	n = 0;
	do {
		if ((c = getc(fp)) == EOF)
			return (n);
		if (!isspace(c))
			n++;
	} while (c != '\n');
	return (n);
}

static int
eatwhite(FILE *fp)
{
	int c;

	/* this test works around a side effect of getc() */
	if (feof(fp))
		return (EOF);
	do
		c = getc(fp);
	while ((c == ' ') || (c == '\t'));
	return (c);
}

int
gpkgmapvfp(struct cfent *ept, VFP_T *vfp)
{
	int		c;
	boolean_t	first_char = B_TRUE;
	(void) strlcpy(ept->pkg_class, BADCLASS, sizeof (ept->pkg_class));
	(void) strlcpy(ept->ainfo.owner, d_owner, sizeof (ept->ainfo.owner));
	(void) strlcpy(ept->ainfo.group, d_group, sizeof (ept->ainfo.group));

	setErrstr(NULL);
	ept->volno = 0;
	ept->ftype = BADFTYPE;
	ept->pkg_class_idx = -1;
	ept->path = NULL;
	ept->ainfo.local = NULL;
	ept->ainfo.mode = d_mode;
	ept->ainfo.major = BADMAJOR;
	ept->ainfo.minor = BADMINOR;
	ept->cinfo.cksum = (-1L);
	ept->cinfo.modtime = (-1L);
	ept->cinfo.size = (-1L);

	ept->npkgs = 0;

	/* return error if no vfp specified */

	if (vfp == (VFP_T *)NULL) {
		return (-1);
	}

readline:
	while (((c = vfpGetcNoInc(vfp)) != '\0') && (isspace(vfpGetc(vfp))))
		;

	/*
	 * If the first character is not a digit, we assume that the
	 * volume number is 1.
	 */
	if (first_char && !isdigit(c)) {
		ept->volno = 1;
	}
	first_char = B_FALSE;

	/*
	 * In case of hsfs the zero-padding of partial pages
	 * returned by mmap is not done properly. A separate bug has been filed
	 * on this.
	 */

	if (vfp->_vfpCurr && (vfp->_vfpCurr > vfp->_vfpEnd)) {
		return (0);
	}

	switch (c) {
	    case '\0':
		return (0);

	    case '0':
	    case '1':
	    case '2':
	    case '3':
	    case '4':
	    case '5':
	    case '6':
	    case '7':
	    case '8':
	    case '9':
		if (ept->volno) {
			setErrstr(pkg_gt(ERR_BAD_VOLUME_NUMBER));
			goto error;
		}
		do {
			ept->volno = (ept->volno*10)+c-'0';
			c = vfpGetc(vfp);
		} while (isdigit(c));
		if (ept->volno == 0) {
			ept->volno = 1;
		}

		goto readline;

	    case ':':
	    case '#':
		(void) findendvfp(&vfpGetCurrCharPtr(vfp));
		/*FALLTHRU*/
	    case '\n':
		/*
		 * Since we are going to scan the next line,
		 * we need to reset volume number and first_char.
		 */
		ept->volno = 0;
		first_char = B_TRUE;
		goto readline;

	    case 'i':
		ept->ftype = (char)c;
		while (((c = vfpGetcNoInc(vfp)) != '\0') &&
						(isspace(vfpGetc(vfp))))
			;
		/*FALLTHRU*/
	    case '.':
	    case '/':
		vfpDecCurrPtr(vfp);

		if (getstrvfp(&vfpGetCurrCharPtr(vfp), "=", PATH_MAX, mypath)) {
			setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
			goto error;
		}
		ept->path = mypath;
		c = vfpGetc(vfp);
		if (c == '=') {
			if (getstrvfp(&vfpGetCurrCharPtr(vfp), NULL, PATH_MAX,
							mylocal)) {
				setErrstr(pkg_gt(ERR_CANT_READ_LCLPATH));
				goto error;
			}
			ept->ainfo.local = mylocal;
		} else {
			vfpDecCurrPtr(vfp);
		}

		if (ept->ftype == 'i') {
			/* content info might exist */
			if (!getlnumvfp(&vfpGetCurrCharPtr(vfp), 10,
				(fsblkcnt_t *)&ept->cinfo.size, BADCONT) &&
			    (getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
				(long *)&ept->cinfo.cksum, BADCONT) ||
			    getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
				(long *)&ept->cinfo.modtime, BADCONT))) {
				setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
				goto error;
			}
		}

		if (getendvfp(&vfpGetCurrCharPtr(vfp))) {
			setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
			return (-1);
		}
		return (1);

	    case '?':
	    case 'f':
	    case 'v':
	    case 'e':
	    case 'l':
	    case 's':
	    case 'p':
	    case 'c':
	    case 'b':
	    case 'd':
	    case 'x':
		ept->ftype = (char)c;
		if (getstrvfp(&vfpGetCurrCharPtr(vfp), NULL,
						CLSSIZ, ept->pkg_class)) {
			setErrstr(pkg_gt(ERR_CANNOT_READ_CLASS_TOKEN));
			goto error;
		}
		if (getstrvfp(&vfpGetCurrCharPtr(vfp), "=", PATH_MAX, mypath)) {
			setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
			goto error;
		}
		ept->path = mypath;

		c = vfpGetc(vfp);
		if (c == '=') {
			/* local path */
			if (getstrvfp(&vfpGetCurrCharPtr(vfp), NULL,
							PATH_MAX, mylocal)) {
				if (ept->ftype == 's' || ept->ftype == 'l') {
					setErrstr(pkg_gt(ERR_READLINK));
				} else {
					setErrstr(
						pkg_gt(ERR_CANT_READ_LCLPATH));
				}
				goto error;
			}
			ept->ainfo.local = mylocal;
		} else if ((ept->ftype == 's') || (ept->ftype == 'l')) {
			if ((c != '\0') && (c != '\n'))
				(void) findendvfp(&vfpGetCurrCharPtr(vfp));
			setErrstr(pkg_gt(ERR_BAD_LINK_SPEC));
			return (-1);
		} else {
			vfpDecCurrPtr(vfp);
		}
		break;

	    default:
		setErrstr(pkg_gt(ERR_UNKNOWN_FTYPE));
error:
		(void) findendvfp(&vfpGetCurrCharPtr(vfp));
		return (-1);
	}

	if (((ept->ftype == 's') || (ept->ftype == 'l')) &&
					(ept->ainfo.local == NULL)) {
		setErrstr(pkg_gt(ERR_NO_LINKSOURCE));
		goto error;
	}

	if (((ept->ftype == 'c') || (ept->ftype == 'b'))) {
		ept->ainfo.major = BADMAJOR;
		ept->ainfo.minor = BADMINOR;

		if (getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
				(long *)&ept->ainfo.major, BADMAJOR) ||
		    getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
				(long *)&ept->ainfo.minor, BADMINOR)) {
			setErrstr(pkg_gt(ERR_CANNOT_READ_MM_DEVNUMS));
			goto error;
		}
	}

	/*
	 * Links and information files don't have attributes associated with
	 * them. The following either resolves potential variables or passes
	 * them through. Mode is tested for validity to some degree. BAD???
	 * is returned to indicate that no meaningful mode was provided. A
	 * higher authority will decide if that's OK or not. CUR??? means that
	 * the prototype file specifically requires a wildcard ('?') for
	 * that entry. We issue an error if attributes were entered wrong.
	 * We just return BAD??? if there was no entry at all.
	 */
	if ((ept->ftype == 'd') || (ept->ftype == 'x') || (ept->ftype == 'c') ||
		(ept->ftype == 'b') || (ept->ftype == 'p') ||
		(ept->ftype == 'f') || (ept->ftype == 'v') ||
		(ept->ftype == 'e')) {
		int retval;

		retval = getvalmodevfp(&vfpGetCurrCharPtr(vfp),
				&(ept->ainfo.mode),
				CURMODE, (mapmode != MAPNONE));

		if (retval == 1) {
			goto end;	/* nothing else on the line */
		} else if (retval == 2) {
			goto error;	/* mode is too no good */
		}

		/* owner & group should be here */
		if ((retval = getstrvfp(&vfpGetCurrCharPtr(vfp), NULL, ATRSIZ,
		    ept->ainfo.owner)) == 1)
			goto end;	/* no owner or group - warning */
		if (retval == -1) {
			setErrstr(pkg_gt(ERR_OWNTOOLONG));
			goto error;
		}

		if ((retval = getstrvfp(&vfpGetCurrCharPtr(vfp), NULL, ATRSIZ,
		    ept->ainfo.group)) == 1)
			goto end;	/* no group - warning */
		if (retval == -1) {
			setErrstr(pkg_gt(ERR_GRPTOOLONG));
			goto error;
		}

		/* Resolve the parameters if required. */
		if (mapmode != MAPNONE) {
			if (mapvar(mapmode, ept->ainfo.owner)) {
				(void) snprintf(getErrbufAddr(),
					getErrbufSize(), pkg_gt(ERR_NOVAR),
					maptype, ept->ainfo.owner);
				setErrstr(getErrbufAddr());
				goto error;
			}
			if (mapvar(mapmode, ept->ainfo.group)) {
				(void) snprintf(getErrbufAddr(),
					getErrbufSize(), pkg_gt(ERR_NOVAR),
					maptype, ept->ainfo.group);
				setErrstr(getErrbufAddr());
				goto error;
			}
		}
	}

	if ((ept->ftype == 'i') || (ept->ftype == 'f') ||
			(ept->ftype == 'v') || (ept->ftype == 'e')) {
		/* look for content description */
		if (!getlnumvfp(&vfpGetCurrCharPtr(vfp), 10,
				(fsblkcnt_t *)&ept->cinfo.size, BADCONT) &&
		(getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
				(long *)&ept->cinfo.cksum, BADCONT) ||
		getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
				(long *)&ept->cinfo.modtime, BADCONT))) {
			setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
			goto error;
		}
	}

	if (ept->ftype == 'i')
		goto end;

end:
	if (getendvfp(&vfpGetCurrCharPtr(vfp)) && ept->pinfo) {
		setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
		return (-1);
	}

done:
	return (1);
}

/*
 * Get and validate the mode attribute. This returns an error if
 *	1. the mode string is too long
 *	2. the mode string includes alpha characters
 *	3. the mode string is not octal
 *	4. mode string is an install parameter
 *	5. mode is an unresolved build parameter and MAPBUILD is
 *	   in effect.
 * If the mode is a build parameter, it is
 *	1. returned as is if MAPNONE is in effect
 *	2. evaluated if MAPBUILD is in effect
 *
 * NOTE : We use "mapmode!=MAPBUILD" to gather that it is install
 * time. At install time we just fix a mode with bad bits set by
 * setting it to CURMODE. This should be an error in a few releases
 * (2.8 maybe) but faulty modes are so common in existing packages
 * that this is a reasonable exception. -- JST 1994-11-9
 *
 * RETURNS
 *	0 if mode is being returned as a valid value
 *	1 if no attributes are present on the line
 *	2 if there was a fundamental error
 */
static int
getvalmodevfp(char **cp, mode_t *d, long bad, int map)
{
	char	tempmode[ATRSIZ+1];
	mode_t	tempmode_t;
	int	retval;
	int	n;

	if ((retval = getstrvfp(cp, NULL, sizeof (tempmode), tempmode)) == 1) {
		return (1);
	} else if (retval == -1) {
		setErrstr(pkg_gt(ERR_MODELONG));
		return (2);
	}

	/*
	 * If it isn't a '?' (meaning go with whatever mode is
	 * there), validate the mode and convert it to a mode_t. The
	 * "bad" variable here is a misnomer. It doesn't necessarily
	 * mean bad.
	 */
	if (tempmode[0] == '?') {
		*d = WILDCARD;
		return (0);
	}

	/*
	 * Mode may not be an install parameter or a
	 * non-build parameter.
	 */

	if (tempmode[0] == '$' &&
	    (isupper(tempmode[1]) || !islower(tempmode[1]))) {
		setErrstr(pkg_gt(ERR_IMODE));
		return (2);
	}

	if ((map) && (mapvar(mapmode, tempmode))) {
		(void) snprintf(getErrbufAddr(), getErrbufSize(),
				pkg_gt(ERR_NOVAR), maptype, tempmode);
		setErrstr(getErrbufAddr());
		return (2);
	}

	if (tempmode[0] == '$') {
		*d = BADMODE;	/* may be a problem */
		return (0);
	}

	/* it's supposed to be something we can convert to a number */

	n = 0;

	/* reject it if it contains nonnumbers or it's not octal */

	while (tempmode[n] && !isspace(tempmode[n])) {
		if (!isdigit(tempmode[n])) {
			setErrstr(pkg_gt(ERR_MODEALPHA));
			return (2);
		}

		if (strchr("89abcdefABCDEF", tempmode[n])) {
			setErrstr(pkg_gt(ERR_BASEINVAL));
			return (2);
		}
		n++;
	}

	tempmode_t = strtol(tempmode, NULL, 8);

	/*
	 * We reject it if it contains inappropriate
	 * bits.
	 */
	if (tempmode_t & (~(S_IAMB | S_ISUID | S_ISGID | S_ISVTX))) {
		if (mapmode == MAPBUILD) {
			setErrstr(pkg_gt(ERR_MODEBITS));
			return (2);
		}
		tempmode_t = bad;
	}

	*d = tempmode_t;

	return (0);
}

int
getnumvfp(char **cp, int base, long *d, long bad)
{
	int c;
	char	*p = *cp;

	if (*p == '\0') {
		return (0);
	}

	/* leading white space ignored */
	while (((c = *p) != '\0') && (isspace(*p++)))
		;
	if (c == '?') {
		*d = bad;
		*cp = p;
		return (0);
	}

	if ((c == '\0') || (c == '\n') || !isdigit(c)) {
		p--;
		*cp = p;
		return (1);
	}

	*d = 0;
	while (isdigit(c)) {
		*d = (*d * base) + (c & 017);
		c = *p++;
	}
	p--;
	*cp = p;
	return (0);
}

int
getlnumvfp(char **cp, int base, fsblkcnt_t *d, long bad)
{
	int c;
	char	*p = *cp;

	if (*p == '\0') {
		return (0);
	}

	/* leading white space ignored */
	while (((c = *p) != '\0') && (isspace(*p++)))
		;
	if (c == '?') {
		*d = bad;
		*cp = p;
		return (0);
	}

	if ((c == '\0') || (c == '\n') || !isdigit(c)) {
		p--;
		*cp = p;
		return (1);
	}

	*d = 0;
	while (isdigit(c)) {
		*d = (*d * base) + (c & 017);
		c = *p++;
	}
	p--;
	*cp = p;
	return (0);
}

static int
getstrvfp(char **cp, char *sep, int n, char *str)
{
	char	delims[256];
	int	c;
	char	*p = *cp;
	char	*p1;
	size_t	len;

	if (*p == '\0') {
		return (1);
	}

	/* leading white space ignored */

	while (((c = *p) != '\0') && (isspace(*p++)))
		;
	if ((c == '\0') || (c == '\n')) {
		p--;
		*cp = p;
		return (1); /* nothing there */
	}

	p--;

	/* generate complete list of delimiters to scan for */

	(void) strlcpy(delims, " \t\n", sizeof (delims));
	if ((sep != (char *)NULL) && (*sep != '\0')) {
		(void) strlcat(delims, sep, sizeof (delims));
	}

	/* compute length based on delimiter found or not */

	p1 = strpbrk(p, delims);
	if (p1 == (char *)NULL) {
		len = strlen(p);
	} else {
		len = (ptrdiff_t)p1 - (ptrdiff_t)p;
	}

	/* if string will fit in result buffer copy string and return success */

	if (len < n) {
		(void) memcpy(str, p, len);
		str[len] = '\0';
		p += len;
		*cp = p;
		return (0);
	}

	/* result buffer too small; copy partial string, return error */
	(void) memcpy(str, p, n-1);
	str[n-1] = '\0';
	p += n;
	*cp = p;
	return (-1);
}

/*
 * Name:	getendvfp
 * Description:	Locate the end of the current line given a pointer into a buffer
 *		containing characters that is null terminated.
 * Arguments:	char **cp - pointer to pointer to null-terminated string buffer
 * Returns:	int == 0 -- no non-space characters preceeded the newline
 *		    != 0 -- one or more non-space characters preceeded newline
 * Effects:	cp is updated to point to the first character PAST the first new
 *		line character found. If no newline character is found, cp is
 *		updated to point to the '\0' at the end of the buffer.
 */

static int
getendvfp(char **cp)
{
	int	n;
	char	*p = *cp;

	n = 0;

	/* if at end of buffer return no more characters left */

	if (*p == '\0') {
		return (0);
	}

	/* find the first null or end of line character */

	while ((*p != '\0') && (*p != '\n')) {
		if (n == 0) {
			if (!isspace(*p)) {
				n++;
			}
		}
		p++;
	}

	/* if at newline, increment pointer to first character past newline */

	if (*p == '\n') {
		p++;
	}

	/* set return pointer to null or first character past newline */

	*cp = p;

	/* return space/nospace indicator */

	return (n);
}

/*
 * Name:	findendvfp
 * Description:	Locate the end of the current line given a pointer into a buffer
 *		containing characters that is null terminated.
 * Arguments:	char **cp - pointer to pointer to null-terminated string buffer
 * Returns:	none
 * Effects:	cp is updated to point to the first character PAST the first new
 *		line character found. If no newline character is found, cp is
 *		updated to point to the '\0' at the end of the buffer.
 */

static void
findendvfp(char **cp)
{
	char	*p1;
	char	*p = *cp;

	/* if at end of buffer return no more characters left */

	if (*p == '\0') {
		return;
	}

	/* find the end of the line */

	p1 = strchr(p, '\n');
	if (p1 != (char *)NULL) {
		*cp = ++p1;
		return;
	}

	/* no newline found - point to null terminator */

	*cp = strchr(p, '\0');
}