1 /*
2  *      $Id: conf.c 191 2007-03-30 23:26:38Z boote $
3  */
4 /************************************************************************
5 *									*
6 *			     Copyright (C)  2003			*
7 *				Internet2				*
8 *			     All Rights Reserved			*
9 *									*
10 ************************************************************************/
11 /*
12  *	File:		conf.c
13  *
14  *	Author:		Jeff W. Boote
15  *			Internet2
16  *
17  *	Date:		Tue Sep  9 16:13:25 MDT 2003
18  *
19  *	Description:
20  */
21 #include <I2util/utilP.h>
22 
23 #include <stdlib.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <errno.h>
29 
30 /*
31  * Function:	I2GetConfLine
32  *
33  * Description:
34  * 		Read a single line from a file fp. remove leading whitespace,
35  * 		skip blank lines and comment lines. Put the result in the
36  * 		char buffer pointed at by lbuf, growing it as necessary.
37  *
38  * In Args:
39  *
40  * Out Args:
41  *
42  * Scope:
43  * Returns:
44  * Side Effect:
45  */
46 int
I2GetConfLine(I2ErrHandle eh,FILE * fp,int rc,char ** lbuf,size_t * lbuf_max)47 I2GetConfLine(
48 	I2ErrHandle	eh,
49 	FILE		*fp,
50 	int		rc,
51 	char		**lbuf,
52 	size_t		*lbuf_max
53 	)
54 {
55 	int	c;
56 	char	*line = *lbuf;
57 	size_t	i=0;
58 
59 	while((c = fgetc(fp)) != EOF){
60 
61 		/*
62 		 * If c is a newline - increment the row-counter.
63 		 * If lbuf already has content - break out, otherwise
64 		 * this was a leading blank line, continue until there
65 		 * is content.
66 		 */
67 		if(c == '\n'){
68 			rc++;
69 			if(i) break;
70 			continue;
71 		}
72 
73 		/*
74 		 * swallow comment lines
75 		 */
76 		if(!i && c == '#'){
77 			while((c = fgetc(fp)) != EOF){
78 				if(c == '\n'){
79 					rc++;
80 					break;
81 				}
82 			}
83 			continue;
84 		}
85 
86 		/*
87 		 * swallow leading whitespace
88 		 */
89 		if(!i && isspace((int)c)){
90 			continue;
91 		}
92 
93 		/*
94 		 * Check for line continuation.
95 		 */
96 		if(c == '\\'){
97 			if(fgetc(fp) == '\n'){
98 				rc++;
99 				continue;
100 			}
101 			I2ErrLogP(eh,EINVAL,"Invalid use of \'\\\'");
102 			return -rc;
103 		}
104 
105 		/*
106 		 * make sure lbuf is large enough for this content
107 		 */
108                 if(i+2 > *lbuf_max){
109                     while(i+2 > *lbuf_max){
110                         *lbuf_max += I2LINEBUFINC;
111                     }
112                     *lbuf = realloc(line,sizeof(char) * *lbuf_max);
113                     if(!*lbuf){
114                         if(line){
115                             free(line);
116                         }
117                         I2ErrLog(eh,"realloc(%u): %M",*lbuf_max);
118                         return -rc;
119                     }
120                     line = *lbuf;
121                 }
122 
123 		/*
124 		 * copy char
125 		 */
126 		line[i++] = c;
127 	}
128 
129 	if(!i){
130 		return 0;
131 	}
132 
133 	line[i] = '\0';
134 
135 	if(c == EOF){
136 		rc++;
137 	}
138 
139 	return rc;
140 }
141 
142 /*
143  * Function:	I2ReadConfVar
144  *
145  * Description:
146  * 	Read the next non-comment line from the config file. The file
147  * 	should be in the format of:
148  * 		key [value] [#blah comment]
149  *
150  * 	key and value are delineated by whitespace.  All leading and
151  * 	trailing whitespace is ignored. A trailing comment is legal and
152  * 	all charactors between a # and the trailing \n are ignored.
153  *
154  * In Args:
155  *
156  * Out Args:
157  *
158  * Scope:
159  * Returns:
160  * Side Effect:
161  */
162 int
I2ReadConfVar(FILE * fp,int rc,char * key,char * val,size_t max,char ** lbuf,size_t * lbuf_max)163 I2ReadConfVar(
164 	FILE	*fp,
165 	int	rc,
166 	char	*key,
167 	char	*val,
168 	size_t	max,
169 	char	**lbuf,
170 	size_t	*lbuf_max
171 	)
172 {
173 	char	*line;
174 
175 	if((rc = I2GetConfLine(NULL,fp,rc,lbuf,lbuf_max)) > 0){
176 
177 		/*
178 		 * Pull off key.
179 		 */
180 		if(!(line = strtok(*lbuf,I2WSPACESET))){
181 			rc = -rc;
182 			goto DONE;
183 		}
184 
185 		/*
186 		 * Ensure key is not too long.
187 		 */
188 		if(strlen(line)+1 > max){
189 			rc = -rc;
190 			goto DONE;
191 		}
192 		strcpy(key,line);
193 
194 		if((line = strtok(NULL,I2WSPACESET))){
195 			/*
196 			 * If there is no "value" for this key, then
197 			 * a comment is valid.
198 			 */
199 			if(*line == '#'){
200 				val[0] = '\0';
201 				goto DONE;
202 			}
203 
204 			/*
205 			 * Ensure value is not too long.
206 			 */
207 			if(strlen(line)+1 > max){
208 				rc = -rc;
209 				goto DONE;
210 			}
211 			strcpy(val,line);
212 		}
213 		else{
214 			val[0] = '\0';
215 		}
216 
217 		/*
218 		 * Ensure there is no trailing data
219 		 */
220 		if((line = strtok(NULL,I2WSPACESET))){
221 			/*
222 			 * Comments are the only valid token.
223 			 */
224 			if(*line != '#'){
225 				rc = -rc;
226 			}
227 		}
228 	}
229 
230 DONE:
231 	return rc;
232 }
233 
234 /*
235  * This is very similar to I2GetConfLine but does not allow line
236  * continuation - and copies comment/blank lines to tofp.
237  */
238 static int
parsefileline(I2ErrHandle eh,FILE * fp,int rc,char ** lbuf,size_t * lbuf_max,FILE * tofp)239 parsefileline(
240 	I2ErrHandle	eh,
241 	FILE		*fp,
242 	int		rc,
243 	char		**lbuf,
244 	size_t		*lbuf_max,
245 	FILE		*tofp
246 	)
247 {
248 	int	c;
249 	size_t	i;
250 	char	*line = *lbuf;
251 	size_t	nc=0; /* number of "significant" chars in the line */
252 	size_t	ns=0; /* number of leading spaces */
253 
254 	while((c = fgetc(fp)) != EOF){
255 
256 		/*
257 		 * If c is a newline - increment the row-counter.
258 		 * If lbuf already has content - break out, otherwise
259 		 * this was a blank line, continue until there
260 		 * is content.
261 		 */
262 		if(c == '\n'){
263 			rc++;
264 			if(nc) break;
265 			/* don't worry about leading spaces for blank lines */
266 			if(tofp) fprintf(tofp,"\n");
267 			ns=0;
268 			continue;
269 		}
270 
271 		/*
272 		 * swallow comment lines
273 		 */
274 		if(!nc && c == '#'){
275 			if(tofp){
276 				/* preserve leading spaces */
277 				for(i=0;i<ns;i++) fprintf(tofp," ");
278 				fprintf(tofp,"#");
279 			}
280 			while((c = fgetc(fp)) != EOF){
281 				if(tofp)
282 					fprintf(tofp,"%c",c);
283 				if(c == '\n'){
284 					rc++;
285 					break;
286 				}
287 			}
288 			continue;
289 		}
290 
291 		/*
292 		 * swallow leading whitespace
293 		 * (These will be preserved for comment lines - and removed
294 		 * for all other lines.)
295 		 */
296 		if(!nc && isspace((int)c)){
297 			ns++;
298 			continue;
299 		}
300 
301                 /*
302                  * Leading spaces are only allowed in comment lines
303                  */
304                 if(ns){
305 		    I2ErrLog(eh,"Leading spaces not allowed: line %d",rc);
306                     return -rc;
307                 }
308 
309 		/*
310 		 * make sure lbuf is large enough for this content
311 		 */
312                 if(nc+2 > *lbuf_max){
313                     while(nc+2 > *lbuf_max){
314                         *lbuf_max += I2LINEBUFINC;
315                     }
316                     *lbuf = realloc(line,sizeof(char) * *lbuf_max);
317                     if(!*lbuf){
318                         if(line){
319                             free(line);
320                         }
321                         I2ErrLog(eh,"realloc(%u): %M",*lbuf_max);
322                         return -rc;
323                     }
324                     line = *lbuf;
325                 }
326 
327 		/*
328 		 * copy char
329 		 */
330 		line[nc++] = c;
331 	}
332 
333 	if(!nc){
334 		return 0;
335 	}
336 
337 	line[nc] = '\0';
338 
339 	if(c == EOF){
340 		rc++;
341 	}
342 
343 	return rc;
344 }
345 
346 /*
347  * Function:	I2ParseKeyFile
348  *
349  * Description:
350  * 		Read a single line from a file fp. remove leading whitespace,
351  * 		skip blank lines and comment lines. Put the result in the
352  * 		char buffer pointed at by lbuf, growing it as necessary.
353  *
354  * 		Read a single identity/key from the keyfile. If tofp is set,
355  * 		then copy all "unmatched" lines from fp to tofp while parsing
356  * 		the file. If id_query is set, only return the entry that
357  * 		matches (if any does) skipping all others - and copying them
358  * 		to tofp if needed. A quick way to simply copy all remaining
359  * 		records to the tofp is to specify an id_query to a 0 length
360  * 		string (i.e. id_query[0] == '\0').
361  *
362  * In Args:
363  *
364  * Out Args:
365  *
366  * Scope:
367  * Returns:
368  * Side Effect:
369  */
370 int
I2ParseKeyFile(I2ErrHandle eh,FILE * fp,int rc,char ** lbuf,size_t * lbuf_max,FILE * tofp,const char * id_query,char * id_ret,uint8_t * key_ret)371 I2ParseKeyFile(
372 	I2ErrHandle eh,
373 	FILE	    *fp,
374 	int	    rc,
375 	char	    **lbuf,
376 	size_t	    *lbuf_max,
377 	FILE	    *tofp,
378 	const char  *id_query,
379 	char	    *id_ret, /* [I2MAXIDENTITYLEN+1] or null */
380 	uint8_t	    *key_ret /* [I2KEYLEN] or null */
381 	)
382 {
383 	char	    *line;
384 	int	    i;
385 	char	    rbuf[I2MAXIDENTITYLEN+1]; /* add one extra byte */
386 	char	    *keystart;
387 	uint8_t     kbuf[I2KEYLEN];
388 
389 	/*
390 	 * If there is no keyfile, parsing is very, very fast.
391 	 */
392 	if(!fp){
393 		return 0;
394 	}
395 
396 	/*
397 	 * Fetch each non-blank, non-comment line from the keyfile.
398 	 * completely validate each line and then determine at the
399 	 * end of the loop if the caller is interested in this line or not.
400 	 * (This strict interpretation of the syntax of the keyfile should
401 	 * help find errors as quickly as possible instead of letting them
402 	 * hide until they actually bite someone.)
403 	 */
404 	while((rc = parsefileline(eh,fp,rc,lbuf,lbuf_max,tofp)) > 0){
405 
406 		line = *lbuf;
407 
408 		i=0;
409 		/*
410 		 * Can potentially copy I2MAXIDENTITYLEN+1 bytes: rbuf is
411 		 * sized to handle this and the next 'if' is setup to
412 		 * detect this error condition.
413 		 */
414 		while(i <= I2MAXIDENTITYLEN){
415 			if(isspace((int)*line) || (*line == '\0')){
416 				break;
417 			}
418 			rbuf[i++] = *line++;
419 		}
420 
421 		if( i > I2MAXIDENTITYLEN){
422 			I2ErrLogP(eh,EINVAL,"Invalid identity name (too long)");
423 			return -rc;
424 		}
425 		rbuf[i] = '\0';
426 
427 		/*
428 		 * Get the hexkey
429 		 */
430 		while(isspace((int)*line)){
431 			line++;
432 		}
433 
434 		keystart = line;
435 		i=0;
436 		while(*line != '\0'){
437 			if(isspace((int)*line)){
438 				break;
439 			}
440 			i++;
441 			line++;
442 		}
443 
444 		/*
445 		 * If i is not equal to the hex-encoded length of a key...
446 		 */
447 		if(i != (I2KEYLEN*2)){
448 			I2ErrLogP(eh,EINVAL,"Invalid key length");
449 			return -rc;
450 		}
451 
452 		/*
453 		 * Make sure the only thing trailing the key is a comment
454 		 * or whitespace.
455 		 */
456 		while(*line != '\0'){
457 			if(*line == '#'){
458 				break;
459 			}
460 			if(!isspace((int)*line)){
461 				I2ErrLogP(eh,EINVAL,"Invalid chars after key");
462 				return -rc;
463 			}
464 			line++;
465 		}
466 
467 		if(!I2HexDecode(keystart,kbuf,I2KEYLEN)){
468 			I2ErrLogP(eh,EINVAL,"Invalid key: not hex?");
469 			return -rc;
470 		}
471 
472 		/*
473 		 * If a specific "identity" is being searched for: skip/copy
474 		 * lines that don't match and continue parsing the file.
475 		 */
476 		if(id_query && strncmp(rbuf,id_query,I2MAXIDENTITYLEN)){
477 			/*
478 			 * Write line to tofp, then 'continue'
479 			 */
480 			if(tofp) fprintf(tofp,"%s\n",*lbuf);
481 			continue;
482 		}
483 
484 		/*
485 		 * caller is interested in this record - return the values.
486 		 */
487 		if(id_ret){
488 			strncpy(id_ret,rbuf,sizeof(rbuf));
489 		}
490 
491 		if(key_ret){
492 			memcpy(key_ret,kbuf,I2KEYLEN);
493 		}
494 
495 		break;
496 	}
497 
498 	return rc;
499 }
500 
501 /*
502  * Function:	I2WriteKeyLine
503  *
504  * Description:
505  *
506  * In Args:
507  *
508  * Out Args:
509  *
510  * Scope:
511  * Returns:
512  * Side Effect:
513  */
514 int
I2WriteKeyLine(I2ErrHandle eh,FILE * fp,const char * id,const uint8_t * key)515 I2WriteKeyLine(
516 	I2ErrHandle	eh,
517 	FILE		*fp,
518 	const char	*id,
519 	const uint8_t	*key
520 	)
521 {
522 	int	ret;
523 	char	hbuf[(I2KEYLEN*2)+1]; /* size for hex version */
524 
525 	if(!id || (id[0] == '\0') || (strlen(id) > I2MAXIDENTITYLEN)){
526 		I2ErrLogP(eh,EINVAL,"I2WriteKeyLine(): Invalid identity name");
527 		return -1;
528 	}
529 
530 	I2HexEncode(hbuf,key,I2KEYLEN);
531 
532 	/*
533 	 * if fprintf has an error, set ret < 0 for a failure return.
534 	 */
535 	ret = fprintf(fp,"%s\t%s\n",id,hbuf);
536 
537 	if(ret > 0) ret = 0;
538 
539 	return ret;
540 }
541 
542 /*
543  * Function:	I2ParsePFFile
544  *
545  * Description:
546  * 		Read a single line from a file fp. remove leading whitespace,
547  * 		skip blank lines and comment lines. Put the result in the
548  * 		char buffer pointed at by lbuf, growing it as necessary.
549  *
550  * 		Read a single identity/pass-phrase from the pffile. If tofp
551  * 		is set, then copy all "unmatched" lines from fp to tofp while
552  * 		parsing the file. If id_query is set, only return the entry that
553  * 		matches (if any does) skipping all others - and copying them
554  * 		to tofp if needed. A quick way to simply copy all remaining
555  * 		records to the tofp is to specify an id_query to a 0 length
556  * 		string (i.e. id_query[0] == '\0').
557  *
558  * 		The entry that is returned currently breaks the line, so
559  * 		*lbuf can not just be written back to the file directly.
560  *
561  * In Args:
562  *
563  * Out Args:
564  *
565  * Scope:
566  * Returns:
567  * Side Effect:
568  */
569 int
I2ParsePFFile(I2ErrHandle eh,FILE * filep,FILE * tofilep,int rc,const char * id_query,char ** id_ret,char ** pf_ret,size_t * pf_len,char ** lbuf,size_t * lbuf_max)570 I2ParsePFFile(
571 	I2ErrHandle eh,
572 	FILE	    *filep,
573 	FILE	    *tofilep,
574 	int	    rc,
575 	const char  *id_query,
576 	char	    **id_ret,   /* points in lbuf */
577 	char	    **pf_ret,   /* points in lbuf */
578         size_t      *pf_len,
579 	char	    **lbuf,
580 	size_t	    *lbuf_max
581 	)
582 {
583     char    *line;
584     char    *ts;
585     char    *hex;
586     char    *pf;
587     size_t  id_len;
588     size_t  hex_len;
589     size_t  idq_len;
590     size_t  hex_oset;
591 
592     /*
593      * If there is no pffile, parsing is very, very fast.
594      */
595     if(!filep){
596         return 0;
597     }
598 
599     if(id_query){
600         idq_len = strlen(id_query);
601     }
602 
603     /*
604      * Fetch each non-blank, non-comment line from the pffile.
605      *
606      * Completely validate each line and then determine at the
607      * end of the loop if the caller is interested in this line or not.
608      * (This strict interpretation of the syntax of the pffile should
609      * help find errors as quickly as possible instead of letting them
610      * hide until they actually bite someone.)
611      *
612      * Valid syntax is:
613      *
614      */
615     while((rc = parsefileline(eh,filep,rc,lbuf,lbuf_max,tofilep)) > 0){
616 
617         size_t  used,needed;
618 
619         /*
620          * line starts with id
621          */
622         line = *lbuf;
623 
624         /*
625          * Find beginning of hex-pf
626          */
627         id_len = strcspn(line,I2WSPACESET);
628         hex = line + id_len;
629 
630         /*
631          * Must be at least one space separator
632          */
633         if(!isspace((int)*hex)){
634             return -rc;
635         }
636 
637         /*
638          * If a specific "identity" is being searched for: skip/copy
639          * lines that don't match and continue parsing the file.
640          */
641         if(id_query &&
642                 ((idq_len == 0) || strncmp(line,id_query,MIN(id_len,idq_len)))){
643             /*
644              * Write line to tofilep, then 'continue'
645              */
646             if(tofilep) fprintf(tofilep,"%s\n",*lbuf);
647             continue;
648         }
649 
650         /* Terminate id, then eat trailing white-space */
651         *hex = '\0';
652         hex++;
653         while(isspace((int)*hex)){
654             hex++;
655         }
656         hex_oset = hex - line;
657 
658         /* how long is the hex-pf */
659         hex_len = strcspn(hex,I2WSPACESET);
660 
661         /*
662          * eat trailing white-space - and terminate hex-pf
663          */
664         ts = hex + hex_len;
665         while(isspace((int)*ts)){
666             *ts = '\0';
667             ts++;
668         }
669 
670         /*
671          * hex can not be nul, must be multiple of 2,
672          * and must be the last thing on the line.
673          */
674         if(!hex_len || (hex_len % 2) || (*ts != '\0')){
675             return -rc;
676         }
677 
678         /*
679          * Make sure there is enough room after full line in lbuf to hold
680          * return pf. (ts currently points at the terminating nul
681          * byte for the full line.)
682          */
683         used = (ts - *lbuf) + 1;
684         needed = (hex_len / 2);
685 
686         /*
687          * make sure lbuf is large enough for this content
688          */
689         if((used + needed) > *lbuf_max){
690             while((used + needed) > *lbuf_max){
691                 *lbuf_max += I2LINEBUFINC;
692             }
693             *lbuf = realloc(line,sizeof(char) * *lbuf_max);
694             if(!*lbuf){
695                 if(line){
696                     free(line);
697                 }
698                 I2ErrLog(eh,"realloc(%u): %M",*lbuf_max);
699                 return -rc;
700             }
701             line = *lbuf;
702         }
703 
704         /*
705          * Terminate id, and point pf to beginning of binary pf
706          * to be returned.
707          */
708         hex = line + hex_oset;
709         pf = line + used;
710 
711         if(!I2HexDecode(hex,(uint8_t *)pf,hex_len/2)){
712             I2ErrLogP(eh,EINVAL,"Invalid key: not hex?");
713             return -rc;
714         }
715 
716         if(id_ret){
717             *id_ret = line;
718         }
719         if(pf_ret){
720             *pf_ret = pf;
721         }
722         if(pf_len){
723             *pf_len = hex_len/2;
724         }
725 
726         break;
727     }
728 
729     return rc;
730 }
731 
732 /*
733  * Function:	I2WritePFLine
734  *
735  * Description:
736  *
737  * In Args:
738  *
739  * Out Args:
740  *
741  * Scope:
742  * Returns:
743  * Side Effect:
744  */
745 int
I2WritePFLine(I2ErrHandle eh,FILE * fp,const char * id,const uint8_t * bytes,size_t nbytes,char ** lbuf,size_t * lbuf_max)746 I2WritePFLine(
747 	I2ErrHandle	eh,
748 	FILE		*fp,
749 	const char	*id,        /* nul terminated */
750 	const uint8_t	*bytes,
751         size_t          nbytes,
752 	char	        **lbuf,     /* memory for hex string */
753 	size_t	        *lbuf_max
754 	)
755 {
756 	int	ret;
757         char    *line = *lbuf;
758 
759 	if(!id || (id[0] == '\0')){
760 		I2ErrLogP(eh,EINVAL,"I2WriteKeyLine(): Invalid identity name");
761 		return -1;
762 	}
763 
764         /*
765          * make sure lbuf is large enough for this content
766          */
767         if((nbytes*2 + 1) > *lbuf_max){
768             while((nbytes*2 + 1) > *lbuf_max){
769                 *lbuf_max += I2LINEBUFINC;
770             }
771             *lbuf = realloc(line,sizeof(char) * *lbuf_max);
772             if(!*lbuf){
773                 if(line){
774                     free(line);
775                 }
776                 I2ErrLog(eh,"realloc(%u): %M",*lbuf_max);
777                 return -1;
778             }
779             line = *lbuf;
780         }
781 
782 
783 	I2HexEncode(line,bytes,nbytes);
784 
785 	/*
786 	 * if fprintf has an error, set ret < 0 for a failure return.
787 	 */
788 	ret = fprintf(fp,"%s\t%s\n",id,line);
789 
790 	if(ret > 0) ret = 0;
791 
792 	return ret;
793 }
794 
795 /*
796  * Function:	I2StrToNum
797  *
798  * Description:
799  *
800  * In Args:
801  *
802  * Out Args:
803  *
804  * Scope:
805  * Returns:
806  * Side Effect:
807  */
808 int
I2StrToNum(I2numT * limnum,char * limstr)809 I2StrToNum(
810 		I2numT	*limnum,
811 		char	*limstr
812 		)
813 {
814 	size_t		silen=0;
815 	size_t		len;
816 	char		*endptr;
817 	I2numT		ret, mult=1;
818 
819 	while(isdigit((int)limstr[silen])){
820 		silen++;
821 	}
822 	len = strlen(limstr);
823 
824 	if(len != silen){
825 		/*
826 		 * Ensure that there is at most one non-digit and that it
827 		 * is the last char.
828 		 */
829 		if((len - silen) > 1){
830 			return -1;
831 		}
832 
833 		switch (tolower(limstr[silen])){
834 		case 'e':
835 			mult *= 1000;	/* 1e18 */
836 		case 'p':
837 			mult *= 1000;	/* 1e15 */
838 		case 't':
839 			mult *= 1000;	/* 1e12 */
840 		case 'g':
841 			mult *= 1000;	/* 1e9 */
842 		case 'm':
843 			mult *= 1000;	/* 1e6 */
844 		case 'k':
845 			mult *= 1000;	/* 1e3 */
846 			break;
847 		default:
848 			return -1;
849 			/* UNREACHED */
850 		}
851 		limstr[silen] = '\0';
852 	}
853 	ret = strtoull(limstr, &endptr, 10);
854 	if(endptr != &limstr[silen]){
855 		return -1;
856 	}
857 
858 	if(ret == 0){
859 		*limnum = 0;
860 		return 0;
861 	}
862 
863 	/* Check for overflow. */
864 	*limnum = ret * mult;
865 	return (*limnum < ret || *limnum < mult)? (-1) : 0;
866 }
867 
868 /*
869  * Function:	I2StrToByte
870  *
871  * Description:
872  *
873  * In Args:
874  *
875  * Out Args:
876  *
877  * Scope:
878  * Returns:
879  * Side Effect:
880  */
881 int
I2Str2Byte(I2numT * limnum,char * limstr)882 I2Str2Byte(
883 		I2numT	*limnum,
884 		char	*limstr
885 		)
886 {
887 	size_t		silen=0;
888 	size_t		len;
889 	char		*endptr;
890 	I2numT		ret, mult=1;
891 
892 	while(isdigit((int)limstr[silen])){
893 		silen++;
894 	}
895 	len = strlen(limstr);
896 
897 	if(len != silen){
898 		/*
899 		 * Ensure that there is at most one non-digit and that it
900 		 * is the last char.
901 		 */
902 		if((len - silen) > 1){
903 			return -1;
904 		}
905 
906 		switch (tolower(limstr[silen])){
907 		case 'e':
908 			mult <<= 10;
909 		case 'p':
910 			mult <<= 10;
911 		case 't':
912 			mult <<= 10;
913 		case 'g':
914 			mult <<= 10;
915 		case 'm':
916 			mult <<= 10;
917 		case 'k':
918 			mult <<= 10;
919 			break;
920 		default:
921 			return -1;
922 			/* UNREACHED */
923 		}
924 		limstr[silen] = '\0';
925 	}
926 	ret = strtoull(limstr, &endptr, 10);
927 	if(endptr != &limstr[silen]){
928 		return -1;
929 	}
930 
931 	if(ret == 0){
932 		*limnum = 0;
933 		return 0;
934 	}
935 
936 	/* Check for overflow. */
937 	*limnum = ret * mult;
938 	return (*limnum < ret || *limnum < mult)? (-1) : 0;
939 }
940