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