/* $NetBSD: getcap.c,v 1.29 1999/03/29 09:27:29 abs Exp $ */12/*-3* Copyright (c) 1992, 19934* The Regents of the University of California. All rights reserved.5*6* This code is derived from software contributed to Berkeley by7* Casey Leedom of Lawrence Livermore National Laboratory.8*9* Redistribution and use in source and binary forms, with or without10* modification, are permitted provided that the following conditions11* are met:12* 1. Redistributions of source code must retain the above copyright13* notice, this list of conditions and the following disclaimer.14* 2. Redistributions in binary form must reproduce the above copyright15* notice, this list of conditions and the following disclaimer in the16* documentation and/or other materials provided with the distribution.17* 3. Neither the name of the University nor the names of its contributors18* may be used to endorse or promote products derived from this software19* without specific prior written permission.20*21* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND22* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE23* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE24* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE25* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL26* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS27* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)28* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT29* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY30* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF31* SUCH DAMAGE.32*/3334#include <config.h>3536#include "roken.h"3738#include <sys/types.h>39#include <ctype.h>40#if defined(HAVE_DB_185_H)41#include <db_185.h>42#elif defined(HAVE_DB_H)43#include <db.h>44#endif45#include <errno.h>46#include <fcntl.h>47#include <limits.h>48#include <stdio.h>49#include <stdlib.h>50#include <string.h>51#include <unistd.h>5253#define BFRAG 102454#if 055#define BSIZE 102456#endif57#define ESC ('[' & 037) /* ASCII ESC */58#define MAX_RECURSION 32 /* maximum getent recursion */59#define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */6061#define RECOK (char)062#define TCERR (char)163#define SHADOW (char)26465static size_t topreclen; /* toprec length */66static char *toprec; /* Additional record specified by cgetset() */67static int gottoprec; /* Flag indicating retrieval of toprecord */6869#if 0 /*70* Don't use db support unless it's build into libc but we don't71* check for that now, so just disable the code.72*/73#if defined(HAVE_DBOPEN) && defined(HAVE_DB_H)74#define USE_DB75#endif76#endif7778#ifdef USE_DB79static int cdbget (DB *, char **, const char *);80#endif81static int getent (char **, size_t *, char **, int, const char *, int, char *);82static int nfcmp (char *, char *);838485ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetset(const char *ent);86ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL cgetcap(char *buf, const char *cap, int type);87ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetent(char **buf, char **db_array, const char *name);88ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetmatch(const char *buf, const char *name);89ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetclose(void);90#if 091int cgetfirst(char **buf, char **db_array);92int cgetnext(char **bp, char **db_array);93#endif94ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetstr(char *buf, const char *cap, char **str);95ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetustr(char *buf, const char *cap, char **str);96ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetnum(char *buf, const char *cap, long *num);97/*98* Cgetset() allows the addition of a user specified buffer to be added99* to the database array, in effect "pushing" the buffer on top of the100* virtual database. 0 is returned on success, -1 on failure.101*/102ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL103cgetset(const char *ent)104{105const char *source, *check;106char *dest;107108if (ent == NULL) {109if (toprec)110free(toprec);111toprec = NULL;112topreclen = 0;113return (0);114}115topreclen = strlen(ent);116if ((toprec = malloc (topreclen + 1)) == NULL) {117errno = ENOMEM;118return (-1);119}120gottoprec = 0;121122source=ent;123dest=toprec;124while (*source) { /* Strip whitespace */125*dest++ = *source++; /* Do not check first field */126while (*source == ':') {127check=source+1;128while (*check && (isspace((unsigned char)*check) ||129(*check=='\\' && isspace((unsigned char)check[1]))))130++check;131if( *check == ':' )132source=check;133else134break;135136}137}138*dest=0;139140return (0);141}142143/*144* Cgetcap searches the capability record buf for the capability cap with145* type `type'. A pointer to the value of cap is returned on success, NULL146* if the requested capability couldn't be found.147*148* Specifying a type of ':' means that nothing should follow cap (:cap:).149* In this case a pointer to the terminating ':' or NUL will be returned if150* cap is found.151*152* If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)153* return NULL.154*/155ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL156cgetcap(char *buf, const char *cap, int type)157{158char *bp;159const char *cp;160161bp = buf;162for (;;) {163/*164* Skip past the current capability field - it's either the165* name field if this is the first time through the loop, or166* the remainder of a field whose name failed to match cap.167*/168for (;;)169if (*bp == '\0')170return (NULL);171else172if (*bp++ == ':')173break;174175/*176* Try to match (cap, type) in buf.177*/178for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)179continue;180if (*cp != '\0')181continue;182if (*bp == '@')183return (NULL);184if (type == ':') {185if (*bp != '\0' && *bp != ':')186continue;187return(bp);188}189if (*bp != type)190continue;191bp++;192return (*bp == '@' ? NULL : bp);193}194/* NOTREACHED */195}196197/*198* Cgetent extracts the capability record name from the NULL terminated file199* array db_array and returns a pointer to a malloc'd copy of it in buf.200* Buf must be retained through all subsequent calls to cgetcap, cgetnum,201* cgetflag, and cgetstr, but may then be free'd. 0 is returned on success,202* -1 if the requested record couldn't be found, -2 if a system error was203* encountered (couldn't open/read a file, etc.), and -3 if a potential204* reference loop is detected.205*/206ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL207cgetent(char **buf, char **db_array, const char *name)208{209size_t dummy;210211return (getent(buf, &dummy, db_array, -1, name, 0, NULL));212}213214/*215* Getent implements the functions of cgetent. If fd is non-negative,216* *db_array has already been opened and fd is the open file descriptor. We217* do this to save time and avoid using up file descriptors for tc=218* recursions.219*220* Getent returns the same success/failure codes as cgetent. On success, a221* pointer to a malloc'ed capability record with all tc= capabilities fully222* expanded and its length (not including trailing ASCII NUL) are left in223* *cap and *len.224*225* Basic algorithm:226* + Allocate memory incrementally as needed in chunks of size BFRAG227* for capability buffer.228* + Recurse for each tc=name and interpolate result. Stop when all229* names interpolated, a name can't be found, or depth exceeds230* MAX_RECURSION.231*/232static int233getent(char **cap, size_t *len, char **db_array, int fd,234const char *name, int depth, char *nfield)235{236char *r_end, *rp = NULL, **db_p; /* pacify gcc */237int myfd = 0, eof, foundit;238char *record;239int tc_not_resolved;240241/*242* Return with ``loop detected'' error if we've recursed more than243* MAX_RECURSION times.244*/245if (depth > MAX_RECURSION)246return (-3);247248/*249* Check if we have a top record from cgetset().250*/251if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) {252size_t len = topreclen + BFRAG;253if ((record = malloc (len)) == NULL) {254errno = ENOMEM;255return (-2);256}257(void)strlcpy(record, toprec, len);258db_p = db_array;259rp = record + topreclen + 1;260r_end = rp + BFRAG;261goto tc_exp;262}263/*264* Allocate first chunk of memory.265*/266if ((record = malloc(BFRAG)) == NULL) {267errno = ENOMEM;268return (-2);269}270r_end = record + BFRAG;271foundit = 0;272/*273* Loop through database array until finding the record.274*/275276for (db_p = db_array; *db_p != NULL; db_p++) {277eof = 0;278279/*280* Open database if not already open.281*/282283if (fd >= 0) {284(void)lseek(fd, (off_t)0, SEEK_SET);285} else {286#ifdef USE_DB287char pbuf[_POSIX_PATH_MAX];288char *cbuf;289size_t clen;290int retval;291DB *capdbp;292293(void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p);294if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0))295!= NULL) {296free(record);297retval = cdbget(capdbp, &record, name);298if (retval < 0) {299/* no record available */300(void)capdbp->close(capdbp);301return (retval);302}303/* save the data; close frees it */304clen = strlen(record);305cbuf = malloc(clen + 1);306if (cbuf == NULL)307return (-2);308memmove(cbuf, record, clen + 1);309if (capdbp->close(capdbp) < 0) {310free(cbuf);311return (-2);312}313*len = clen;314*cap = cbuf;315return (retval);316} else317#endif318{319fd = open(*db_p, O_RDONLY, 0);320if (fd < 0) {321/* No error on unfound file. */322continue;323}324myfd = 1;325}326}327/*328* Find the requested capability record ...329*/330{331char buf[BUFSIZ];332char *b_end, *bp, *cp;333int c, slash;334335/*336* Loop invariants:337* There is always room for one more character in record.338* R_end always points just past end of record.339* Rp always points just past last character in record.340* B_end always points just past last character in buf.341* Bp always points at next character in buf.342* Cp remembers where the last colon was.343*/344b_end = buf;345bp = buf;346cp = 0;347slash = 0;348for (;;) {349350/*351* Read in a line implementing (\, newline)352* line continuation.353*/354rp = record;355for (;;) {356if (bp >= b_end) {357int n;358359n = read(fd, buf, sizeof(buf));360if (n <= 0) {361if (myfd)362(void)close(fd);363if (n < 0) {364free(record);365return (-2);366} else {367fd = -1;368eof = 1;369break;370}371}372b_end = buf+n;373bp = buf;374}375376c = *bp++;377if (c == '\n') {378if (slash) {379slash = 0;380rp--;381continue;382} else383break;384}385if (slash) {386slash = 0;387cp = 0;388}389if (c == ':') {390/*391* If the field was `empty' (i.e.392* contained only white space), back up393* to the colon (eliminating the394* field).395*/396if (cp)397rp = cp;398else399cp = rp;400} else if (c == '\\') {401slash = 1;402} else if (c != ' ' && c != '\t') {403/*404* Forget where the colon was, as this405* is not an empty field.406*/407cp = 0;408}409*rp++ = c;410411/*412* Enforce loop invariant: if no room413* left in record buffer, try to get414* some more.415*/416if (rp >= r_end) {417u_int pos;418size_t newsize;419420pos = rp - record;421newsize = r_end - record + BFRAG;422record = realloc(record, newsize);423if (record == NULL) {424errno = ENOMEM;425if (myfd)426(void)close(fd);427return (-2);428}429r_end = record + newsize;430rp = record + pos;431}432}433/* Eliminate any white space after the last colon. */434if (cp)435rp = cp + 1;436/* Loop invariant lets us do this. */437*rp++ = '\0';438439/*440* If encountered eof check next file.441*/442if (eof)443break;444445/*446* Toss blank lines and comments.447*/448if (*record == '\0' || *record == '#')449continue;450451/*452* See if this is the record we want ...453*/454if (cgetmatch(record, name) == 0) {455if (nfield == NULL || !nfcmp(nfield, record)) {456foundit = 1;457break; /* found it! */458}459}460}461}462if (foundit)463break;464}465466if (!foundit)467return (-1);468469/*470* Got the capability record, but now we have to expand all tc=name471* references in it ...472*/473tc_exp: {474char *newicap, *s;475size_t ilen, newilen;476int diff, iret, tclen;477char *icap, *scan, *tc, *tcstart, *tcend;478479/*480* Loop invariants:481* There is room for one more character in record.482* R_end points just past end of record.483* Rp points just past last character in record.484* Scan points at remainder of record that needs to be485* scanned for tc=name constructs.486*/487scan = record;488tc_not_resolved = 0;489for (;;) {490if ((tc = cgetcap(scan, "tc", '=')) == NULL)491break;492493/*494* Find end of tc=name and stomp on the trailing `:'495* (if present) so we can use it to call ourselves.496*/497s = tc;498for (;;)499if (*s == '\0')500break;501else502if (*s++ == ':') {503*(s - 1) = '\0';504break;505}506tcstart = tc - 3;507tclen = s - tcstart;508tcend = s;509510iret = getent(&icap, &ilen, db_p, fd, tc, depth+1,511NULL);512newicap = icap; /* Put into a register. */513newilen = ilen;514if (iret != 0) {515/* an error */516if (iret < -1) {517if (myfd)518(void)close(fd);519free(record);520return (iret);521}522if (iret == 1)523tc_not_resolved = 1;524/* couldn't resolve tc */525if (iret == -1) {526*(s - 1) = ':';527scan = s - 1;528tc_not_resolved = 1;529continue;530531}532}533/* not interested in name field of tc'ed record */534s = newicap;535for (;;)536if (*s == '\0')537break;538else539if (*s++ == ':')540break;541newilen -= s - newicap;542newicap = s;543544/* make sure interpolated record is `:'-terminated */545s += newilen;546if (*(s-1) != ':') {547*s = ':'; /* overwrite NUL with : */548newilen++;549}550551/*552* Make sure there's enough room to insert the553* new record.554*/555diff = newilen - tclen;556if (diff >= r_end - rp) {557u_int pos, tcpos, tcposend;558size_t newsize;559560pos = rp - record;561newsize = r_end - record + diff + BFRAG;562tcpos = tcstart - record;563tcposend = tcend - record;564record = realloc(record, newsize);565if (record == NULL) {566errno = ENOMEM;567if (myfd)568(void)close(fd);569free(icap);570return (-2);571}572r_end = record + newsize;573rp = record + pos;574tcstart = record + tcpos;575tcend = record + tcposend;576}577578/*579* Insert tc'ed record into our record.580*/581s = tcstart + newilen;582memmove(s, tcend, (size_t)(rp - tcend));583memmove(tcstart, newicap, newilen);584rp += diff;585free(icap);586587/*588* Start scan on `:' so next cgetcap works properly589* (cgetcap always skips first field).590*/591scan = s-1;592}593594}595/*596* Close file (if we opened it), give back any extra memory, and597* return capability, length and success.598*/599if (myfd)600(void)close(fd);601*len = rp - record - 1; /* don't count NUL */602if (r_end > rp)603if ((record =604realloc(record, (size_t)(rp - record))) == NULL) {605errno = ENOMEM;606return (-2);607}608609*cap = record;610if (tc_not_resolved)611return (1);612return (0);613}614615#ifdef USE_DB616static int617cdbget(DB *capdbp, char **bp, const char *name)618{619DBT key;620DBT data;621622/* LINTED key is not modified */623key.data = (char *)name;624key.size = strlen(name);625626for (;;) {627/* Get the reference. */628switch(capdbp->get(capdbp, &key, &data, 0)) {629case -1:630return (-2);631case 1:632return (-1);633}634635/* If not an index to another record, leave. */636if (((char *)data.data)[0] != SHADOW)637break;638639key.data = (char *)data.data + 1;640key.size = data.size - 1;641}642643*bp = (char *)data.data + 1;644return (((char *)(data.data))[0] == TCERR ? 1 : 0);645}646#endif /* USE_DB */647648/*649* Cgetmatch will return 0 if name is one of the names of the capability650* record buf, -1 if not.651*/652int653cgetmatch(const char *buf, const char *name)654{655const char *np, *bp;656657/*658* Start search at beginning of record.659*/660bp = buf;661for (;;) {662/*663* Try to match a record name.664*/665np = name;666for (;;)667if (*np == '\0') {668if (*bp == '|' || *bp == ':' || *bp == '\0')669return (0);670else671break;672} else673if (*bp++ != *np++)674break;675676/*677* Match failed, skip to next name in record.678*/679bp--; /* a '|' or ':' may have stopped the match */680for (;;)681if (*bp == '\0' || *bp == ':')682return (-1); /* match failed totally */683else684if (*bp++ == '|')685break; /* found next name */686}687}688689#if 0690int691cgetfirst(char **buf, char **db_array)692{693(void)cgetclose();694return (cgetnext(buf, db_array));695}696#endif697698static FILE *pfp;699static int slash;700static char **dbp;701702ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL703cgetclose(void)704{705if (pfp != NULL) {706(void)fclose(pfp);707pfp = NULL;708}709dbp = NULL;710gottoprec = 0;711slash = 0;712return(0);713}714715#if 0716/*717* Cgetnext() gets either the first or next entry in the logical database718* specified by db_array. It returns 0 upon completion of the database, 1719* upon returning an entry with more remaining, and -1 if an error occurs.720*/721int722cgetnext(char **bp, char **db_array)723{724size_t len;725int status, done;726char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE];727size_t dummy;728729if (dbp == NULL)730dbp = db_array;731732if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) {733(void)cgetclose();734return (-1);735}736for(;;) {737if (toprec && !gottoprec) {738gottoprec = 1;739line = toprec;740} else {741line = fgetln(pfp, &len);742if (line == NULL && pfp) {743if (ferror(pfp)) {744(void)cgetclose();745return (-1);746} else {747(void)fclose(pfp);748pfp = NULL;749if (*++dbp == NULL) {750(void)cgetclose();751return (0);752} else if ((pfp =753fopen(*dbp, "r")) == NULL) {754(void)cgetclose();755return (-1);756} else757continue;758}759} else760line[len - 1] = '\0';761if (len == 1) {762slash = 0;763continue;764}765if (isspace((unsigned char)*line) ||766*line == ':' || *line == '#' || slash) {767if (line[len - 2] == '\\')768slash = 1;769else770slash = 0;771continue;772}773if (line[len - 2] == '\\')774slash = 1;775else776slash = 0;777}778779780/*781* Line points to a name line.782*/783done = 0;784np = nbuf;785for (;;) {786for (cp = line; *cp != '\0'; cp++) {787if (*cp == ':') {788*np++ = ':';789done = 1;790break;791}792if (*cp == '\\')793break;794*np++ = *cp;795}796if (done) {797*np = '\0';798break;799} else { /* name field extends beyond the line */800line = fgetln(pfp, &len);801if (line == NULL && pfp) {802if (ferror(pfp)) {803(void)cgetclose();804return (-1);805}806(void)fclose(pfp);807pfp = NULL;808*np = '\0';809break;810} else811line[len - 1] = '\0';812}813}814rp = buf;815for(cp = nbuf; *cp != '\0'; cp++)816if (*cp == '|' || *cp == ':')817break;818else819*rp++ = *cp;820821*rp = '\0';822/*823* XXX824* Last argument of getent here should be nbuf if we want true825* sequential access in the case of duplicates.826* With NULL, getent will return the first entry found827* rather than the duplicate entry record. This is a828* matter of semantics that should be resolved.829*/830status = getent(bp, &dummy, db_array, -1, buf, 0, NULL);831if (status == -2 || status == -3)832(void)cgetclose();833834return (status + 1);835}836/* NOTREACHED */837}838#endif839840/*841* Cgetstr retrieves the value of the string capability cap from the842* capability record pointed to by buf. A pointer to a decoded, NUL843* terminated, malloc'd copy of the string is returned in the char *844* pointed to by str. The length of the string not including the trailing845* NUL is returned on success, -1 if the requested string capability846* couldn't be found, -2 if a system error was encountered (storage847* allocation failure).848*/849ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL850cgetstr(char *buf, const char *cap, char **str)851{852u_int m_room;853const char *bp;854char *mp;855int len;856char *mem, *nmem;857858*str = NULL;859860/*861* Find string capability cap862*/863bp = cgetcap(buf, cap, '=');864if (bp == NULL)865return (-1);866867/*868* Conversion / storage allocation loop ... Allocate memory in869* chunks SFRAG in size.870*/871if ((mem = malloc(SFRAG)) == NULL) {872errno = ENOMEM;873return (-2); /* couldn't even allocate the first fragment */874}875m_room = SFRAG;876mp = mem;877878while (*bp != ':' && *bp != '\0') {879/*880* Loop invariants:881* There is always room for one more character in mem.882* Mp always points just past last character in mem.883* Bp always points at next character in buf.884*/885if (*bp == '^') {886bp++;887if (*bp == ':' || *bp == '\0')888break; /* drop unfinished escape */889*mp++ = *bp++ & 037;890} else if (*bp == '\\') {891bp++;892if (*bp == ':' || *bp == '\0')893break; /* drop unfinished escape */894if ('0' <= *bp && *bp <= '7') {895int n, i;896897n = 0;898i = 3; /* maximum of three octal digits */899do {900n = n * 8 + (*bp++ - '0');901} while (--i && '0' <= *bp && *bp <= '7');902*mp++ = n;903}904else switch (*bp++) {905case 'b': case 'B':906*mp++ = '\b';907break;908case 't': case 'T':909*mp++ = '\t';910break;911case 'n': case 'N':912*mp++ = '\n';913break;914case 'f': case 'F':915*mp++ = '\f';916break;917case 'r': case 'R':918*mp++ = '\r';919break;920case 'e': case 'E':921*mp++ = ESC;922break;923case 'c': case 'C':924*mp++ = ':';925break;926default:927/*928* Catches '\', '^', and929* everything else.930*/931*mp++ = *(bp-1);932break;933}934} else935*mp++ = *bp++;936m_room--;937938/*939* Enforce loop invariant: if no room left in current940* buffer, try to get some more.941*/942if (m_room == 0) {943size_t size = mp - mem;944945if ((nmem = realloc(mem, size + SFRAG)) == NULL) {946free(mem);947return (-2);948}949mem = nmem;950m_room = SFRAG;951mp = mem + size;952}953}954*mp++ = '\0'; /* loop invariant let's us do this */955m_room--;956len = mp - mem - 1;957958/*959* Give back any extra memory and return value and success.960*/961if (m_room != 0) {962if ((nmem = realloc(mem, (size_t)(mp - mem))) == NULL) {963free(mem);964return (-2);965}966mem = nmem;967}968*str = mem;969return (len);970}971972/*973* Cgetustr retrieves the value of the string capability cap from the974* capability record pointed to by buf. The difference between cgetustr()975* and cgetstr() is that cgetustr does not decode escapes but rather treats976* all characters literally. A pointer to a NUL terminated malloc'd977* copy of the string is returned in the char pointed to by str. The978* length of the string not including the trailing NUL is returned on success,979* -1 if the requested string capability couldn't be found, -2 if a system980* error was encountered (storage allocation failure).981*/982ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL983cgetustr(char *buf, const char *cap, char **str)984{985u_int m_room;986const char *bp;987char *mp;988int len;989char *mem;990991/*992* Find string capability cap993*/994if ((bp = cgetcap(buf, cap, '=')) == NULL)995return (-1);996997/*998* Conversion / storage allocation loop ... Allocate memory in999* chunks SFRAG in size.1000*/1001if ((mem = malloc(SFRAG)) == NULL) {1002errno = ENOMEM;1003return (-2); /* couldn't even allocate the first fragment */1004}1005m_room = SFRAG;1006mp = mem;10071008while (*bp != ':' && *bp != '\0') {1009/*1010* Loop invariants:1011* There is always room for one more character in mem.1012* Mp always points just past last character in mem.1013* Bp always points at next character in buf.1014*/1015*mp++ = *bp++;1016m_room--;10171018/*1019* Enforce loop invariant: if no room left in current1020* buffer, try to get some more.1021*/1022if (m_room == 0) {1023size_t size = mp - mem;10241025if ((mem = realloc(mem, size + SFRAG)) == NULL)1026return (-2);1027m_room = SFRAG;1028mp = mem + size;1029}1030}1031*mp++ = '\0'; /* loop invariant let's us do this */1032m_room--;1033len = mp - mem - 1;10341035/*1036* Give back any extra memory and return value and success.1037*/1038if (m_room != 0)1039if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL)1040return (-2);1041*str = mem;1042return (len);1043}10441045/*1046* Cgetnum retrieves the value of the numeric capability cap from the1047* capability record pointed to by buf. The numeric value is returned in1048* the long pointed to by num. 0 is returned on success, -1 if the requested1049* numeric capability couldn't be found.1050*/1051ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL1052cgetnum(char *buf, const char *cap, long *num)1053{1054long n;1055int base, digit;1056const char *bp;10571058/*1059* Find numeric capability cap1060*/1061bp = cgetcap(buf, cap, '#');1062if (bp == NULL)1063return (-1);10641065/*1066* Look at value and determine numeric base:1067* 0x... or 0X... hexadecimal,1068* else 0... octal,1069* else decimal.1070*/1071if (*bp == '0') {1072bp++;1073if (*bp == 'x' || *bp == 'X') {1074bp++;1075base = 16;1076} else1077base = 8;1078} else1079base = 10;10801081/*1082* Conversion loop ...1083*/1084n = 0;1085for (;;) {1086if ('0' <= *bp && *bp <= '9')1087digit = *bp - '0';1088else if ('a' <= *bp && *bp <= 'f')1089digit = 10 + *bp - 'a';1090else if ('A' <= *bp && *bp <= 'F')1091digit = 10 + *bp - 'A';1092else1093break;10941095if (digit >= base)1096break;10971098n = n * base + digit;1099bp++;1100}11011102/*1103* Return value and success.1104*/1105*num = n;1106return (0);1107}110811091110/*1111* Compare name field of record.1112*/1113static int1114nfcmp(char *nf, char *rec)1115{1116char *cp, tmp;1117int ret;11181119for (cp = rec; *cp != ':'; cp++)1120;11211122tmp = *(cp + 1);1123*(cp + 1) = '\0';1124ret = strcmp(nf, rec);1125*(cp + 1) = tmp;11261127return (ret);1128}112911301131