xref: /csrg-svn/lib/libc/gen/getcap.c (revision 55868)
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  * %sccs.include.redist.c%
9  */
10 
11 #if defined(LIBC_SCCS) && !defined(lint)
12 static char sccsid[] = "@(#)getcap.c	5.1 (Berkeley) 08/06/92";
13 #endif /* LIBC_SCCS and not lint */
14 
15 #include <sys/types.h>
16 
17 #include <ctype.h>
18 #include <errno.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 
26 #define	BFRAG		1024
27 #define	BSIZE		80
28 #define	ESC		('[' & 037)	/* ASCII ESC */
29 #define	MAX_RECURSION	32		/* maximum getent recursion */
30 #define	SFRAG		100		/* cgetstr mallocs in SFRAG chunks */
31 
32 static size_t	 topreclen;	/* toprec length */
33 static char	*toprec;	/* Additional record specified by cgetset() */
34 
35 static int getent __P((char **, u_int *, char **, int, char *, int));
36 
37 /*
38  * Cgetset() allows the addition of a user specified buffer to be added
39  * to the database array, in effect "pushing" the buffer on top of the
40  * virtual database. 0 is returned on success, -1 on failure.
41  */
42 int
43 cgetset(ent)
44 	char *ent;
45 {
46 	if (ent == NULL) {
47 		if (toprec)
48 			free(toprec);
49                 toprec = NULL;
50                 topreclen = 0;
51                 return (0);
52         }
53         topreclen = strlen(ent);
54         if ((toprec = malloc (topreclen + 1)) == NULL) {
55 		errno = ENOMEM;
56                 return (-1);
57 	}
58         (void)strcpy(toprec, ent);
59         return (0);
60 }
61 
62 /*
63  * Cgetcap searches the capability record buf for the capability cap with
64  * type `type'.  A pointer to the value of cap is returned on success, NULL
65  * if the requested capability couldn't be found.
66  *
67  * Specifying a type of ':' means that nothing should follow cap (:cap:).
68  * In this case a pointer to the terminating ':' or NUL will be returned if
69  * cap is found.
70  *
71  * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)
72  * return NULL.
73  */
74 char *
75 cgetcap(buf, cap, type)
76 	char *buf, *cap;
77 	int type;
78 {
79 	register char *bp, *cp;
80 
81 	bp = buf;
82 	for (;;) {
83 		/*
84 		 * Skip past the current capability field - it's either the
85 		 * name field if this is the first time through the loop, or
86 		 * the remainder of a field whose name failed to match cap.
87 		 */
88 		for (;;)
89 			if (*bp == '\0')
90 				return (NULL);
91 			else
92 				if (*bp++ == ':')
93 					break;
94 
95 		/*
96 		 * Try to match (cap, type) in buf.
97 		 */
98 		for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
99 			continue;
100 		if (*cp != '\0')
101 			continue;
102 		if (*bp == '@')
103 			return (NULL);
104 		if (type == ':') {
105 			if (*bp != '\0' && *bp != ':')
106 				continue;
107 			return(bp);
108 		}
109 		if (*bp != type)
110 			continue;
111 		bp++;
112 		return (*bp == '@' ? NULL : bp);
113 	}
114 	/* NOTREACHED */
115 }
116 
117 /*
118  * Cgetent extracts the capability record name from the NULL terminated file
119  * array db_array and returns a pointer to a malloc'd copy of it in buf.
120  * Buf must be retained through all subsequent calls to cgetcap, cgetnum,
121  * cgetflag, and cgetstr, but may then be free'd.  0 is returned on success,
122  * -1 if the requested record couldn't be found, -2 if a system error was
123  * encountered (couldn't open/read a file, etc.), and -3 if a potential
124  * reference loop is detected.
125  */
126 int
127 cgetent(buf, db_array, name)
128 	char **buf, **db_array, *name;
129 {
130 	u_int dummy;
131 
132 	return (getent(buf, &dummy, db_array, -1, name, 0));
133 }
134 
135 /*
136  * Getent implements the functions of cgetent.  If fd is non-negative,
137  * *db_array has already been opened and fd is the open file descriptor.  We
138  * do this to save time and avoid using up file descriptors for tc=
139  * recursions.
140  *
141  * Getent returns the same success/failure codes as cgetent.  On success, a
142  * pointer to a malloc'ed capability record with all tc= capabilities fully
143  * expanded and its length (not including trailing ASCII NUL) are left in
144  * *cap and *len.
145  *
146  * Basic algorithm:
147  *	+ Allocate memory incrementally as needed in chunks of size BFRAG
148  *	  for capability buffer.
149  *	+ Recurse for each tc=name and interpolate result.  Stop when all
150  *	  names interpolated, a name can't be found, or depth exceeds
151  *	  MAX_RECURSION.
152  */
153 static int
154 getent(cap, len, db_array, fd, name, depth)
155 	char **cap, **db_array, *name;
156 	u_int *len;
157 	int fd, depth;
158 {
159 	register char *r_end, *rp, **db_p;
160 	int myfd, eof, foundit;
161 	char *record;
162 
163 	/*
164 	 * Return with ``loop detected'' error if we've recursed more than
165 	 * MAX_RECURSION times.
166 	 */
167 	if (depth > MAX_RECURSION)
168 		return (-3);
169 
170 	/*
171 	 * Check if we have a top record from cgetset().
172          */
173 	if (depth == 0 && toprec != NULL) {
174 		if ((record = malloc (topreclen + BFRAG)) == NULL) {
175 			errno = ENOMEM;
176 			return (-2);
177 		}
178 		(void)strcpy(record, toprec);
179 		myfd = 0;
180 		db_p = db_array;
181 		rp = record + topreclen + 1;
182 		r_end = rp + BFRAG;
183 		goto tc_exp;
184 	}
185 	/*
186 	 * Allocate first chunk of memory.
187 	 */
188 	if ((record = malloc(BFRAG)) == NULL) {
189 		errno = ENOMEM;
190 		return (-2);
191 	}
192 	r_end = record + BFRAG;
193 	foundit = 0;
194 
195 	/*
196 	 * Loop through database array until finding the record.
197 	 */
198 
199 	for (db_p = db_array; *db_p != NULL; db_p++) {
200 		eof = 0;
201 
202 		/*
203 		 * Open database if not already open.
204 		 */
205 		if (fd >= 0) {
206 			(void)lseek(fd, 0L, L_SET);
207 			myfd = 0;
208 		} else {
209 			fd = open(*db_p, O_RDONLY, 0);
210 			if (fd < 0) {
211 				free(record);
212 				return (-2);
213 			}
214 			myfd = 1;
215 		}
216 
217 		/*
218 		 * Find the requested capability record ...
219 		 */
220 		{
221 		char buf[BUFSIZ];
222 		register char *b_end, *bp;
223 		register int c;
224 
225 		/*
226 		 * Loop invariants:
227 		 *	There is always room for one more character in record.
228 		 *	R_end always points just past end of record.
229 		 *	Rp always points just past last character in record.
230 		 *	B_end always points just past last character in buf.
231 		 *	Bp always points at next character in buf.
232 		 */
233 		b_end = buf;
234 		bp = buf;
235 		for (;;) {
236 
237 			/*
238 			 * Read in a line implementing (\, newline)
239 			 * line continuation.
240 			 */
241 			rp = record;
242 			for (;;) {
243 				if (bp >= b_end) {
244 					int n;
245 
246 					n = read(fd, buf, sizeof(buf));
247 					if (n <= 0) {
248 						if (myfd)
249 							(void)close(fd);
250 						if (n < 0) {
251 							free(record);
252 							return (-2);
253 						} else {
254 							fd = -1;
255 							eof = 1;
256 							break;
257 						}
258 					}
259 					b_end = buf+n;
260 					bp = buf;
261 				}
262 
263 				c = *bp++;
264 				if (c == '\n') {
265 					if (rp > record && *(rp-1) == '\\') {
266 						rp--;
267 						continue;
268 					} else
269 						break;
270 				}
271 				*rp++ = c;
272 
273 				/*
274 				 * Enforce loop invariant: if no room
275 				 * left in record buffer, try to get
276 				 * some more.
277 				 */
278 				if (rp >= r_end) {
279 					u_int pos, newsize;
280 
281 					pos = rp - record;
282 					newsize = r_end - record + BFRAG;
283 					record = realloc(record, newsize);
284 					if (record == NULL) {
285 						errno = ENOMEM;
286 						if (myfd)
287 							(void)close(fd);
288 						return (-2);
289 					}
290 					r_end = record + newsize;
291 					rp = record + pos;
292 				}
293 			}
294 				/* loop invariant let's us do this */
295 			*rp++ = '\0';
296 
297 			/*
298 			 * If encountered eof check next file.
299 			 */
300 			if (eof)
301 				break;
302 
303 			/*
304 			 * Toss blank lines and comments.
305 			 */
306 			if (*record == '\0' || *record == '#')
307 				continue;
308 
309 			/*
310 			 * See if this is the record we want ...
311 			 */
312 			if (cgetmatch(record, name) == 0) {
313 				foundit = 1;
314 				break;	/* found it! */
315 			}
316 		}
317 		}
318 		if (foundit)
319 			break;
320 	}
321 
322 	if (!foundit)
323 		return (-1);
324 
325 	/*
326 	 * Got the capability record, but now we have to expand all tc=name
327 	 * references in it ...
328 	 */
329 tc_exp:	{
330 		register char *newicap, *s;
331 		register int newilen;
332 		u_int ilen;
333 		int diff, iret, tclen;
334 		char *icap, *scan, *tc, *tcstart, *tcend;
335 
336 		/*
337 		 * Loop invariants:
338 		 *	There is room for one more character in record.
339 		 *	R_end points just past end of record.
340 		 *	Rp points just past last character in record.
341 		 *	Scan points at remainder of record that needs to be
342 		 *	scanned for tc=name constructs.
343 		 */
344 		scan = record;
345 		for (;;) {
346 			if ((tc = cgetcap(scan, "tc", '=')) == NULL)
347 				break;
348 
349 			/*
350 			 * Find end of tc=name and stomp on the trailing `:'
351 			 * (if present) so we can use it to call ourselves.
352 			 */
353 			s = tc;
354 			for (;;)
355 				if (*s == '\0')
356 					break;
357 				else
358 					if (*s++ == ':') {
359 						*(s-1) = '\0';
360 						break;
361 					}
362 			tcstart = tc - 3;
363 			tclen = s - tcstart;
364 			tcend = s;
365 
366 			iret = getent(&icap, &ilen, db_p, fd, tc, depth+1);
367 			newicap = icap;		/* Put into a register. */
368 			newilen = ilen;
369 			if (iret != 0) {
370 				/* an error or couldn't resolve tc= */
371 				if (myfd)
372 					(void)close(fd);
373 				free(record);
374 				return (iret);
375 			}
376 
377 			/* not interested in name field of tc'ed record */
378 			s = newicap;
379 			for (;;)
380 				if (*s == '\0')
381 					break;
382 				else
383 					if (*s++ == ':')
384 						break;
385 			newilen -= s - newicap;
386 			newicap = s;
387 
388 			/* make sure interpolated record is `:'-terminated */
389 			s += newilen;
390 			if (*(s-1) != ':') {
391 				*s = ':';	/* overwrite NUL with : */
392 				newilen++;
393 			}
394 
395 			/*
396 			 * Make sure there's enough room to insert the
397 			 * new record.
398 			 */
399 			diff = newilen - tclen;
400 			if (diff >= r_end - rp) {
401 				u_int pos, newsize, tcpos, tcposend;
402 
403 				pos = rp - record;
404 				newsize = r_end - record + diff + BFRAG;
405 				tcpos = tcstart - record;
406 				tcposend = tcend - record;
407 				record = realloc(record, newsize);
408 				if (record == NULL) {
409 					errno = ENOMEM;
410 					if (myfd)
411 						(void)close(fd);
412 					free(icap);
413 					return (-2);
414 				}
415 				r_end = record + newsize;
416 				rp = record + pos;
417 				tcstart = record + tcpos;
418 				tcend = record + tcposend;
419 			}
420 
421 			/*
422 			 * Insert tc'ed record into our record.
423 			 */
424 			s = tcstart + newilen;
425 			bcopy(tcend, s, rp - tcend);
426 			bcopy(newicap, tcstart, newilen);
427 			rp += diff;
428 			free(icap);
429 
430 			/*
431 			 * Start scan on `:' so next cgetcap works properly
432 			 * (cgetcap always skips first field).
433 			 */
434 			scan = s-1;
435 		}
436 	}
437 
438 	/*
439 	 * Close file (if we opened it), give back any extra memory, and
440 	 * return capability, length and success.
441 	 */
442 	if (myfd)
443 		(void)close(fd);
444 	*len = rp - record - 1;	/* don't count NUL */
445 	if (r_end > rp)
446 		record = realloc(record, (u_int)(rp - record));
447 	*cap = record;
448 	return (0);
449 }
450 
451 /*
452  * Cgetmatch will return 0 if name is one of the names of the capability
453  * record buf, -1 if not.
454  */
455 int
456 cgetmatch(buf, name)
457 	char *buf, *name;
458 {
459 	register char *np, *bp;
460 
461 	/*
462 	 * Start search at beginning of record.
463 	 */
464 	bp = buf;
465 	for (;;) {
466 		/*
467 		 * Try to match a record name.
468 		 */
469 		np = name;
470 		for (;;)
471 			if (*np == '\0')
472 				if (*bp == '|' || *bp == ':' || *bp == '\0')
473 					return (0);
474 				else
475 					break;
476 			else
477 				if (*bp++ != *np++)
478 					break;
479 
480 		/*
481 		 * Match failed, skip to next name in record.
482 		 */
483 		bp--;	/* a '|' or ':' may have stopped the match */
484 		for (;;)
485 			if (*bp == '\0' || *bp == ':')
486 				return (-1);	/* match failed totally */
487 			else
488 				if (*bp++ == '|')
489 					break;	/* found next name */
490 	}
491 }
492 
493 int
494 cgetfirst(buf, db_array)
495 	char **buf, **db_array;
496 {
497 	(void)cgetclose();
498 	return (cgetnext(buf, db_array));
499 }
500 
501 static FILE *pfp;
502 static int slash;
503 static char **dbp;
504 
505 int
506 cgetclose()
507 {
508 	if (pfp != NULL) {
509 		(void)fclose(pfp);
510 		pfp = NULL;
511 	}
512 	dbp = NULL;
513 	slash = 0;
514 	return (0);
515 }
516 
517 /*
518  * Cgetnext() gets either the first or next entry in the logical database
519  * specified by db_array.  It returns 0 upon completion of the database, 1
520  * upon returning an entry with more remaining, and -1 if an error occurs.
521  */
522 int
523 cgetnext(bp, db_array)
524         register char **bp;
525 	char **db_array;
526 {
527 	size_t len;
528 	int status;
529 	char *cp, *line, *rp, buf[BSIZE];
530 
531 	if (dbp == NULL)
532 		dbp = db_array;
533 
534 	if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL)
535 		return (-1);
536 
537 	for(;;) {
538 		line = fgetline(pfp, &len);
539 		if (line == NULL) {
540 			(void)fclose(pfp);
541 			if (ferror(pfp)) {
542 				pfp = NULL;
543 				dbp = NULL;
544 				slash = 0;
545 				return (-1);
546 			} else {
547 				dbp++;
548 				if (*dbp == NULL) {
549 					pfp = NULL;
550 					dbp = NULL;
551 					slash = 0;
552 					return (0);
553 				} else if ((pfp = fopen(*dbp, "r")) == NULL) {
554 					pfp = NULL;
555 					dbp = NULL;
556 					slash = 0;
557 					return (-1);
558 				} else
559 					continue;
560 			}
561 		}
562 		if (isspace(*line) || *line == ':' || *line == '#'
563 		    || len == 0 || slash) {
564 			if (len > 0 && line[len - 1] == '\\')
565 				slash = 1;
566 			else
567 				slash = 0;
568 			continue;
569 		}
570 		if (len > 0 && line[len - 1] == '\\')
571 			slash = 1;
572 		else
573 			slash = 0;
574 
575 		/* line points to a name line */
576 
577 		rp = buf;
578 		for(cp = line; *cp != NULL; cp++)
579 			if (*cp == '|' || *cp == ':')
580 				break;
581 			else
582 				*rp++ = *cp;
583 
584 		*rp = '\0';
585 		status = cgetent(bp, db_array, &buf[0]);
586 		if (status == 0)
587 			return (1);
588 		if (status == -2 || status == -3) {
589 			pfp = NULL;
590 			dbp = NULL;
591 			slash = 0;
592 			return (status + 1);
593 		}
594 	}
595 	/* NOTREACHED */
596 }
597 
598 /*
599  * Cgetstr retrieves the value of the string capability cap from the
600  * capability record pointed to by buf.  A pointer to a decoded, NUL
601  * terminated, malloc'd copy of the string is returned in the char *
602  * pointed to by str.  The length of the string not including the trailing
603  * NUL is returned on success, -1 if the requested string capability
604  * couldn't be found, -2 if a system error was encountered (storage
605  * allocation failure).
606  */
607 int
608 cgetstr(buf, cap, str)
609 	char *buf, *cap;
610 	char **str;
611 {
612 	register u_int m_room;
613 	register char *bp, *mp;
614 	int len;
615 	char *mem;
616 
617 	/*
618 	 * Find string capability cap
619 	 */
620 	bp = cgetcap(buf, cap, '=');
621 	if (bp == NULL)
622 		return (-1);
623 
624 	/*
625 	 * Conversion / storage allocation loop ...  Allocate memory in
626 	 * chunks SFRAG in size.
627 	 */
628 	if ((mem = malloc(SFRAG)) == NULL) {
629 		errno = ENOMEM;
630 		return (-2);	/* couldn't even allocate the first fragment */
631 	}
632 	m_room = SFRAG;
633 	mp = mem;
634 
635 	while (*bp != ':' && *bp != '\0') {
636 		/*
637 		 * Loop invariants:
638 		 *	There is always room for one more character in mem.
639 		 *	Mp always points just past last character in mem.
640 		 *	Bp always points at next character in buf.
641 		 */
642 		if (*bp == '^') {
643 			bp++;
644 			if (*bp == ':' || *bp == '\0')
645 				break;	/* drop unfinished escape */
646 			*mp++ = *bp++ & 037;
647 		} else if (*bp == '\\') {
648 			bp++;
649 			if (*bp == ':' || *bp == '\0')
650 				break;	/* drop unfinished escape */
651 			if ('0' <= *bp && *bp <= '7') {
652 				register int n, i;
653 
654 				n = 0;
655 				i = 3;	/* maximum of three octal digits */
656 				do {
657 					n = n * 8 + (*bp++ - '0');
658 				} while (--i && '0' <= *bp && *bp <= '7');
659 				*mp++ = n;
660 			}
661 			else switch (*bp++) {
662 				case 'b': case 'B':
663 					*mp++ = '\b';
664 					break;
665 				case 't': case 'T':
666 					*mp++ = '\t';
667 					break;
668 				case 'n': case 'N':
669 					*mp++ = '\n';
670 					break;
671 				case 'f': case 'F':
672 					*mp++ = '\f';
673 					break;
674 				case 'r': case 'R':
675 					*mp++ = '\r';
676 					break;
677 				case 'e': case 'E':
678 					*mp++ = ESC;
679 					break;
680 				case 'c': case 'C':
681 					*mp++ = ':';
682 					break;
683 				default:
684 					/*
685 					 * Catches '\', '^', and
686 					 *  everything else.
687 					 */
688 					*mp++ = *(bp-1);
689 					break;
690 			}
691 		} else
692 			*mp++ = *bp++;
693 		m_room--;
694 
695 		/*
696 		 * Enforce loop invariant: if no room left in current
697 		 * buffer, try to get some more.
698 		 */
699 		if (m_room == 0) {
700 			u_int size = mp - mem;
701 
702 			if ((mem = realloc(mem, size + SFRAG)) == NULL)
703 				return (-2);
704 			m_room = SFRAG;
705 			mp = mem + size;
706 		}
707 	}
708 	*mp++ = '\0';	/* loop invariant let's us do this */
709 	m_room--;
710 	len = mp - mem - 1;
711 
712 	/*
713 	 * Give back any extra memory and return value and success.
714 	 */
715 	if (m_room != 0)
716 		mem = realloc(mem, (u_int)(mp - mem));
717 	*str = mem;
718 	return (len);
719 }
720 
721 /*
722  * Cgetustr retrieves the value of the string capability cap from the
723  * capability record pointed to by buf.  The difference between cgetustr()
724  * and cgetstr() is that cgetustr does not decode escapes but rather treats
725  * all characters literally.  A pointer to a  NUL terminated malloc'd
726  * copy of the string is returned in the char pointed to by str.  The
727  * length of the string not including the trailing NUL is returned on success,
728  * -1 if the requested string capability couldn't be found, -2 if a system
729  * error was encountered (storage allocation failure).
730  */
731 int
732 cgetustr(buf, cap, str)
733 	char *buf, *cap, **str;
734 {
735 	register u_int m_room;
736 	register char *bp, *mp;
737 	int len;
738 	char *mem;
739 
740 	/*
741 	 * Find string capability cap
742 	 */
743 	if ((bp = cgetcap(buf, cap, '=')) == NULL)
744 		return (-1);
745 
746 	/*
747 	 * Conversion / storage allocation loop ...  Allocate memory in
748 	 * chunks SFRAG in size.
749 	 */
750 	if ((mem = malloc(SFRAG)) == NULL) {
751 		errno = ENOMEM;
752 		return (-2);	/* couldn't even allocate the first fragment */
753 	}
754 	m_room = SFRAG;
755 	mp = mem;
756 
757 	while (*bp != ':' && *bp != '\0') {
758 		/*
759 		 * Loop invariants:
760 		 *	There is always room for one more character in mem.
761 		 *	Mp always points just past last character in mem.
762 		 *	Bp always points at next character in buf.
763 		 */
764 		*mp++ = *bp++;
765 		m_room--;
766 
767 		/*
768 		 * Enforce loop invariant: if no room left in current
769 		 * buffer, try to get some more.
770 		 */
771 		if (m_room == 0) {
772 			u_int size = mp - mem;
773 
774 			if ((mem = realloc(mem, size + SFRAG)) == NULL)
775 				return (-2);
776 			m_room = SFRAG;
777 			mp = mem + size;
778 		}
779 	}
780 	*mp++ = '\0';	/* loop invariant let's us do this */
781 	m_room--;
782 	len = mp - mem - 1;
783 
784 	/*
785 	 * Give back any extra memory and return value and success.
786 	 */
787 	if (m_room != 0)
788 		mem = realloc(mem, (u_int)(mp - mem));
789 	*str = mem;
790 	return (len);
791 }
792 
793 /*
794  * Cgetnum retrieves the value of the numeric capability cap from the
795  * capability record pointed to by buf.  The numeric value is returned in
796  * the long pointed to by num.  0 is returned on success, -1 if the requested
797  * numeric capability couldn't be found.
798  */
799 int
800 cgetnum(buf, cap, num)
801 	char *buf, *cap;
802 	long *num;
803 {
804 	register long n;
805 	register int base, digit;
806 	register char *bp;
807 
808 	/*
809 	 * Find numeric capability cap
810 	 */
811 	bp = cgetcap(buf, cap, '#');
812 	if (bp == NULL)
813 		return (-1);
814 
815 	/*
816 	 * Look at value and determine numeric base:
817 	 *	0x... or 0X...	hexadecimal,
818 	 * else	0...		octal,
819 	 * else			decimal.
820 	 */
821 	if (*bp == '0') {
822 		bp++;
823 		if (*bp == 'x' || *bp == 'X') {
824 			bp++;
825 			base = 16;
826 		} else
827 			base = 8;
828 	} else
829 		base = 10;
830 
831 	/*
832 	 * Conversion loop ...
833 	 */
834 	n = 0;
835 	for (;;) {
836 		if ('0' <= *bp && *bp <= '9')
837 			digit = *bp - '0';
838 		else
839 			if (   ('a' <= *bp && *bp <= 'f')
840 			    || ('A' <= *bp && *bp <= 'F'))
841 				digit = 10 + *bp - 'a';
842 			else
843 				break;
844 
845 		if (digit >= base)
846 			break;
847 
848 		n = n * base + digit;
849 		bp++;
850 	}
851 
852 	/*
853 	 * Return value and success.
854 	 */
855 	*num = n;
856 	return (0);
857 }
858