2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
2N/A/* All Rights Reserved */
2N/A
2N/A
2N/A
2N/A#include <stdio.h>
2N/A#include <limits.h>
2N/A#include <stdlib.h>
2N/A#include <unistd.h>
2N/A#include <string.h>
2N/A#include <ctype.h>
2N/A#include <fcntl.h>
2N/A#include <sys/types.h>
2N/A#include <sys/stat.h>
2N/A#include <errno.h>
2N/A#include "pkgstrct.h"
2N/A#include "pkglib.h"
2N/A#include "pkglibmsgs.h"
2N/A#include "pkglocale.h"
2N/A
2N/A#define ERR_CANT_READ_LCLPATH "unable to read local pathname"
2N/A#define ERR_BAD_VOLUME_NUMBER "bad volume number"
2N/A#define ERR_CANNOT_READ_PATHNAME_FIELD "unable to read pathname field"
2N/A#define ERR_CANNOT_READ_CONTENT_INFO "unable to read content info"
2N/A#define ERR_EXTRA_TOKENS_PRESENT "extra tokens on input line"
2N/A#define ERR_CANNOT_READ_CLASS_TOKEN "unable to read class token"
2N/A#define ERR_BAD_LINK_SPEC "missing or invalid link specification"
2N/A#define ERR_UNKNOWN_FTYPE "unknown ftype"
2N/A#define ERR_NO_LINKSOURCE "no link source specified"
2N/A#define ERR_CANNOT_READ_MM_DEVNUMS "unable to read major/minor "\
2N/A "device numbers"
2N/Astatic int eatwhite(FILE *fp);
2N/Astatic int getend(FILE *fp);
2N/Astatic int getstr(FILE *fp, char *sep, int n, char *str);
2N/Astatic int getnum(FILE *fp, int base, long *d, long bad);
2N/Astatic int getlnum(FILE *fp, int base, fsblkcnt_t *d, long bad);
2N/Astatic int getvalmode(FILE *fp, mode_t *d, long bad, int map);
2N/A
2N/Astatic int getendvfp(char **cp);
2N/Astatic void findendvfp(char **cp);
2N/Astatic int getstrvfp(char **cp, char *sep, int n, char *str);
2N/Astatic int getvalmodevfp(char **cp, mode_t *d, long bad, int map);
2N/Aint getnumvfp(char **cp, int base, long *d, long bad);
2N/Aint getlnumvfp(char **cp, int base, fsblkcnt_t *d, long bad);
2N/A
2N/Astatic char mypath[PATH_MAX];
2N/Astatic char mylocal[PATH_MAX];
2N/Astatic int mapmode = MAPNONE;
2N/Astatic char *maptype = "";
2N/Astatic mode_t d_mode = BADMODE;
2N/Astatic char *d_owner = BADOWNER;
2N/Astatic char *d_group = BADGROUP;
2N/A
2N/A/*
2N/A * These determine how gpkgmap() deals with mode, owner and group defaults.
2N/A * It is assumed that the owner and group arguments represent static fields
2N/A * which will persist until attrdefault() is called.
2N/A */
2N/Avoid
2N/Aattrpreset(int mode, char *owner, char *group)
2N/A{
2N/A d_mode = mode;
2N/A d_owner = owner;
2N/A d_group = group;
2N/A}
2N/A
2N/Avoid
2N/Aattrdefault()
2N/A{
2N/A d_mode = NOMODE;
2N/A d_owner = NOOWNER;
2N/A d_group = NOGROUP;
2N/A}
2N/A
2N/A/*
2N/A * This determines how gpkgmap() deals with environment variables in the
2N/A * mode, owner and group. Path is evaluated at a higher level based upon
2N/A * other circumstances.
2N/A */
2N/Avoid
2N/Asetmapmode(int mode)
2N/A{
2N/A if (mode >= 0 || mode <= 3) {
2N/A mapmode = mode;
2N/A if (mode == MAPBUILD)
2N/A maptype = " build";
2N/A else if (mode == MAPINSTALL)
2N/A maptype = " install";
2N/A else
2N/A maptype = "";
2N/A }
2N/A}
2N/A
2N/A/* This is the external query interface for mapmode. */
2N/Aint
2N/Agetmapmode(void)
2N/A{
2N/A return (mapmode);
2N/A}
2N/A
2N/A/*
2N/A * Unpack the pkgmap or the contents file or whatever file is in that format.
2N/A * Based upon mapmode, environment parameters will be resolved for mode,
2N/A * owner and group.
2N/A */
2N/A
2N/Aint
2N/Agpkgmap(struct cfent *ept, FILE *fp)
2N/A{
2N/A int c;
2N/A boolean_t first_char = B_TRUE;
2N/A
2N/A setErrstr(NULL);
2N/A ept->volno = 0;
2N/A ept->ftype = BADFTYPE;
2N/A (void) strcpy(ept->pkg_class, BADCLASS);
2N/A ept->pkg_class_idx = -1;
2N/A ept->path = NULL;
2N/A ept->ainfo.local = NULL;
2N/A /* default attributes were supplied, so don't reset */
2N/A ept->ainfo.mode = d_mode;
2N/A (void) strcpy(ept->ainfo.owner, d_owner);
2N/A (void) strcpy(ept->ainfo.group, d_group);
2N/A#ifdef SUNOS41
2N/A ept->ainfo.xmajor = BADMAJOR;
2N/A ept->ainfo.xminor = BADMINOR;
2N/A#else
2N/A ept->ainfo.major = BADMAJOR;
2N/A ept->ainfo.minor = BADMINOR;
2N/A#endif
2N/A ept->cinfo.cksum = ept->cinfo.modtime = ept->cinfo.size = (-1L);
2N/A
2N/A ept->npkgs = 0;
2N/A
2N/A if (!fp)
2N/A return (-1);
2N/Areadline:
2N/A c = eatwhite(fp);
2N/A
2N/A /*
2N/A * If the first character is not a digit, we assume that the
2N/A * volume number is 1.
2N/A */
2N/A if (first_char && !isdigit(c)) {
2N/A ept->volno = 1;
2N/A }
2N/A first_char = B_FALSE;
2N/A
2N/A switch (c) {
2N/A case EOF:
2N/A return (0);
2N/A
2N/A case '0':
2N/A case '1':
2N/A case '2':
2N/A case '3':
2N/A case '4':
2N/A case '5':
2N/A case '6':
2N/A case '7':
2N/A case '8':
2N/A case '9':
2N/A if (ept->volno) {
2N/A setErrstr(pkg_gt(ERR_BAD_VOLUME_NUMBER));
2N/A goto error;
2N/A }
2N/A do {
2N/A ept->volno = (ept->volno*10)+c-'0';
2N/A c = getc(fp);
2N/A } while (isdigit(c));
2N/A if (ept->volno == 0)
2N/A ept->volno = 1;
2N/A
2N/A goto readline;
2N/A
2N/A case ':':
2N/A case '#':
2N/A (void) getend(fp);
2N/A /*FALLTHRU*/
2N/A case '\n':
2N/A /*
2N/A * Since we are going to scan the next line,
2N/A * we need to reset volume number and first_char.
2N/A */
2N/A ept->volno = 0;
2N/A first_char = B_TRUE;
2N/A goto readline;
2N/A
2N/A case 'i':
2N/A ept->ftype = (char)c;
2N/A c = eatwhite(fp);
2N/A /*FALLTHRU*/
2N/A case '.':
2N/A case '/':
2N/A (void) ungetc(c, fp);
2N/A
2N/A if (getstr(fp, "=", PATH_MAX, mypath)) {
2N/A setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
2N/A goto error;
2N/A }
2N/A ept->path = mypath;
2N/A c = getc(fp);
2N/A if (c == '=') {
2N/A if (getstr(fp, NULL, PATH_MAX, mylocal)) {
2N/A setErrstr(pkg_gt(ERR_CANT_READ_LCLPATH));
2N/A goto error;
2N/A }
2N/A ept->ainfo.local = mylocal;
2N/A } else
2N/A (void) ungetc(c, fp);
2N/A
2N/A if (ept->ftype == 'i') {
2N/A /* content info might exist */
2N/A if (!getlnum(fp, 10, (fsblkcnt_t *)&ept->cinfo.size,
2N/A BADCONT) &&
2N/A (getnum(fp, 10, (long *)&ept->cinfo.cksum,
2N/A BADCONT) ||
2N/A getnum(fp, 10, (long *)&ept->cinfo.modtime,
2N/A BADCONT))) {
2N/A setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
2N/A goto error;
2N/A }
2N/A }
2N/A if (getend(fp)) {
2N/A setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
2N/A return (-1);
2N/A }
2N/A return (1);
2N/A
2N/A case '?':
2N/A case 'f':
2N/A case 'v':
2N/A case 'e':
2N/A case 'l':
2N/A case 's':
2N/A case 'p':
2N/A case 'c':
2N/A case 'b':
2N/A case 'd':
2N/A case 'x':
2N/A ept->ftype = (char)c;
2N/A if (getstr(fp, NULL, CLSSIZ, ept->pkg_class)) {
2N/A setErrstr(pkg_gt(ERR_CANNOT_READ_CLASS_TOKEN));
2N/A goto error;
2N/A }
2N/A if (getstr(fp, "=", PATH_MAX, mypath)) {
2N/A setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
2N/A goto error;
2N/A }
2N/A ept->path = mypath;
2N/A
2N/A c = getc(fp);
2N/A if (c == '=') {
2N/A /* local path */
2N/A if (getstr(fp, NULL, PATH_MAX, mylocal)) {
2N/A if (ept->ftype == 's' || ept->ftype == 'l') {
2N/A setErrstr(pkg_gt(ERR_READLINK));
2N/A } else {
2N/A setErrstr(
2N/A pkg_gt(ERR_CANT_READ_LCLPATH));
2N/A }
2N/A goto error;
2N/A }
2N/A ept->ainfo.local = mylocal;
2N/A } else if (strchr("sl", ept->ftype)) {
2N/A if ((c != EOF) && (c != '\n'))
2N/A (void) getend(fp);
2N/A setErrstr(pkg_gt(ERR_BAD_LINK_SPEC));
2N/A return (-1);
2N/A } else
2N/A (void) ungetc(c, fp);
2N/A break;
2N/A
2N/A default:
2N/A setErrstr(pkg_gt(ERR_UNKNOWN_FTYPE));
2N/Aerror:
2N/A (void) getend(fp);
2N/A return (-1);
2N/A }
2N/A
2N/A if (strchr("sl", ept->ftype) && (ept->ainfo.local == NULL)) {
2N/A setErrstr(pkg_gt(ERR_NO_LINKSOURCE));
2N/A goto error;
2N/A }
2N/A
2N/A if (strchr("cb", ept->ftype)) {
2N/A#ifdef SUNOS41
2N/A ept->ainfo.xmajor = BADMAJOR;
2N/A ept->ainfo.xminor = BADMINOR;
2N/A if (getnum(fp, 10, (long *)&ept->ainfo.xmajor, BADMAJOR) ||
2N/A getnum(fp, 10, (long *)&ept->ainfo.xminor, BADMINOR))
2N/A#else
2N/A ept->ainfo.major = BADMAJOR;
2N/A ept->ainfo.minor = BADMINOR;
2N/A if (getnum(fp, 10, (long *)&ept->ainfo.major, BADMAJOR) ||
2N/A getnum(fp, 10, (long *)&ept->ainfo.minor, BADMINOR))
2N/A#endif
2N/A {
2N/A setErrstr(pkg_gt(ERR_CANNOT_READ_MM_DEVNUMS));
2N/A goto error;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Links and information files don't have attributes associated with
2N/A * them. The following either resolves potential variables or passes
2N/A * them through. Mode is tested for validity to some degree. BAD???
2N/A * is returned to indicate that no meaningful mode was provided. A
2N/A * higher authority will decide if that's OK or not. CUR??? means that
2N/A * the prototype file specifically requires a wildcard ('?') for
2N/A * that entry. We issue an error if attributes were entered wrong.
2N/A * We just return BAD??? if there was no entry at all.
2N/A */
2N/A if (strchr("cbdxpfve", ept->ftype)) {
2N/A int retval;
2N/A
2N/A if ((retval = getvalmode(fp, &(ept->ainfo.mode), CURMODE,
2N/A (mapmode != MAPNONE))) == 1)
2N/A goto end; /* nothing else on the line */
2N/A else if (retval == 2)
2N/A goto error; /* mode is too no good */
2N/A
2N/A /* owner & group should be here */
2N/A if ((retval = getstr(fp, NULL, ATRSIZ,
2N/A ept->ainfo.owner)) == 1)
2N/A goto end; /* no owner or group - warning */
2N/A if (retval == -1) {
2N/A setErrstr(pkg_gt(ERR_OWNTOOLONG));
2N/A goto error;
2N/A }
2N/A
2N/A if ((retval = getstr(fp, NULL, ATRSIZ,
2N/A ept->ainfo.group)) == 1)
2N/A goto end; /* no group - warning */
2N/A if (retval == -1) {
2N/A setErrstr(pkg_gt(ERR_GRPTOOLONG));
2N/A goto error;
2N/A }
2N/A
2N/A /* Resolve the parameters if required. */
2N/A if (mapmode != MAPNONE) {
2N/A if (mapvar(mapmode, ept->ainfo.owner)) {
2N/A (void) snprintf(getErrbufAddr(),
2N/A getErrbufSize(),
2N/A pkg_gt(ERR_NOVAR),
2N/A maptype, ept->ainfo.owner);
2N/A setErrstr(getErrbufAddr());
2N/A goto error;
2N/A }
2N/A if (mapvar(mapmode, ept->ainfo.group)) {
2N/A (void) snprintf(getErrbufAddr(),
2N/A getErrbufSize(), pkg_gt(ERR_NOVAR),
2N/A maptype, ept->ainfo.group);
2N/A setErrstr(getErrbufAddr());
2N/A goto error;
2N/A }
2N/A }
2N/A }
2N/A
2N/A if (strchr("ifve", ept->ftype)) {
2N/A /* look for content description */
2N/A if (!getlnum(fp, 10, (fsblkcnt_t *)&ept->cinfo.size, BADCONT) &&
2N/A (getnum(fp, 10, (long *)&ept->cinfo.cksum, BADCONT) ||
2N/A getnum(fp, 10, (long *)&ept->cinfo.modtime, BADCONT))) {
2N/A setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
2N/A goto error;
2N/A }
2N/A }
2N/A
2N/A if (ept->ftype == 'i')
2N/A goto end;
2N/A
2N/Aend:
2N/A if (getend(fp) && ept->pinfo) {
2N/A setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
2N/A return (-1);
2N/A }
2N/A
2N/Adone:
2N/A return (1);
2N/A}
2N/A
2N/A/*
2N/A * Get and validate the mode attribute. This returns an error if
2N/A * 1. the mode string is too long
2N/A * 2. the mode string includes alpha characters
2N/A * 3. the mode string is not octal
2N/A * 4. mode string is an install parameter
2N/A * 5. mode is an unresolved build parameter and MAPBUILD is
2N/A * in effect.
2N/A * If the mode is a build parameter, it is
2N/A * 1. returned as is if MAPNONE is in effect
2N/A * 2. evaluated if MAPBUILD is in effect
2N/A *
2N/A * NOTE : We use "mapmode!=MAPBUILD" to gather that it is install
2N/A * time. At install time we just fix a mode with bad bits set by
2N/A * setting it to CURMODE. This should be an error in a few releases
2N/A * (2.8 maybe) but faulty modes are so common in existing packages
2N/A * that this is a reasonable exception. -- JST 1994-11-9
2N/A *
2N/A * RETURNS
2N/A * 0 if mode is being returned as a valid value
2N/A * 1 if no attributes are present on the line
2N/A * 2 if there was a fundamental error
2N/A */
2N/Astatic int
2N/Agetvalmode(FILE *fp, mode_t *d, long bad, int map)
2N/A{
2N/A char tempmode[20];
2N/A mode_t tempmode_t;
2N/A int retval;
2N/A
2N/A if ((retval = getstr(fp, NULL, ATRSIZ, tempmode)) == 1)
2N/A return (1);
2N/A else if (retval == -1) {
2N/A setErrstr(pkg_gt(ERR_MODELONG));
2N/A return (2);
2N/A } else {
2N/A /*
2N/A * If it isn't a '?' (meaning go with whatever mode is
2N/A * there), validate the mode and convert it to a mode_t. The
2N/A * "bad" variable here is a misnomer. It doesn't necessarily
2N/A * mean bad.
2N/A */
2N/A if (tempmode[0] == '?') {
2N/A *d = WILDCARD;
2N/A } else {
2N/A /*
2N/A * Mode may not be an install parameter or a
2N/A * non-build parameter.
2N/A */
2N/A if (tempmode[0] == '$' &&
2N/A (isupper(tempmode[1]) || !islower(tempmode[1]))) {
2N/A setErrstr(pkg_gt(ERR_IMODE));
2N/A return (2);
2N/A }
2N/A
2N/A if ((map) && (mapvar(mapmode, tempmode))) {
2N/A (void) snprintf(getErrbufAddr(),
2N/A getErrbufSize(),
2N/A pkg_gt(ERR_NOVAR),
2N/A maptype, tempmode);
2N/A setErrstr(getErrbufAddr());
2N/A return (2);
2N/A }
2N/A
2N/A
2N/A if (tempmode[0] == '$') {
2N/A *d = BADMODE; /* may be a problem */
2N/A } else {
2N/A /*
2N/A * At this point it's supposed to be
2N/A * something we can convert to a number.
2N/A */
2N/A int n = 0;
2N/A
2N/A /*
2N/A * We reject it if it contains nonnumbers or
2N/A * it's not octal.
2N/A */
2N/A while (tempmode[n] && !isspace(tempmode[n])) {
2N/A if (!isdigit(tempmode[n])) {
2N/A setErrstr(
2N/A pkg_gt(ERR_MODEALPHA));
2N/A return (2);
2N/A }
2N/A
2N/A if (strchr("89abcdefABCDEF",
2N/A tempmode[n])) {
2N/A setErrstr(
2N/A pkg_gt(ERR_BASEINVAL));
2N/A return (2);
2N/A }
2N/A n++;
2N/A }
2N/A
2N/A tempmode_t = strtol(tempmode, NULL, 8);
2N/A
2N/A /*
2N/A * We reject it if it contains inappropriate
2N/A * bits.
2N/A */
2N/A if (tempmode_t & ~(S_IAMB |
2N/A S_ISUID | S_ISGID | S_ISVTX)) {
2N/A if (mapmode != MAPBUILD) {
2N/A tempmode_t = bad;
2N/A } else {
2N/A setErrstr(pkg_gt(ERR_MODEBITS));
2N/A return (2);
2N/A }
2N/A }
2N/A *d = tempmode_t;
2N/A }
2N/A }
2N/A return (0);
2N/A }
2N/A}
2N/A
2N/Astatic int
2N/Agetnum(FILE *fp, int base, long *d, long bad)
2N/A{
2N/A int c, b;
2N/A
2N/A /* leading white space ignored */
2N/A c = eatwhite(fp);
2N/A if (c == '?') {
2N/A *d = bad;
2N/A return (0);
2N/A }
2N/A
2N/A if ((c == EOF) || (c == '\n') || !isdigit(c)) {
2N/A (void) ungetc(c, fp);
2N/A return (1);
2N/A }
2N/A
2N/A *d = 0;
2N/A while (isdigit(c)) {
2N/A b = (c & 017);
2N/A if (b >= base)
2N/A return (2);
2N/A *d = (*d * base) + b;
2N/A c = getc(fp);
2N/A }
2N/A (void) ungetc(c, fp);
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Agetlnum(FILE *fp, int base, fsblkcnt_t *d, long bad)
2N/A{
2N/A int c, b;
2N/A
2N/A /* leading white space ignored */
2N/A c = eatwhite(fp);
2N/A if (c == '?') {
2N/A *d = bad;
2N/A return (0);
2N/A }
2N/A
2N/A if ((c == EOF) || (c == '\n') || !isdigit(c)) {
2N/A (void) ungetc(c, fp);
2N/A return (1);
2N/A }
2N/A
2N/A *d = 0;
2N/A while (isdigit(c)) {
2N/A b = (c & 017);
2N/A if (b >= base)
2N/A return (2);
2N/A *d = (*d * base) + b;
2N/A c = getc(fp);
2N/A }
2N/A (void) ungetc(c, fp);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Get a string from the file. Returns
2N/A * 0 if all OK
2N/A * 1 if nothing there
2N/A * -1 if string is too long
2N/A */
2N/Astatic int
2N/Agetstr(FILE *fp, char *sep, int n, char *str)
2N/A{
2N/A int c;
2N/A
2N/A /* leading white space ignored */
2N/A c = eatwhite(fp);
2N/A if ((c == EOF) || (c == '\n')) {
2N/A (void) ungetc(c, fp);
2N/A return (1); /* nothing there */
2N/A }
2N/A
2N/A /* fill up string until space, tab, or separator */
2N/A while (!strchr(" \t", c) && (!sep || !strchr(sep, c))) {
2N/A if (n-- < 1) {
2N/A *str = '\0';
2N/A return (-1); /* too long */
2N/A }
2N/A *str++ = (char)c;
2N/A c = getc(fp);
2N/A if ((c == EOF) || (c == '\n'))
2N/A break; /* no more on this line */
2N/A }
2N/A *str = '\0';
2N/A (void) ungetc(c, fp);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Agetend(FILE *fp)
2N/A{
2N/A int c;
2N/A int n;
2N/A
2N/A n = 0;
2N/A do {
2N/A if ((c = getc(fp)) == EOF)
2N/A return (n);
2N/A if (!isspace(c))
2N/A n++;
2N/A } while (c != '\n');
2N/A return (n);
2N/A}
2N/A
2N/Astatic int
2N/Aeatwhite(FILE *fp)
2N/A{
2N/A int c;
2N/A
2N/A /* this test works around a side effect of getc() */
2N/A if (feof(fp))
2N/A return (EOF);
2N/A do
2N/A c = getc(fp);
2N/A while ((c == ' ') || (c == '\t'));
2N/A return (c);
2N/A}
2N/A
2N/Aint
2N/Agpkgmapvfp(struct cfent *ept, VFP_T *vfp)
2N/A{
2N/A int c;
2N/A boolean_t first_char = B_TRUE;
2N/A (void) strlcpy(ept->pkg_class, BADCLASS, sizeof (ept->pkg_class));
2N/A (void) strlcpy(ept->ainfo.owner, d_owner, sizeof (ept->ainfo.owner));
2N/A (void) strlcpy(ept->ainfo.group, d_group, sizeof (ept->ainfo.group));
2N/A
2N/A setErrstr(NULL);
2N/A ept->volno = 0;
2N/A ept->ftype = BADFTYPE;
2N/A ept->pkg_class_idx = -1;
2N/A ept->path = NULL;
2N/A ept->ainfo.local = NULL;
2N/A ept->ainfo.mode = d_mode;
2N/A ept->ainfo.major = BADMAJOR;
2N/A ept->ainfo.minor = BADMINOR;
2N/A ept->cinfo.cksum = (-1L);
2N/A ept->cinfo.modtime = (-1L);
2N/A ept->cinfo.size = (-1L);
2N/A
2N/A ept->npkgs = 0;
2N/A
2N/A /* return error if no vfp specified */
2N/A
2N/A if (vfp == (VFP_T *)NULL) {
2N/A return (-1);
2N/A }
2N/A
2N/Areadline:
2N/A while (((c = vfpGetcNoInc(vfp)) != '\0') && (isspace(vfpGetc(vfp))))
2N/A ;
2N/A
2N/A /*
2N/A * If the first character is not a digit, we assume that the
2N/A * volume number is 1.
2N/A */
2N/A if (first_char && !isdigit(c)) {
2N/A ept->volno = 1;
2N/A }
2N/A first_char = B_FALSE;
2N/A
2N/A /*
2N/A * In case of hsfs the zero-padding of partial pages
2N/A * returned by mmap is not done properly. A separate bug has been filed
2N/A * on this.
2N/A */
2N/A
2N/A if (vfp->_vfpCurr && (vfp->_vfpCurr > vfp->_vfpEnd)) {
2N/A return (0);
2N/A }
2N/A
2N/A switch (c) {
2N/A case '\0':
2N/A return (0);
2N/A
2N/A case '0':
2N/A case '1':
2N/A case '2':
2N/A case '3':
2N/A case '4':
2N/A case '5':
2N/A case '6':
2N/A case '7':
2N/A case '8':
2N/A case '9':
2N/A if (ept->volno) {
2N/A setErrstr(pkg_gt(ERR_BAD_VOLUME_NUMBER));
2N/A goto error;
2N/A }
2N/A do {
2N/A ept->volno = (ept->volno*10)+c-'0';
2N/A c = vfpGetc(vfp);
2N/A } while (isdigit(c));
2N/A if (ept->volno == 0) {
2N/A ept->volno = 1;
2N/A }
2N/A
2N/A goto readline;
2N/A
2N/A case ':':
2N/A case '#':
2N/A (void) findendvfp(&vfpGetCurrCharPtr(vfp));
2N/A /*FALLTHRU*/
2N/A case '\n':
2N/A /*
2N/A * Since we are going to scan the next line,
2N/A * we need to reset volume number and first_char.
2N/A */
2N/A ept->volno = 0;
2N/A first_char = B_TRUE;
2N/A goto readline;
2N/A
2N/A case 'i':
2N/A ept->ftype = (char)c;
2N/A while (((c = vfpGetcNoInc(vfp)) != '\0') &&
2N/A (isspace(vfpGetc(vfp))))
2N/A ;
2N/A /*FALLTHRU*/
2N/A case '.':
2N/A case '/':
2N/A vfpDecCurrPtr(vfp);
2N/A
2N/A if (getstrvfp(&vfpGetCurrCharPtr(vfp), "=", PATH_MAX, mypath)) {
2N/A setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
2N/A goto error;
2N/A }
2N/A ept->path = mypath;
2N/A c = vfpGetc(vfp);
2N/A if (c == '=') {
2N/A if (getstrvfp(&vfpGetCurrCharPtr(vfp), NULL, PATH_MAX,
2N/A mylocal)) {
2N/A setErrstr(pkg_gt(ERR_CANT_READ_LCLPATH));
2N/A goto error;
2N/A }
2N/A ept->ainfo.local = mylocal;
2N/A } else {
2N/A vfpDecCurrPtr(vfp);
2N/A }
2N/A
2N/A if (ept->ftype == 'i') {
2N/A /* content info might exist */
2N/A if (!getlnumvfp(&vfpGetCurrCharPtr(vfp), 10,
2N/A (fsblkcnt_t *)&ept->cinfo.size, BADCONT) &&
2N/A (getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
2N/A (long *)&ept->cinfo.cksum, BADCONT) ||
2N/A getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
2N/A (long *)&ept->cinfo.modtime, BADCONT))) {
2N/A setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
2N/A goto error;
2N/A }
2N/A }
2N/A
2N/A if (getendvfp(&vfpGetCurrCharPtr(vfp))) {
2N/A setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
2N/A return (-1);
2N/A }
2N/A return (1);
2N/A
2N/A case '?':
2N/A case 'f':
2N/A case 'v':
2N/A case 'e':
2N/A case 'l':
2N/A case 's':
2N/A case 'p':
2N/A case 'c':
2N/A case 'b':
2N/A case 'd':
2N/A case 'x':
2N/A ept->ftype = (char)c;
2N/A if (getstrvfp(&vfpGetCurrCharPtr(vfp), NULL,
2N/A CLSSIZ, ept->pkg_class)) {
2N/A setErrstr(pkg_gt(ERR_CANNOT_READ_CLASS_TOKEN));
2N/A goto error;
2N/A }
2N/A if (getstrvfp(&vfpGetCurrCharPtr(vfp), "=", PATH_MAX, mypath)) {
2N/A setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
2N/A goto error;
2N/A }
2N/A ept->path = mypath;
2N/A
2N/A c = vfpGetc(vfp);
2N/A if (c == '=') {
2N/A /* local path */
2N/A if (getstrvfp(&vfpGetCurrCharPtr(vfp), NULL,
2N/A PATH_MAX, mylocal)) {
2N/A if (ept->ftype == 's' || ept->ftype == 'l') {
2N/A setErrstr(pkg_gt(ERR_READLINK));
2N/A } else {
2N/A setErrstr(
2N/A pkg_gt(ERR_CANT_READ_LCLPATH));
2N/A }
2N/A goto error;
2N/A }
2N/A ept->ainfo.local = mylocal;
2N/A } else if ((ept->ftype == 's') || (ept->ftype == 'l')) {
2N/A if ((c != '\0') && (c != '\n'))
2N/A (void) findendvfp(&vfpGetCurrCharPtr(vfp));
2N/A setErrstr(pkg_gt(ERR_BAD_LINK_SPEC));
2N/A return (-1);
2N/A } else {
2N/A vfpDecCurrPtr(vfp);
2N/A }
2N/A break;
2N/A
2N/A default:
2N/A setErrstr(pkg_gt(ERR_UNKNOWN_FTYPE));
2N/Aerror:
2N/A (void) findendvfp(&vfpGetCurrCharPtr(vfp));
2N/A return (-1);
2N/A }
2N/A
2N/A if (((ept->ftype == 's') || (ept->ftype == 'l')) &&
2N/A (ept->ainfo.local == NULL)) {
2N/A setErrstr(pkg_gt(ERR_NO_LINKSOURCE));
2N/A goto error;
2N/A }
2N/A
2N/A if (((ept->ftype == 'c') || (ept->ftype == 'b'))) {
2N/A ept->ainfo.major = BADMAJOR;
2N/A ept->ainfo.minor = BADMINOR;
2N/A
2N/A if (getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
2N/A (long *)&ept->ainfo.major, BADMAJOR) ||
2N/A getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
2N/A (long *)&ept->ainfo.minor, BADMINOR)) {
2N/A setErrstr(pkg_gt(ERR_CANNOT_READ_MM_DEVNUMS));
2N/A goto error;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Links and information files don't have attributes associated with
2N/A * them. The following either resolves potential variables or passes
2N/A * them through. Mode is tested for validity to some degree. BAD???
2N/A * is returned to indicate that no meaningful mode was provided. A
2N/A * higher authority will decide if that's OK or not. CUR??? means that
2N/A * the prototype file specifically requires a wildcard ('?') for
2N/A * that entry. We issue an error if attributes were entered wrong.
2N/A * We just return BAD??? if there was no entry at all.
2N/A */
2N/A if ((ept->ftype == 'd') || (ept->ftype == 'x') || (ept->ftype == 'c') ||
2N/A (ept->ftype == 'b') || (ept->ftype == 'p') ||
2N/A (ept->ftype == 'f') || (ept->ftype == 'v') ||
2N/A (ept->ftype == 'e')) {
2N/A int retval;
2N/A
2N/A retval = getvalmodevfp(&vfpGetCurrCharPtr(vfp),
2N/A &(ept->ainfo.mode),
2N/A CURMODE, (mapmode != MAPNONE));
2N/A
2N/A if (retval == 1) {
2N/A goto end; /* nothing else on the line */
2N/A } else if (retval == 2) {
2N/A goto error; /* mode is too no good */
2N/A }
2N/A
2N/A /* owner & group should be here */
2N/A if ((retval = getstrvfp(&vfpGetCurrCharPtr(vfp), NULL, ATRSIZ,
2N/A ept->ainfo.owner)) == 1)
2N/A goto end; /* no owner or group - warning */
2N/A if (retval == -1) {
2N/A setErrstr(pkg_gt(ERR_OWNTOOLONG));
2N/A goto error;
2N/A }
2N/A
2N/A if ((retval = getstrvfp(&vfpGetCurrCharPtr(vfp), NULL, ATRSIZ,
2N/A ept->ainfo.group)) == 1)
2N/A goto end; /* no group - warning */
2N/A if (retval == -1) {
2N/A setErrstr(pkg_gt(ERR_GRPTOOLONG));
2N/A goto error;
2N/A }
2N/A
2N/A /* Resolve the parameters if required. */
2N/A if (mapmode != MAPNONE) {
2N/A if (mapvar(mapmode, ept->ainfo.owner)) {
2N/A (void) snprintf(getErrbufAddr(),
2N/A getErrbufSize(), pkg_gt(ERR_NOVAR),
2N/A maptype, ept->ainfo.owner);
2N/A setErrstr(getErrbufAddr());
2N/A goto error;
2N/A }
2N/A if (mapvar(mapmode, ept->ainfo.group)) {
2N/A (void) snprintf(getErrbufAddr(),
2N/A getErrbufSize(), pkg_gt(ERR_NOVAR),
2N/A maptype, ept->ainfo.group);
2N/A setErrstr(getErrbufAddr());
2N/A goto error;
2N/A }
2N/A }
2N/A }
2N/A
2N/A if ((ept->ftype == 'i') || (ept->ftype == 'f') ||
2N/A (ept->ftype == 'v') || (ept->ftype == 'e')) {
2N/A /* look for content description */
2N/A if (!getlnumvfp(&vfpGetCurrCharPtr(vfp), 10,
2N/A (fsblkcnt_t *)&ept->cinfo.size, BADCONT) &&
2N/A (getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
2N/A (long *)&ept->cinfo.cksum, BADCONT) ||
2N/A getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
2N/A (long *)&ept->cinfo.modtime, BADCONT))) {
2N/A setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
2N/A goto error;
2N/A }
2N/A }
2N/A
2N/A if (ept->ftype == 'i')
2N/A goto end;
2N/A
2N/Aend:
2N/A if (getendvfp(&vfpGetCurrCharPtr(vfp)) && ept->pinfo) {
2N/A setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
2N/A return (-1);
2N/A }
2N/A
2N/Adone:
2N/A return (1);
2N/A}
2N/A
2N/A/*
2N/A * Get and validate the mode attribute. This returns an error if
2N/A * 1. the mode string is too long
2N/A * 2. the mode string includes alpha characters
2N/A * 3. the mode string is not octal
2N/A * 4. mode string is an install parameter
2N/A * 5. mode is an unresolved build parameter and MAPBUILD is
2N/A * in effect.
2N/A * If the mode is a build parameter, it is
2N/A * 1. returned as is if MAPNONE is in effect
2N/A * 2. evaluated if MAPBUILD is in effect
2N/A *
2N/A * NOTE : We use "mapmode!=MAPBUILD" to gather that it is install
2N/A * time. At install time we just fix a mode with bad bits set by
2N/A * setting it to CURMODE. This should be an error in a few releases
2N/A * (2.8 maybe) but faulty modes are so common in existing packages
2N/A * that this is a reasonable exception. -- JST 1994-11-9
2N/A *
2N/A * RETURNS
2N/A * 0 if mode is being returned as a valid value
2N/A * 1 if no attributes are present on the line
2N/A * 2 if there was a fundamental error
2N/A */
2N/Astatic int
2N/Agetvalmodevfp(char **cp, mode_t *d, long bad, int map)
2N/A{
2N/A char tempmode[ATRSIZ+1];
2N/A mode_t tempmode_t;
2N/A int retval;
2N/A int n;
2N/A
2N/A if ((retval = getstrvfp(cp, NULL, sizeof (tempmode), tempmode)) == 1) {
2N/A return (1);
2N/A } else if (retval == -1) {
2N/A setErrstr(pkg_gt(ERR_MODELONG));
2N/A return (2);
2N/A }
2N/A
2N/A /*
2N/A * If it isn't a '?' (meaning go with whatever mode is
2N/A * there), validate the mode and convert it to a mode_t. The
2N/A * "bad" variable here is a misnomer. It doesn't necessarily
2N/A * mean bad.
2N/A */
2N/A if (tempmode[0] == '?') {
2N/A *d = WILDCARD;
2N/A return (0);
2N/A }
2N/A
2N/A /*
2N/A * Mode may not be an install parameter or a
2N/A * non-build parameter.
2N/A */
2N/A
2N/A if (tempmode[0] == '$' &&
2N/A (isupper(tempmode[1]) || !islower(tempmode[1]))) {
2N/A setErrstr(pkg_gt(ERR_IMODE));
2N/A return (2);
2N/A }
2N/A
2N/A if ((map) && (mapvar(mapmode, tempmode))) {
2N/A (void) snprintf(getErrbufAddr(), getErrbufSize(),
2N/A pkg_gt(ERR_NOVAR), maptype, tempmode);
2N/A setErrstr(getErrbufAddr());
2N/A return (2);
2N/A }
2N/A
2N/A if (tempmode[0] == '$') {
2N/A *d = BADMODE; /* may be a problem */
2N/A return (0);
2N/A }
2N/A
2N/A /* it's supposed to be something we can convert to a number */
2N/A
2N/A n = 0;
2N/A
2N/A /* reject it if it contains nonnumbers or it's not octal */
2N/A
2N/A while (tempmode[n] && !isspace(tempmode[n])) {
2N/A if (!isdigit(tempmode[n])) {
2N/A setErrstr(pkg_gt(ERR_MODEALPHA));
2N/A return (2);
2N/A }
2N/A
2N/A if (strchr("89abcdefABCDEF", tempmode[n])) {
2N/A setErrstr(pkg_gt(ERR_BASEINVAL));
2N/A return (2);
2N/A }
2N/A n++;
2N/A }
2N/A
2N/A tempmode_t = strtol(tempmode, NULL, 8);
2N/A
2N/A /*
2N/A * We reject it if it contains inappropriate
2N/A * bits.
2N/A */
2N/A if (tempmode_t & (~(S_IAMB | S_ISUID | S_ISGID | S_ISVTX))) {
2N/A if (mapmode == MAPBUILD) {
2N/A setErrstr(pkg_gt(ERR_MODEBITS));
2N/A return (2);
2N/A }
2N/A tempmode_t = bad;
2N/A }
2N/A
2N/A *d = tempmode_t;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Agetnumvfp(char **cp, int base, long *d, long bad)
2N/A{
2N/A int c;
2N/A char *p = *cp;
2N/A
2N/A if (*p == '\0') {
2N/A return (0);
2N/A }
2N/A
2N/A /* leading white space ignored */
2N/A while (((c = *p) != '\0') && (isspace(*p++)))
2N/A ;
2N/A if (c == '?') {
2N/A *d = bad;
2N/A *cp = p;
2N/A return (0);
2N/A }
2N/A
2N/A if ((c == '\0') || (c == '\n') || !isdigit(c)) {
2N/A p--;
2N/A *cp = p;
2N/A return (1);
2N/A }
2N/A
2N/A *d = 0;
2N/A while (isdigit(c)) {
2N/A *d = (*d * base) + (c & 017);
2N/A c = *p++;
2N/A }
2N/A p--;
2N/A *cp = p;
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Agetlnumvfp(char **cp, int base, fsblkcnt_t *d, long bad)
2N/A{
2N/A int c;
2N/A char *p = *cp;
2N/A
2N/A if (*p == '\0') {
2N/A return (0);
2N/A }
2N/A
2N/A /* leading white space ignored */
2N/A while (((c = *p) != '\0') && (isspace(*p++)))
2N/A ;
2N/A if (c == '?') {
2N/A *d = bad;
2N/A *cp = p;
2N/A return (0);
2N/A }
2N/A
2N/A if ((c == '\0') || (c == '\n') || !isdigit(c)) {
2N/A p--;
2N/A *cp = p;
2N/A return (1);
2N/A }
2N/A
2N/A *d = 0;
2N/A while (isdigit(c)) {
2N/A *d = (*d * base) + (c & 017);
2N/A c = *p++;
2N/A }
2N/A p--;
2N/A *cp = p;
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Agetstrvfp(char **cp, char *sep, int n, char *str)
2N/A{
2N/A char delims[256];
2N/A int c;
2N/A char *p = *cp;
2N/A char *p1;
2N/A size_t len;
2N/A
2N/A if (*p == '\0') {
2N/A return (1);
2N/A }
2N/A
2N/A /* leading white space ignored */
2N/A
2N/A while (((c = *p) != '\0') && (isspace(*p++)))
2N/A ;
2N/A if ((c == '\0') || (c == '\n')) {
2N/A p--;
2N/A *cp = p;
2N/A return (1); /* nothing there */
2N/A }
2N/A
2N/A p--;
2N/A
2N/A /* generate complete list of delimiters to scan for */
2N/A
2N/A (void) strlcpy(delims, " \t\n", sizeof (delims));
2N/A if ((sep != (char *)NULL) && (*sep != '\0')) {
2N/A (void) strlcat(delims, sep, sizeof (delims));
2N/A }
2N/A
2N/A /* compute length based on delimiter found or not */
2N/A
2N/A p1 = strpbrk(p, delims);
2N/A if (p1 == (char *)NULL) {
2N/A len = strlen(p);
2N/A } else {
2N/A len = (ptrdiff_t)p1 - (ptrdiff_t)p;
2N/A }
2N/A
2N/A /* if string will fit in result buffer copy string and return success */
2N/A
2N/A if (len < n) {
2N/A (void) memcpy(str, p, len);
2N/A str[len] = '\0';
2N/A p += len;
2N/A *cp = p;
2N/A return (0);
2N/A }
2N/A
2N/A /* result buffer too small; copy partial string, return error */
2N/A (void) memcpy(str, p, n-1);
2N/A str[n-1] = '\0';
2N/A p += n;
2N/A *cp = p;
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * Name: getendvfp
2N/A * Description: Locate the end of the current line given a pointer into a buffer
2N/A * containing characters that is null terminated.
2N/A * Arguments: char **cp - pointer to pointer to null-terminated string buffer
2N/A * Returns: int == 0 -- no non-space characters preceded the newline
2N/A * != 0 -- one or more non-space characters preceded newline
2N/A * Effects: cp is updated to point to the first character PAST the first new
2N/A * line character found. If no newline character is found, cp is
2N/A * updated to point to the '\0' at the end of the buffer.
2N/A */
2N/A
2N/Astatic int
2N/Agetendvfp(char **cp)
2N/A{
2N/A int n;
2N/A char *p = *cp;
2N/A
2N/A n = 0;
2N/A
2N/A /* if at end of buffer return no more characters left */
2N/A
2N/A if (*p == '\0') {
2N/A return (0);
2N/A }
2N/A
2N/A /* find the first null or end of line character */
2N/A
2N/A while ((*p != '\0') && (*p != '\n')) {
2N/A if (n == 0) {
2N/A if (!isspace(*p)) {
2N/A n++;
2N/A }
2N/A }
2N/A p++;
2N/A }
2N/A
2N/A /* if at newline, increment pointer to first character past newline */
2N/A
2N/A if (*p == '\n') {
2N/A p++;
2N/A }
2N/A
2N/A /* set return pointer to null or first character past newline */
2N/A
2N/A *cp = p;
2N/A
2N/A /* return space/nospace indicator */
2N/A
2N/A return (n);
2N/A}
2N/A
2N/A/*
2N/A * Name: findendvfp
2N/A * Description: Locate the end of the current line given a pointer into a buffer
2N/A * containing characters that is null terminated.
2N/A * Arguments: char **cp - pointer to pointer to null-terminated string buffer
2N/A * Returns: none
2N/A * Effects: cp is updated to point to the first character PAST the first new
2N/A * line character found. If no newline character is found, cp is
2N/A * updated to point to the '\0' at the end of the buffer.
2N/A */
2N/A
2N/Astatic void
2N/Afindendvfp(char **cp)
2N/A{
2N/A char *p1;
2N/A char *p = *cp;
2N/A
2N/A /* if at end of buffer return no more characters left */
2N/A
2N/A if (*p == '\0') {
2N/A return;
2N/A }
2N/A
2N/A /* find the end of the line */
2N/A
2N/A p1 = strchr(p, '\n');
2N/A if (p1 != (char *)NULL) {
2N/A *cp = ++p1;
2N/A return;
2N/A }
2N/A
2N/A /* no newline found - point to null terminator */
2N/A
2N/A *cp = strchr(p, '\0');
2N/A}