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