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