PageRenderTime 83ms CodeModel.GetById 14ms app.highlight 61ms RepoModel.GetById 1ms app.codeStats 0ms

/crypto/heimdal/lib/roken/getcap.c

https://bitbucket.org/freebsd/freebsd-head/
C | 1129 lines | 729 code | 96 blank | 304 comment | 281 complexity | 1e55a2b9f9cf348d5c92e8afc9a4c55b MD5 | raw file
   1/*	$NetBSD: getcap.c,v 1.29 1999/03/29 09:27:29 abs Exp $	*/
   2
   3/*-
   4 * Copyright (c) 1992, 1993
   5 *	The Regents of the University of California.  All rights reserved.
   6 *
   7 * This code is derived from software contributed to Berkeley by
   8 * Casey Leedom of Lawrence Livermore National Laboratory.
   9 *
  10 * Redistribution and use in source and binary forms, with or without
  11 * modification, are permitted provided that the following conditions
  12 * are met:
  13 * 1. Redistributions of source code must retain the above copyright
  14 *    notice, this list of conditions and the following disclaimer.
  15 * 2. Redistributions in binary form must reproduce the above copyright
  16 *    notice, this list of conditions and the following disclaimer in the
  17 *    documentation and/or other materials provided with the distribution.
  18 * 3. Neither the name of the University nor the names of its contributors
  19 *    may be used to endorse or promote products derived from this software
  20 *    without specific prior written permission.
  21 *
  22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  32 * SUCH DAMAGE.
  33 */
  34
  35#include <config.h>
  36
  37#include "roken.h"
  38
  39#include <sys/types.h>
  40#include <ctype.h>
  41#if defined(HAVE_DB_185_H)
  42#include <db_185.h>
  43#elif defined(HAVE_DB_H)
  44#include <db.h>
  45#endif
  46#include <errno.h>
  47#include <fcntl.h>
  48#include <limits.h>
  49#include <stdio.h>
  50#include <stdlib.h>
  51#include <string.h>
  52#include <unistd.h>
  53
  54#define	BFRAG		1024
  55#if 0
  56#define	BSIZE		1024
  57#endif
  58#define	ESC		('[' & 037)	/* ASCII ESC */
  59#define	MAX_RECURSION	32		/* maximum getent recursion */
  60#define	SFRAG		100		/* cgetstr mallocs in SFRAG chunks */
  61
  62#define RECOK	(char)0
  63#define TCERR	(char)1
  64#define	SHADOW	(char)2
  65
  66static size_t	 topreclen;	/* toprec length */
  67static char	*toprec;	/* Additional record specified by cgetset() */
  68static int	 gottoprec;	/* Flag indicating retrieval of toprecord */
  69
  70#if 0 /*
  71       * Don't use db support unless it's build into libc but we don't
  72       * check for that now, so just disable the code.
  73       */
  74#if defined(HAVE_DBOPEN) && defined(HAVE_DB_H)
  75#define USE_DB
  76#endif
  77#endif
  78
  79#ifdef USE_DB
  80static int	cdbget (DB *, char **, const char *);
  81#endif
  82static int 	getent (char **, size_t *, char **, int, const char *, int, char *);
  83static int	nfcmp (char *, char *);
  84
  85
  86ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetset(const char *ent);
  87ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL cgetcap(char *buf, const char *cap, int type);
  88ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetent(char **buf, char **db_array, const char *name);
  89ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetmatch(const char *buf, const char *name);
  90ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetclose(void);
  91#if 0
  92int cgetfirst(char **buf, char **db_array);
  93int cgetnext(char **bp, char **db_array);
  94#endif
  95ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetstr(char *buf, const char *cap, char **str);
  96ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetustr(char *buf, const char *cap, char **str);
  97ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetnum(char *buf, const char *cap, long *num);
  98/*
  99 * Cgetset() allows the addition of a user specified buffer to be added
 100 * to the database array, in effect "pushing" the buffer on top of the
 101 * virtual database. 0 is returned on success, -1 on failure.
 102 */
 103ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
 104cgetset(const char *ent)
 105{
 106    const char *source, *check;
 107    char *dest;
 108
 109    if (ent == NULL) {
 110	if (toprec)
 111	    free(toprec);
 112	toprec = NULL;
 113	topreclen = 0;
 114	return (0);
 115    }
 116    topreclen = strlen(ent);
 117    if ((toprec = malloc (topreclen + 1)) == NULL) {
 118	errno = ENOMEM;
 119	return (-1);
 120    }
 121    gottoprec = 0;
 122
 123    source=ent;
 124    dest=toprec;
 125    while (*source) { /* Strip whitespace */
 126	*dest++ = *source++; /* Do not check first field */
 127	while (*source == ':') {
 128	    check=source+1;
 129	    while (*check && (isspace((unsigned char)*check) ||
 130			      (*check=='\\' && isspace((unsigned char)check[1]))))
 131		++check;
 132	    if( *check == ':' )
 133		source=check;
 134	    else
 135		break;
 136
 137	}
 138    }
 139    *dest=0;
 140
 141    return (0);
 142}
 143
 144/*
 145 * Cgetcap searches the capability record buf for the capability cap with
 146 * type `type'.  A pointer to the value of cap is returned on success, NULL
 147 * if the requested capability couldn't be found.
 148 *
 149 * Specifying a type of ':' means that nothing should follow cap (:cap:).
 150 * In this case a pointer to the terminating ':' or NUL will be returned if
 151 * cap is found.
 152 *
 153 * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)
 154 * return NULL.
 155 */
 156ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL
 157cgetcap(char *buf, const char *cap, int type)
 158{
 159    char *bp;
 160    const char *cp;
 161
 162    bp = buf;
 163    for (;;) {
 164	/*
 165	 * Skip past the current capability field - it's either the
 166	 * name field if this is the first time through the loop, or
 167	 * the remainder of a field whose name failed to match cap.
 168	 */
 169	for (;;)
 170	    if (*bp == '\0')
 171		return (NULL);
 172	    else
 173		if (*bp++ == ':')
 174		    break;
 175
 176	/*
 177	 * Try to match (cap, type) in buf.
 178	 */
 179	for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
 180	    continue;
 181	if (*cp != '\0')
 182	    continue;
 183	if (*bp == '@')
 184	    return (NULL);
 185	if (type == ':') {
 186	    if (*bp != '\0' && *bp != ':')
 187		continue;
 188	    return(bp);
 189	}
 190	if (*bp != type)
 191	    continue;
 192	bp++;
 193	return (*bp == '@' ? NULL : bp);
 194    }
 195    /* NOTREACHED */
 196}
 197
 198/*
 199 * Cgetent extracts the capability record name from the NULL terminated file
 200 * array db_array and returns a pointer to a malloc'd copy of it in buf.
 201 * Buf must be retained through all subsequent calls to cgetcap, cgetnum,
 202 * cgetflag, and cgetstr, but may then be free'd.  0 is returned on success,
 203 * -1 if the requested record couldn't be found, -2 if a system error was
 204 * encountered (couldn't open/read a file, etc.), and -3 if a potential
 205 * reference loop is detected.
 206 */
 207ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
 208cgetent(char **buf, char **db_array, const char *name)
 209{
 210    size_t dummy;
 211
 212    return (getent(buf, &dummy, db_array, -1, name, 0, NULL));
 213}
 214
 215/*
 216 * Getent implements the functions of cgetent.  If fd is non-negative,
 217 * *db_array has already been opened and fd is the open file descriptor.  We
 218 * do this to save time and avoid using up file descriptors for tc=
 219 * recursions.
 220 *
 221 * Getent returns the same success/failure codes as cgetent.  On success, a
 222 * pointer to a malloc'ed capability record with all tc= capabilities fully
 223 * expanded and its length (not including trailing ASCII NUL) are left in
 224 * *cap and *len.
 225 *
 226 * Basic algorithm:
 227 *	+ Allocate memory incrementally as needed in chunks of size BFRAG
 228 *	  for capability buffer.
 229 *	+ Recurse for each tc=name and interpolate result.  Stop when all
 230 *	  names interpolated, a name can't be found, or depth exceeds
 231 *	  MAX_RECURSION.
 232 */
 233static int
 234getent(char **cap, size_t *len, char **db_array, int fd,
 235       const char *name, int depth, char *nfield)
 236{
 237    char *r_end, *rp = NULL, **db_p;	/* pacify gcc */
 238    int myfd = 0, eof, foundit;
 239    char *record;
 240    int tc_not_resolved;
 241
 242    /*
 243     * Return with ``loop detected'' error if we've recursed more than
 244     * MAX_RECURSION times.
 245     */
 246    if (depth > MAX_RECURSION)
 247	return (-3);
 248
 249    /*
 250     * Check if we have a top record from cgetset().
 251     */
 252    if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) {
 253	size_t len = topreclen + BFRAG;
 254	if ((record = malloc (len)) == NULL) {
 255	    errno = ENOMEM;
 256	    return (-2);
 257	}
 258	(void)strlcpy(record, toprec, len);
 259	db_p = db_array;
 260	rp = record + topreclen + 1;
 261	r_end = rp + BFRAG;
 262	goto tc_exp;
 263    }
 264    /*
 265     * Allocate first chunk of memory.
 266     */
 267    if ((record = malloc(BFRAG)) == NULL) {
 268	errno = ENOMEM;
 269	return (-2);
 270    }
 271    r_end = record + BFRAG;
 272    foundit = 0;
 273    /*
 274     * Loop through database array until finding the record.
 275     */
 276
 277    for (db_p = db_array; *db_p != NULL; db_p++) {
 278	eof = 0;
 279
 280	/*
 281	 * Open database if not already open.
 282	 */
 283
 284	if (fd >= 0) {
 285	    (void)lseek(fd, (off_t)0, SEEK_SET);
 286	} else {
 287#ifdef USE_DB
 288	    char pbuf[_POSIX_PATH_MAX];
 289	    char *cbuf;
 290	    size_t clen;
 291	    int retval;
 292	    DB *capdbp;
 293
 294	    (void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p);
 295	    if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0))
 296		!= NULL) {
 297		free(record);
 298		retval = cdbget(capdbp, &record, name);
 299		if (retval < 0) {
 300		    /* no record available */
 301		    (void)capdbp->close(capdbp);
 302		    return (retval);
 303		}
 304				/* save the data; close frees it */
 305		clen = strlen(record);
 306		cbuf = malloc(clen + 1);
 307		if (cbuf == NULL)
 308		    return (-2);
 309		memmove(cbuf, record, clen + 1);
 310		if (capdbp->close(capdbp) < 0) {
 311		    free(cbuf);
 312		    return (-2);
 313		}
 314		*len = clen;
 315		*cap = cbuf;
 316		return (retval);
 317	    } else
 318#endif
 319	    {
 320		fd = open(*db_p, O_RDONLY, 0);
 321		if (fd < 0) {
 322		    /* No error on unfound file. */
 323		    continue;
 324		}
 325		myfd = 1;
 326	    }
 327	}
 328	/*
 329	 * Find the requested capability record ...
 330	 */
 331	{
 332	    char buf[BUFSIZ];
 333	    char *b_end, *bp, *cp;
 334	    int c, slash;
 335
 336	    /*
 337	     * Loop invariants:
 338	     *	There is always room for one more character in record.
 339	     *	R_end always points just past end of record.
 340	     *	Rp always points just past last character in record.
 341	     *	B_end always points just past last character in buf.
 342	     *	Bp always points at next character in buf.
 343	     *	Cp remembers where the last colon was.
 344	     */
 345	    b_end = buf;
 346	    bp = buf;
 347	    cp = 0;
 348	    slash = 0;
 349	    for (;;) {
 350
 351		/*
 352		 * Read in a line implementing (\, newline)
 353		 * line continuation.
 354		 */
 355		rp = record;
 356		for (;;) {
 357		    if (bp >= b_end) {
 358			int n;
 359
 360			n = read(fd, buf, sizeof(buf));
 361			if (n <= 0) {
 362			    if (myfd)
 363				(void)close(fd);
 364			    if (n < 0) {
 365				free(record);
 366				return (-2);
 367			    } else {
 368				fd = -1;
 369				eof = 1;
 370				break;
 371			    }
 372			}
 373			b_end = buf+n;
 374			bp = buf;
 375		    }
 376
 377		    c = *bp++;
 378		    if (c == '\n') {
 379			if (slash) {
 380			    slash = 0;
 381			    rp--;
 382			    continue;
 383			} else
 384			    break;
 385		    }
 386		    if (slash) {
 387			slash = 0;
 388			cp = 0;
 389		    }
 390		    if (c == ':') {
 391			/*
 392			 * If the field was `empty' (i.e.
 393			 * contained only white space), back up
 394			 * to the colon (eliminating the
 395			 * field).
 396			 */
 397			if (cp)
 398			    rp = cp;
 399			else
 400			    cp = rp;
 401		    } else if (c == '\\') {
 402			slash = 1;
 403		    } else if (c != ' ' && c != '\t') {
 404			/*
 405			 * Forget where the colon was, as this
 406			 * is not an empty field.
 407			 */
 408			cp = 0;
 409		    }
 410		    *rp++ = c;
 411
 412				/*
 413				 * Enforce loop invariant: if no room
 414				 * left in record buffer, try to get
 415				 * some more.
 416				 */
 417		    if (rp >= r_end) {
 418			u_int pos;
 419			size_t newsize;
 420
 421			pos = rp - record;
 422			newsize = r_end - record + BFRAG;
 423			record = realloc(record, newsize);
 424			if (record == NULL) {
 425			    errno = ENOMEM;
 426			    if (myfd)
 427				(void)close(fd);
 428			    return (-2);
 429			}
 430			r_end = record + newsize;
 431			rp = record + pos;
 432		    }
 433		}
 434		/* Eliminate any white space after the last colon. */
 435		if (cp)
 436		    rp = cp + 1;
 437		/* Loop invariant lets us do this. */
 438		*rp++ = '\0';
 439
 440		/*
 441		 * If encountered eof check next file.
 442		 */
 443		if (eof)
 444		    break;
 445
 446		/*
 447		 * Toss blank lines and comments.
 448		 */
 449		if (*record == '\0' || *record == '#')
 450		    continue;
 451
 452		/*
 453		 * See if this is the record we want ...
 454		 */
 455		if (cgetmatch(record, name) == 0) {
 456		    if (nfield == NULL || !nfcmp(nfield, record)) {
 457			foundit = 1;
 458			break;	/* found it! */
 459		    }
 460		}
 461	    }
 462	}
 463	if (foundit)
 464	    break;
 465    }
 466
 467    if (!foundit)
 468	return (-1);
 469
 470    /*
 471     * Got the capability record, but now we have to expand all tc=name
 472     * references in it ...
 473     */
 474 tc_exp:	{
 475	char *newicap, *s;
 476	size_t ilen, newilen;
 477	int diff, iret, tclen;
 478	char *icap, *scan, *tc, *tcstart, *tcend;
 479
 480	/*
 481	 * Loop invariants:
 482	 *	There is room for one more character in record.
 483	 *	R_end points just past end of record.
 484	 *	Rp points just past last character in record.
 485	 *	Scan points at remainder of record that needs to be
 486	 *	scanned for tc=name constructs.
 487	 */
 488	scan = record;
 489	tc_not_resolved = 0;
 490	for (;;) {
 491	    if ((tc = cgetcap(scan, "tc", '=')) == NULL)
 492		break;
 493
 494	    /*
 495	     * Find end of tc=name and stomp on the trailing `:'
 496	     * (if present) so we can use it to call ourselves.
 497	     */
 498	    s = tc;
 499	    for (;;)
 500		if (*s == '\0')
 501		    break;
 502		else
 503		    if (*s++ == ':') {
 504			*(s - 1) = '\0';
 505			break;
 506		    }
 507	    tcstart = tc - 3;
 508	    tclen = s - tcstart;
 509	    tcend = s;
 510
 511	    iret = getent(&icap, &ilen, db_p, fd, tc, depth+1,
 512			  NULL);
 513	    newicap = icap;		/* Put into a register. */
 514	    newilen = ilen;
 515	    if (iret != 0) {
 516				/* an error */
 517		if (iret < -1) {
 518		    if (myfd)
 519			(void)close(fd);
 520		    free(record);
 521		    return (iret);
 522		}
 523		if (iret == 1)
 524		    tc_not_resolved = 1;
 525				/* couldn't resolve tc */
 526		if (iret == -1) {
 527		    *(s - 1) = ':';
 528		    scan = s - 1;
 529		    tc_not_resolved = 1;
 530		    continue;
 531
 532		}
 533	    }
 534	    /* not interested in name field of tc'ed record */
 535	    s = newicap;
 536	    for (;;)
 537		if (*s == '\0')
 538		    break;
 539		else
 540		    if (*s++ == ':')
 541			break;
 542	    newilen -= s - newicap;
 543	    newicap = s;
 544
 545	    /* make sure interpolated record is `:'-terminated */
 546	    s += newilen;
 547	    if (*(s-1) != ':') {
 548		*s = ':';	/* overwrite NUL with : */
 549		newilen++;
 550	    }
 551
 552	    /*
 553	     * Make sure there's enough room to insert the
 554	     * new record.
 555	     */
 556	    diff = newilen - tclen;
 557	    if (diff >= r_end - rp) {
 558		u_int pos, tcpos, tcposend;
 559		size_t newsize;
 560
 561		pos = rp - record;
 562		newsize = r_end - record + diff + BFRAG;
 563		tcpos = tcstart - record;
 564		tcposend = tcend - record;
 565		record = realloc(record, newsize);
 566		if (record == NULL) {
 567		    errno = ENOMEM;
 568		    if (myfd)
 569			(void)close(fd);
 570		    free(icap);
 571		    return (-2);
 572		}
 573		r_end = record + newsize;
 574		rp = record + pos;
 575		tcstart = record + tcpos;
 576		tcend = record + tcposend;
 577	    }
 578
 579	    /*
 580	     * Insert tc'ed record into our record.
 581	     */
 582	    s = tcstart + newilen;
 583	    memmove(s, tcend,  (size_t)(rp - tcend));
 584	    memmove(tcstart, newicap, newilen);
 585	    rp += diff;
 586	    free(icap);
 587
 588	    /*
 589	     * Start scan on `:' so next cgetcap works properly
 590	     * (cgetcap always skips first field).
 591	     */
 592	    scan = s-1;
 593	}
 594
 595    }
 596    /*
 597     * Close file (if we opened it), give back any extra memory, and
 598     * return capability, length and success.
 599     */
 600    if (myfd)
 601	(void)close(fd);
 602    *len = rp - record - 1;	/* don't count NUL */
 603    if (r_end > rp)
 604	if ((record =
 605	     realloc(record, (size_t)(rp - record))) == NULL) {
 606	    errno = ENOMEM;
 607	    return (-2);
 608	}
 609
 610    *cap = record;
 611    if (tc_not_resolved)
 612	return (1);
 613    return (0);
 614}
 615
 616#ifdef USE_DB
 617static int
 618cdbget(DB *capdbp, char **bp, const char *name)
 619{
 620	DBT key;
 621	DBT data;
 622
 623	/* LINTED key is not modified */
 624	key.data = (char *)name;
 625	key.size = strlen(name);
 626
 627	for (;;) {
 628		/* Get the reference. */
 629		switch(capdbp->get(capdbp, &key, &data, 0)) {
 630		case -1:
 631			return (-2);
 632		case 1:
 633			return (-1);
 634		}
 635
 636		/* If not an index to another record, leave. */
 637		if (((char *)data.data)[0] != SHADOW)
 638			break;
 639
 640		key.data = (char *)data.data + 1;
 641		key.size = data.size - 1;
 642	}
 643
 644	*bp = (char *)data.data + 1;
 645	return (((char *)(data.data))[0] == TCERR ? 1 : 0);
 646}
 647#endif /* USE_DB */
 648
 649/*
 650 * Cgetmatch will return 0 if name is one of the names of the capability
 651 * record buf, -1 if not.
 652 */
 653int
 654cgetmatch(const char *buf, const char *name)
 655{
 656    const char *np, *bp;
 657
 658    /*
 659     * Start search at beginning of record.
 660     */
 661    bp = buf;
 662    for (;;) {
 663	/*
 664	 * Try to match a record name.
 665	 */
 666	np = name;
 667	for (;;)
 668	    if (*np == '\0') {
 669		if (*bp == '|' || *bp == ':' || *bp == '\0')
 670		    return (0);
 671		else
 672		    break;
 673	    } else
 674		if (*bp++ != *np++)
 675		    break;
 676
 677	/*
 678	 * Match failed, skip to next name in record.
 679	 */
 680	bp--;	/* a '|' or ':' may have stopped the match */
 681	for (;;)
 682	    if (*bp == '\0' || *bp == ':')
 683		return (-1);	/* match failed totally */
 684	    else
 685		if (*bp++ == '|')
 686		    break;	/* found next name */
 687    }
 688}
 689
 690#if 0
 691int
 692cgetfirst(char **buf, char **db_array)
 693{
 694    (void)cgetclose();
 695    return (cgetnext(buf, db_array));
 696}
 697#endif
 698
 699static FILE *pfp;
 700static int slash;
 701static char **dbp;
 702
 703ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
 704cgetclose(void)
 705{
 706    if (pfp != NULL) {
 707	(void)fclose(pfp);
 708	pfp = NULL;
 709    }
 710    dbp = NULL;
 711    gottoprec = 0;
 712    slash = 0;
 713    return(0);
 714}
 715
 716#if 0
 717/*
 718 * Cgetnext() gets either the first or next entry in the logical database
 719 * specified by db_array.  It returns 0 upon completion of the database, 1
 720 * upon returning an entry with more remaining, and -1 if an error occurs.
 721 */
 722int
 723cgetnext(char **bp, char **db_array)
 724{
 725    size_t len;
 726    int status, done;
 727    char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE];
 728    size_t dummy;
 729
 730    if (dbp == NULL)
 731	dbp = db_array;
 732
 733    if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) {
 734	(void)cgetclose();
 735	return (-1);
 736    }
 737    for(;;) {
 738	if (toprec && !gottoprec) {
 739	    gottoprec = 1;
 740	    line = toprec;
 741	} else {
 742	    line = fgetln(pfp, &len);
 743	    if (line == NULL && pfp) {
 744		if (ferror(pfp)) {
 745		    (void)cgetclose();
 746		    return (-1);
 747		} else {
 748		    (void)fclose(pfp);
 749		    pfp = NULL;
 750		    if (*++dbp == NULL) {
 751			(void)cgetclose();
 752			return (0);
 753		    } else if ((pfp =
 754				fopen(*dbp, "r")) == NULL) {
 755			(void)cgetclose();
 756			return (-1);
 757		    } else
 758			continue;
 759		}
 760	    } else
 761		line[len - 1] = '\0';
 762	    if (len == 1) {
 763		slash = 0;
 764		continue;
 765	    }
 766	    if (isspace((unsigned char)*line) ||
 767		*line == ':' || *line == '#' || slash) {
 768		if (line[len - 2] == '\\')
 769		    slash = 1;
 770		else
 771		    slash = 0;
 772		continue;
 773	    }
 774	    if (line[len - 2] == '\\')
 775		slash = 1;
 776	    else
 777		slash = 0;
 778	}
 779
 780
 781	/*
 782	 * Line points to a name line.
 783	 */
 784	done = 0;
 785	np = nbuf;
 786	for (;;) {
 787	    for (cp = line; *cp != '\0'; cp++) {
 788		if (*cp == ':') {
 789		    *np++ = ':';
 790		    done = 1;
 791		    break;
 792		}
 793		if (*cp == '\\')
 794		    break;
 795		*np++ = *cp;
 796	    }
 797	    if (done) {
 798		*np = '\0';
 799		break;
 800	    } else { /* name field extends beyond the line */
 801		line = fgetln(pfp, &len);
 802		if (line == NULL && pfp) {
 803		    if (ferror(pfp)) {
 804			(void)cgetclose();
 805			return (-1);
 806		    }
 807		    (void)fclose(pfp);
 808		    pfp = NULL;
 809		    *np = '\0';
 810		    break;
 811		} else
 812		    line[len - 1] = '\0';
 813	    }
 814	}
 815	rp = buf;
 816	for(cp = nbuf; *cp != '\0'; cp++)
 817	    if (*cp == '|' || *cp == ':')
 818		break;
 819	    else
 820		*rp++ = *cp;
 821
 822	*rp = '\0';
 823	/*
 824	 * XXX
 825	 * Last argument of getent here should be nbuf if we want true
 826	 * sequential access in the case of duplicates.
 827	 * With NULL, getent will return the first entry found
 828	 * rather than the duplicate entry record.  This is a
 829	 * matter of semantics that should be resolved.
 830	 */
 831	status = getent(bp, &dummy, db_array, -1, buf, 0, NULL);
 832	if (status == -2 || status == -3)
 833	    (void)cgetclose();
 834
 835	return (status + 1);
 836    }
 837    /* NOTREACHED */
 838}
 839#endif
 840
 841/*
 842 * Cgetstr retrieves the value of the string capability cap from the
 843 * capability record pointed to by buf.  A pointer to a decoded, NUL
 844 * terminated, malloc'd copy of the string is returned in the char *
 845 * pointed to by str.  The length of the string not including the trailing
 846 * NUL is returned on success, -1 if the requested string capability
 847 * couldn't be found, -2 if a system error was encountered (storage
 848 * allocation failure).
 849 */
 850ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
 851cgetstr(char *buf, const char *cap, char **str)
 852{
 853    u_int m_room;
 854    const char *bp;
 855    char *mp;
 856    int len;
 857    char *mem, *nmem;
 858
 859    *str = NULL;
 860
 861    /*
 862     * Find string capability cap
 863     */
 864    bp = cgetcap(buf, cap, '=');
 865    if (bp == NULL)
 866	return (-1);
 867
 868    /*
 869     * Conversion / storage allocation loop ...  Allocate memory in
 870     * chunks SFRAG in size.
 871     */
 872    if ((mem = malloc(SFRAG)) == NULL) {
 873	errno = ENOMEM;
 874	return (-2);	/* couldn't even allocate the first fragment */
 875    }
 876    m_room = SFRAG;
 877    mp = mem;
 878
 879    while (*bp != ':' && *bp != '\0') {
 880	/*
 881	 * Loop invariants:
 882	 *	There is always room for one more character in mem.
 883	 *	Mp always points just past last character in mem.
 884	 *	Bp always points at next character in buf.
 885	 */
 886	if (*bp == '^') {
 887	    bp++;
 888	    if (*bp == ':' || *bp == '\0')
 889		break;	/* drop unfinished escape */
 890	    *mp++ = *bp++ & 037;
 891	} else if (*bp == '\\') {
 892	    bp++;
 893	    if (*bp == ':' || *bp == '\0')
 894		break;	/* drop unfinished escape */
 895	    if ('0' <= *bp && *bp <= '7') {
 896		int n, i;
 897
 898		n = 0;
 899		i = 3;	/* maximum of three octal digits */
 900		do {
 901		    n = n * 8 + (*bp++ - '0');
 902		} while (--i && '0' <= *bp && *bp <= '7');
 903		*mp++ = n;
 904	    }
 905	    else switch (*bp++) {
 906	    case 'b': case 'B':
 907		*mp++ = '\b';
 908		break;
 909	    case 't': case 'T':
 910		*mp++ = '\t';
 911		break;
 912	    case 'n': case 'N':
 913		*mp++ = '\n';
 914		break;
 915	    case 'f': case 'F':
 916		*mp++ = '\f';
 917		break;
 918	    case 'r': case 'R':
 919		*mp++ = '\r';
 920		break;
 921	    case 'e': case 'E':
 922		*mp++ = ESC;
 923		break;
 924	    case 'c': case 'C':
 925		*mp++ = ':';
 926		break;
 927	    default:
 928		/*
 929		 * Catches '\', '^', and
 930		 *  everything else.
 931		 */
 932		*mp++ = *(bp-1);
 933		break;
 934	    }
 935	} else
 936	    *mp++ = *bp++;
 937	m_room--;
 938
 939	/*
 940	 * Enforce loop invariant: if no room left in current
 941	 * buffer, try to get some more.
 942	 */
 943	if (m_room == 0) {
 944	    size_t size = mp - mem;
 945
 946	    if ((nmem = realloc(mem, size + SFRAG)) == NULL) {
 947		free(mem);
 948		return (-2);
 949	    }
 950	    mem = nmem;
 951	    m_room = SFRAG;
 952	    mp = mem + size;
 953	}
 954    }
 955    *mp++ = '\0';	/* loop invariant let's us do this */
 956    m_room--;
 957    len = mp - mem - 1;
 958
 959    /*
 960     * Give back any extra memory and return value and success.
 961     */
 962    if (m_room != 0) {
 963	if ((nmem = realloc(mem, (size_t)(mp - mem))) == NULL) {
 964	    free(mem);
 965	    return (-2);
 966	}
 967	mem = nmem;
 968    }
 969    *str = mem;
 970    return (len);
 971}
 972
 973/*
 974 * Cgetustr retrieves the value of the string capability cap from the
 975 * capability record pointed to by buf.  The difference between cgetustr()
 976 * and cgetstr() is that cgetustr does not decode escapes but rather treats
 977 * all characters literally.  A pointer to a  NUL terminated malloc'd
 978 * copy of the string is returned in the char pointed to by str.  The
 979 * length of the string not including the trailing NUL is returned on success,
 980 * -1 if the requested string capability couldn't be found, -2 if a system
 981 * error was encountered (storage allocation failure).
 982 */
 983ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
 984cgetustr(char *buf, const char *cap, char **str)
 985{
 986    u_int m_room;
 987    const char *bp;
 988    char *mp;
 989    int len;
 990    char *mem;
 991
 992    /*
 993     * Find string capability cap
 994     */
 995    if ((bp = cgetcap(buf, cap, '=')) == NULL)
 996	return (-1);
 997
 998    /*
 999     * Conversion / storage allocation loop ...  Allocate memory in
1000     * chunks SFRAG in size.
1001     */
1002    if ((mem = malloc(SFRAG)) == NULL) {
1003	errno = ENOMEM;
1004	return (-2);	/* couldn't even allocate the first fragment */
1005    }
1006    m_room = SFRAG;
1007    mp = mem;
1008
1009    while (*bp != ':' && *bp != '\0') {
1010	/*
1011	 * Loop invariants:
1012	 *	There is always room for one more character in mem.
1013	 *	Mp always points just past last character in mem.
1014	 *	Bp always points at next character in buf.
1015	 */
1016	*mp++ = *bp++;
1017	m_room--;
1018
1019	/*
1020	 * Enforce loop invariant: if no room left in current
1021	 * buffer, try to get some more.
1022	 */
1023	if (m_room == 0) {
1024	    size_t size = mp - mem;
1025
1026	    if ((mem = realloc(mem, size + SFRAG)) == NULL)
1027		return (-2);
1028	    m_room = SFRAG;
1029	    mp = mem + size;
1030	}
1031    }
1032    *mp++ = '\0';	/* loop invariant let's us do this */
1033    m_room--;
1034    len = mp - mem - 1;
1035
1036    /*
1037     * Give back any extra memory and return value and success.
1038     */
1039    if (m_room != 0)
1040	if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL)
1041	    return (-2);
1042    *str = mem;
1043    return (len);
1044}
1045
1046/*
1047 * Cgetnum retrieves the value of the numeric capability cap from the
1048 * capability record pointed to by buf.  The numeric value is returned in
1049 * the long pointed to by num.  0 is returned on success, -1 if the requested
1050 * numeric capability couldn't be found.
1051 */
1052ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
1053cgetnum(char *buf, const char *cap, long *num)
1054{
1055    long n;
1056    int base, digit;
1057    const char *bp;
1058
1059    /*
1060     * Find numeric capability cap
1061     */
1062    bp = cgetcap(buf, cap, '#');
1063    if (bp == NULL)
1064	return (-1);
1065
1066    /*
1067     * Look at value and determine numeric base:
1068     *	0x... or 0X...	hexadecimal,
1069     * else	0...		octal,
1070     * else			decimal.
1071     */
1072    if (*bp == '0') {
1073	bp++;
1074	if (*bp == 'x' || *bp == 'X') {
1075	    bp++;
1076	    base = 16;
1077	} else
1078	    base = 8;
1079    } else
1080	base = 10;
1081
1082    /*
1083     * Conversion loop ...
1084     */
1085    n = 0;
1086    for (;;) {
1087	if ('0' <= *bp && *bp <= '9')
1088	    digit = *bp - '0';
1089	else if ('a' <= *bp && *bp <= 'f')
1090	    digit = 10 + *bp - 'a';
1091	else if ('A' <= *bp && *bp <= 'F')
1092	    digit = 10 + *bp - 'A';
1093	else
1094	    break;
1095
1096	if (digit >= base)
1097	    break;
1098
1099	n = n * base + digit;
1100	bp++;
1101    }
1102
1103    /*
1104     * Return value and success.
1105     */
1106    *num = n;
1107    return (0);
1108}
1109
1110
1111/*
1112 * Compare name field of record.
1113 */
1114static int
1115nfcmp(char *nf, char *rec)
1116{
1117    char *cp, tmp;
1118    int ret;
1119
1120    for (cp = rec; *cp != ':'; cp++)
1121	;
1122
1123    tmp = *(cp + 1);
1124    *(cp + 1) = '\0';
1125    ret = strcmp(nf, rec);
1126    *(cp + 1) = tmp;
1127
1128    return (ret);
1129}