1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <limits.h>
6 
7 #include "../include/xsw_ctype.h"
8 #include "../include/os.h"
9 #include "../include/string.h"
10 #include "../include/fio.h"
11 
12 #ifdef MEMWATCH
13 # include "memwatch.h"
14 #endif
15 
16 
17 FILE *FOpen(const char *path, const char *mode);
18 void FClose(FILE *fp);
19 
20 void FSeekNextLine(FILE *fp);
21 void FSeekPastSpaces(FILE *fp);
22 void FSeekPastChar(FILE *fp, char c);
23 
24 int FSeekToParm(FILE *fp, const char *parm, char comment, char delim);
25 char *FSeekNextParm(FILE *fp, char *buf, char comment, char delim);
26 
27 int FGetValuesI(FILE *fp, int *value, int nvalues);
28 int FGetValuesL(FILE *fp, long *value, int nvalues);
29 int FGetValuesF(FILE *fp, double *value, int nvalues);
30 char *FGetString(FILE *fp);
31 char *FGetStringLined(FILE *fp);
32 char *FGetStringLiteral(FILE *fp);
33 
34 char *FReadNextLineAlloc(FILE *fp, char comment);
35 char *FReadNextLineAllocCount(
36 	FILE *fp, char comment, int *line_count
37 );
38 
39 
40 #define ISCR(c) (((c) == '\n') || ((c) == '\r'))
41 
42 /*
43  *      Allocate memory while reading a line from file in chunk size
44  *      of this many bytes:
45  */
46 #define FREAD_ALLOC_CHUNK_SIZE  8
47 
48 
49 #define ATOI(s)         (((s) != NULL) ? atoi(s) : 0)
50 #define ATOL(s)         (((s) != NULL) ? atol(s) : 0)
51 #define ATOF(s)         (((s) != NULL) ? atof(s) : 0.0f)
52 #define STRDUP(s)       (((s) != NULL) ? strdup(s) : NULL)
53 
54 #define MAX(a,b)        (((a) > (b)) ? (a) : (b))
55 #define MIN(a,b)        (((a) < (b)) ? (a) : (b))
56 #define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))
57 #define STRLEN(s)       (((s) != NULL) ? strlen(s) : 0)
58 #define STRISEMPTY(s)   (((s) != NULL) ? (*(s) == '\0') : 1)
59 
60 
61 /*
62  *	OS wrapper to open file using UNIX path notation.
63  *
64  *	The returned FILE pointer can be used with all the ANSI C standard
65  *	file IO functions.
66  */
FOpen(const char * path,const char * mode)67 FILE *FOpen(const char *path, const char *mode)
68 {
69 	int len;
70 	char *new_path, *strptr2;
71 	const char *strptr1;
72 	FILE *fp;
73 
74 	if(STRISEMPTY(path) || STRISEMPTY(mode))
75 	    return(NULL);
76 
77 	/* Get length of path and allocate new buffer */
78 	len = STRLEN(path);
79 	new_path = (char *)malloc((len + 1) * sizeof(char));
80 	if(new_path == NULL)
81 	    return(NULL);
82 
83 	/* Copy path to new_path */
84 	strptr1 = path;
85 	strptr2 = new_path;
86 	while(*strptr1 != '\0')
87 	{
88 	    *strptr2 = *strptr1;
89 
90 #ifdef __MSW__
91 	    if(*strptr2 == '/')
92 		*strptr2 = '\\';
93 #endif	/* __MSW__ */
94 
95 	    strptr1++; strptr2++;
96 	}
97 	*strptr2 = '\0';
98 
99 	/* Open file */
100 	fp = fopen(new_path, mode);
101 
102 	/* Delete coppied path */
103 	free(new_path);
104 
105 	return(fp);
106 }
107 
108 /*
109  *	Closes the file opened by FOpen().
110  */
FClose(FILE * fp)111 void FClose(FILE *fp)
112 {
113 	if(fp != NULL)
114 	    fclose(fp);
115 }
116 
117 /*
118  *	Seeks to next line, escape sequences will be parsed.
119  */
FSeekNextLine(FILE * fp)120 void FSeekNextLine(FILE *fp)
121 {
122 	int c;
123 
124 	if(fp == NULL)
125 	    return;
126 
127 	do
128 	{
129 	    c = fgetc(fp);
130 
131 	    /* Escape sequence? */
132 	    if(c == '\\')
133 		c = fgetc(fp);
134 	    /* New line? */
135 	    else if(ISCR(c))
136 		break;
137 
138 	} while(c != EOF);
139 }
140 
141 /*
142  *      Seeks fp past any spaces.
143  */
FSeekPastSpaces(FILE * fp)144 void FSeekPastSpaces(FILE *fp)
145 {
146 	int c;
147 
148 	if(fp == NULL)
149 	    return;
150 
151 	while(1)
152 	{
153 	    c = fgetc(fp);
154 	    if(c == EOF)
155 		break;
156 
157 	    if(ISBLANK(c))
158 		continue;
159 
160 	    fseek(fp, -1, SEEK_CUR);
161 	    break;
162 	}
163 }
164 
165 /*
166  *	Seeks fp past the first occurance of c or EOF.
167  */
FSeekPastChar(FILE * fp,char c)168 void FSeekPastChar(FILE *fp, char c)
169 {
170 	int i;
171 
172 
173 	if(fp == NULL)
174 	    return;
175 
176 	do
177 	{
178 	    i = fgetc(fp);
179 	    if(i == c)
180 		break;
181 
182 	} while(i != EOF);
183 }
184 
185 /*
186  *	Seeks fp to the beginning of the value of the specified
187  *	parameter.
188  *
189  *	The delim specifies the deliminator character between the
190  *	parameter and the value. The delim can be '\0' to specify
191  *	"any blanks" (in the case of no deliminators between the
192  *	parameter and the value).
193  *
194  *	Parameters are assumed to not contain spaces or escape sequences.
195  *
196  *	Returns 0 on successful match or non-zero on error.
197  */
FSeekToParm(FILE * fp,const char * parm,char comment,char delim)198 int FSeekToParm(FILE *fp, const char *parm, char comment, char delim)
199 {
200 	int c, parm_len;
201 
202 	if(fp == NULL)
203 	    return(-1);
204 
205 	parm_len = STRLEN(parm);
206 	if(parm_len <= 0)
207 	    return(-1);
208 
209 	do
210 	{
211 	    c = fgetc(fp);
212 	    if(c == EOF)
213 		return(-1);
214 
215 	    /* Seek past spaces */
216 	    if(ISBLANK(c))
217 		FSeekPastSpaces(fp);
218 
219 	    /* New line? */
220 	    if(ISCR(c))
221 	        continue;
222 
223 	    /* First non-blank a comment character? */
224 	    if(c == comment)
225 	    {
226 		FSeekNextLine(fp);
227 		continue;
228 	    }
229 
230 	    /* Matches parameter? */
231 	    if(c == *parm)
232 	    {
233 		/* First char matches parm */
234 		const char *strptr = parm + 1;
235 
236 
237 		while(*strptr != '\0')
238 		{
239 		    c = fgetc(fp);
240 		    if(c != *strptr)
241 			break;
242 
243 		    strptr++;
244 		}
245 		if(*strptr == '\0')
246 		{
247 		    /* Got match, seek fp past deliminator */
248 		    if(delim == '\0')
249 		    {
250 			FSeekPastSpaces(fp);
251 		    }
252 		    else
253 		    {
254 			FSeekPastSpaces(fp);
255 
256 			/* Seek to delim or newline */
257 			do
258 			{
259 			    c = fgetc(fp);
260 			    if((c == EOF) || (c == delim))
261 				break;
262 
263 			    if(ISCR(c))
264 			    {
265 				fseek(fp, -1, SEEK_CUR);
266 				break;
267 			    }
268 
269 			} while(1);
270 
271 			FSeekPastSpaces(fp);
272 		    }
273 
274 		    return(0);
275 		}
276 		else
277 		{
278 		    /* No match, seek to next line */
279 		    FSeekNextLine(fp);
280 		}
281 	    }
282 	    else
283 	    {
284 		/* No match, seek to next line */
285 		FSeekNextLine(fp);
286 	    }
287 
288 	} while(1);
289 
290 	return(-1);
291 }
292 
293 /*
294  *	Fetches the next parameter found at the file position fp.
295  *
296  *	If buf is NULL then a newly allocated string will be returned
297  *	containing the fetched parm. If buf is not NULL, then buf will
298  *	be realloc()'ed and returned as a new pointer containing
299  *	the fetched parm.
300  *
301  *	If EOF is reached by the given fp position, then NULL will
302  *	be returned and the given buf will have been free()ed by this
303  *	function.
304  */
FSeekNextParm(FILE * fp,char * buf,char comment,char delim)305 char *FSeekNextParm(FILE *fp, char *buf, char comment, char delim)
306 {
307 	int c, buf_pos = 0, buf_len, buf_inc = FREAD_ALLOC_CHUNK_SIZE;
308 
309 
310 	if(fp == NULL)
311 	{
312 	    free(buf);
313 	    return(NULL);
314 	}
315 
316 	/* Get length of buf (less than actual allocated is okay) */
317 	buf_len = (buf != NULL) ? strlen(buf) : 0;
318 
319 	/* Seek past spaces and comments to next parameter */
320 	while(1)
321 	{
322 	    FSeekPastSpaces(fp);
323 	    c = fgetc(fp);
324 	    if(c == EOF)
325 	    {
326 		free(buf);
327 		return(NULL);
328 	    }
329 	    else if(c == comment)
330 	    {
331 		FSeekNextLine(fp);
332 		continue;
333 	    }
334 	    else if(ISCR(c))
335 	    {
336 		continue;
337 	    }
338 	    else
339 	    {
340 		fseek(fp, -1, SEEK_CUR);
341 		break;
342 	    }
343 	}
344 
345 	/* Begin fetching this parm */
346 	while(1)
347 	{
348 	    /* Get next char */
349 	    c = fgetc(fp);
350 	    if(c == EOF)
351 	    {
352 		break;
353 	    }
354 
355 	    /* Blank character reached? */
356 	    if(ISBLANK(c))
357 	    {
358 		/* Blank char reached, seek past delimiantor and position
359 		 * fp at beginning of value.
360 		 */
361 		if(delim == '\0')
362 		{
363 		    FSeekPastSpaces(fp);
364 		}
365 		else
366 		{
367 		    FSeekPastSpaces(fp);
368 
369 		    /* Seek to deim or newline */
370 		    do
371 		    {
372 			c = fgetc(fp);
373 			if((c == EOF) || (c == delim))
374 			    break;
375 
376 			if(ISCR(c))
377 			{
378 			    fseek(fp, -1, SEEK_CUR);
379 			    break;
380 			}
381 
382 		    } while(1);
383 
384 		    FSeekPastSpaces(fp);
385 		}
386 		break;
387 	    }
388 
389 	    /* CR reached? */
390 	    if(ISCR(c))
391 	    {
392 	    	fseek(fp, -1, SEEK_CUR);
393 		break;
394 	    }
395 
396 	    /* Deliminator reached? */
397 	    if(c == delim)
398 	    {
399 		FSeekPastSpaces(fp);
400 		break;
401 	    }
402 
403 	    /* Need to allocate buffer? */
404 	    if(buf_pos <= buf_len)
405 	    {
406 		buf_len += buf_inc;
407 
408 		buf = (char *)realloc(buf, buf_len * sizeof(char));
409 		if(buf == NULL)
410 		{
411 		    FSeekNextLine(fp);
412 		    return(NULL);
413 		}
414 	    }
415 
416 	    buf[buf_pos] = (char)c;
417 	    buf_pos++;
418 	}
419 
420 	/* Put null terminating byte on buffer */
421 	if(c == EOF)
422 	{
423 	    free(buf);
424 	    buf = NULL;
425 	}
426 	else
427 	{
428 	    if(buf_pos <= buf_len)
429 	    {
430 		buf_len = buf_pos + 1;
431 
432 		buf = (char *)realloc(buf, buf_len * sizeof(char));
433 		if(buf == NULL)
434 		    return(NULL);
435 	    }
436 	    buf[buf_pos] = '\0';
437 	}
438 
439 	return(buf);
440 }
441 
442 /*
443  *      Loads values as ints from the file starting at the
444  *      specified fp. Will not load more than nvalues.
445  *
446  *      The fp will be positioned at the start of the next line.
447  *
448  *      Returns non-zero on error.
449  */
FGetValuesI(FILE * fp,int * value,int nvalues)450 int FGetValuesI(FILE *fp, int *value, int nvalues)
451 {
452 	int c, i, n, line_done = 0;
453 #define len	80
454 	char num_str[len];
455 
456 
457 	if(fp == NULL)
458 	    return(-1);
459 
460 	FSeekPastSpaces(fp);
461 
462 	/* Begin fetching values */
463 	for(i = 0; i < nvalues; i++)
464 	{
465 	    (*num_str) = '\0';
466 
467 	    /* Read number */
468 	    for(n = 0; n < len; n++)
469 	    {
470 		if(line_done)
471 		    break;
472 
473 		c = fgetc(fp);
474 		if((c == EOF) || ISCR(c))
475 		{
476 		    num_str[n] = '\0';
477 		    line_done = 1;
478 		    break;
479 		}
480 		/* Escape sequence? */
481 		else if(c == '\\')
482 		{
483 		    c = fgetc(fp);
484 		    if(c == EOF)
485 		    {
486 			num_str[n] = '\0';
487 			line_done = 1;
488 			break;
489 		    }
490 		    if(c != '\\')
491 		       c = fgetc(fp);
492 
493 		    if(c == EOF)
494 		    {
495 			num_str[n] = '\0';
496 			line_done = 1;
497 			break;
498 		    }
499 		}
500 		/* Separator? */
501 		else if(ISBLANK(c) || (c == ','))
502 		{
503 		    num_str[n] = '\0';
504 		    FSeekPastSpaces(fp);
505 		    break;
506 		}
507 
508 		num_str[n] = (char)c;
509 	    }
510 	    num_str[len - 1] = '\0';
511 
512 	    value[i] = atoi(num_str);
513 	}
514 	if(!line_done)
515 	    FSeekNextLine(fp);
516 #undef len
517 	return(0);
518 }
519 
520 /*
521  *	Loads values as longs from the file starting at the
522  *	specified fp. Will not load more than nvalues.
523  *
524  *	The fp will be positioned at the start of the next line.
525  *
526  *	Returns non-zero on error.
527  */
FGetValuesL(FILE * fp,long * value,int nvalues)528 int FGetValuesL(FILE *fp, long *value, int nvalues)
529 {
530 	int c, i, n, line_done = 0;
531 #define len     80
532 	char num_str[len];
533 
534 
535 	if(fp == NULL)
536 	    return(-1);
537 
538 	FSeekPastSpaces(fp);
539 
540 	/* Begin fetching values */
541 	for(i = 0; i < nvalues; i++)
542 	{
543 	    (*num_str) = '\0';
544 
545 	    /* Read number */
546 	    for(n = 0; n < len; n++)
547 	    {
548 		if(line_done)
549 		    break;
550 
551 		c = fgetc(fp);
552 		if((c == EOF) || ISCR(c))
553 		{
554 		    num_str[n] = '\0';
555 		    line_done = 1;
556 		    break;
557 		}
558 		/* Escape sequence? */
559 		else if(c == '\\')
560 		{
561 		    c = fgetc(fp);
562 		    if(c == EOF)
563 		    {
564 			num_str[n] = '\0';
565 			line_done = 1;
566 			break;
567 		    }
568 		    if(c != '\\')
569 		       c = fgetc(fp);
570 
571 		    if(c == EOF)
572 		    {
573 			num_str[n] = '\0';
574 			line_done = 1;
575 			break;
576 		    }
577 		}
578 		/* Separator? */
579 		else if(ISBLANK(c) || (c == ','))
580 		{
581 		    num_str[n] = '\0';
582 		    FSeekPastSpaces(fp);
583 		    break;
584 		}
585 
586 		num_str[n] = (char)c;
587 	    }
588 	    num_str[len - 1] = '\0';
589 
590 	    value[i] = atol(num_str);
591 	}
592 	if(!line_done)
593 	    FSeekNextLine(fp);
594 #undef len
595 	return(0);
596 }
597 
598 /*
599  *      Loads values as doubles from the file starting at the
600  *      specified fp. Will not load more than nvalues.
601  *
602  *      The fp will be positioned at the start of the next line.
603  *
604  *	Returns non-zero on error.
605  */
FGetValuesF(FILE * fp,double * value,int nvalues)606 int FGetValuesF(FILE *fp, double *value, int nvalues)
607 {
608 	int c, i, n, line_done = 0;
609 #define len	80
610 	char num_str[len];
611 
612 
613 	if(fp == NULL)
614 	    return(-1);
615 
616 	FSeekPastSpaces(fp);
617 
618 	/* Begin fetching values */
619 	for(i = 0; i < nvalues; i++)
620 	{
621 	    (*num_str) = '\0';
622 
623 	    /* Read number */
624 	    for(n = 0; n < len; n++)
625 	    {
626 		if(line_done)
627 		    break;
628 
629 		c = fgetc(fp);
630 		if((c == EOF) || ISCR(c))
631 		{
632 		    num_str[n] = '\0';
633 		    line_done = 1;
634 		    break;
635 		}
636 		/* Escape sequence? */
637 		else if(c == '\\')
638 		{
639 		    c = fgetc(fp);
640 		    if(c == EOF)
641 		    {
642 			num_str[n] = '\0';
643 			line_done = 1;
644 			break;
645 		    }
646 		    if(c != '\\')
647 		       c = fgetc(fp);
648 
649 		    if(c == EOF)
650 		    {
651 			num_str[n] = '\0';
652 			line_done = 1;
653 			break;
654 		    }
655 		}
656 		/* Separator? */
657 		else if(ISBLANK(c) || (c == ','))
658 		{
659 		    num_str[n] = '\0';
660 		    FSeekPastSpaces(fp);
661 		    break;
662 		}
663 
664 		num_str[n] = (char)c;
665 	    }
666 	    num_str[len - 1] = '\0';
667 
668 	    value[i] = atof(num_str);
669 	}
670 	if(!line_done)
671 	    FSeekNextLine(fp);
672 
673 #undef len
674 	return(0);
675 }
676 
677 /*
678  *	Returns a dynamically allocated string containing the value as a
679  *	string obtained from the file specified by fp. Reads from the
680  *	current position of fp to the next new line character or EOF.
681  *
682  *      Escape sequences will be parsed and spaces will be stripped.
683  *
684  *      The fp is positioned after the new line or at the EOF.
685  */
FGetString(FILE * fp)686 char *FGetString(FILE *fp)
687 {
688 	int c, i = 0, len = 0;
689 	char *s = NULL, *s2;
690 
691 
692 	if(fp == NULL)
693 	    return(s);
694 
695 	/* Begin reading string from file */
696 
697 	/* Skip initial spaces */
698 	c = fgetc(fp);
699 	while((c != EOF) && ISBLANK(c))
700 	    c = fgetc(fp);
701 
702 	if(c == EOF)
703 	    return(s);
704 
705 	/* Read string */
706 	while(1)
707 	{
708 	    /* Need to increase allocation? */
709 	    if(i >= len)
710 	    {
711 		len = MAX(len + 128, i + 1);
712 		s = (char *)realloc(s, len * sizeof(char));
713 		if(s == NULL)
714 		{
715 		    len = i = 0;
716 		    break;
717 		}
718 	    }
719 
720 	    /* Get pointer to current position in string */
721 	    s2 = s + i;
722 
723 	    /* Set new character value */
724 	    *s2 = c;
725 
726 	    /* End of file or end of the line? */
727 	    if((c == EOF) || ISCR(c))
728 	    {
729 		*s2 = '\0';
730 		break;
731 	    }
732 	    /* Escape sequence? */
733 	    else if(c == '\\')
734 	    {
735 		/* Read next character after backslash */
736 		c = fgetc(fp);
737 		if((c == EOF) || (c == '\0'))
738 		{
739 		    *s2 = '\0';
740 		}
741 		else if(ISCR(c))
742 		{
743 		    /* New line (do not save this newline char) */
744 		    i--;
745 		}
746 		else if(c == '\\')
747 		{
748 		    /* Literal backslash */
749 		    *s2 = '\\';
750 		}
751 		else if(c == '0')
752 		{
753 		    /* Null */
754 		    *s2 = '\0';
755 		}
756 		else if(c == 'b')
757 		{
758 		    /* Bell */
759 		    *s2 = '\b';
760 		}
761 		else if(c == 'n')
762 		{
763 		    /* New line */
764 		    *s2 = '\n';
765 		}
766 		else if(c == 'r')
767 		{
768 		    /* Line return */
769 		    *s2 = '\r';
770 		}
771 		else if(c == 't')
772 		{
773 		    /* Tab */
774 		    *s2 = '\t';
775 		}
776 		else
777 		{
778 		    /* Unsupported escape sequence, store it as is */
779 		    *s2 = c;
780 		}
781 
782 		/* Read next character and increment position */
783 		c = fgetc(fp);
784 		i++;
785 	    }
786 	    /* Regular character */
787 	    else
788 	    {
789 		/* Read next character and increment position */
790 		c = fgetc(fp);
791 		i++;
792 	    }
793 	}
794 
795 	/* Cut off tailing spaces */
796 	if(s != NULL)
797 	{
798 	    s2 = s + i - 1;
799 
800 	    while(ISBLANK(*s2) && (s2 >= s))
801 		*s2 = '\0';
802 	}
803 
804 	return(s);
805 }
806 
807 
808 /*
809  *	Works just like FGetString() except the string is loaded
810  *	literally and the only escape sequence to be handled will
811  *	be the two characters '\\' '\n', when those characters
812  *	are encountered the character '\n' will be saved into the return
813  *	string.
814  *
815  *	Spaces will not be striped, the fp will be positioned after the
816  *	newline or EOF (whichever is encountered first).
817  */
FGetStringLined(FILE * fp)818 char *FGetStringLined(FILE *fp)
819 {
820 	int c, i = 0, len = 0;
821 	char *s = NULL, *s2;
822 
823 
824 	if(fp == NULL)
825 	    return(s);
826 
827 	/* Begin reading string from file */
828 
829 	/* Get first character */
830 	c = fgetc(fp);
831 	if(c == EOF)
832 	    return(s);
833 
834 	/* Read string */
835 	while(1)
836 	{
837 	    /* Need to increase allocation? */
838 	    if(i >= len)
839 	    {
840 		len = MAX(len + 128, i + 1);
841 		s = (char *)realloc(s, len * sizeof(char));
842 		if(s == NULL)
843 		{
844 		    len = i = 0;
845 		    break;
846 		}
847 	    }
848 
849 	    /* Get pointer to current position in string */
850 	    s2 = s + i;
851 
852 	    /* Set new character value */
853 	    *s2 = c;
854 
855 	    /* End of file or end of line? */
856 	    if((c == EOF) || ISCR(c))
857 	    {
858 		*s2 = '\0';
859 		break;
860 	    }
861 	    /* Escape sequence? */
862 	    else if(c == '\\')
863 	    {
864 		/* Read next character after backslash */
865 		c = fgetc(fp);
866 		if(c == EOF)
867 		{
868 		    i++;
869 		    continue;
870 		}
871 		else if(ISCR(c))
872 		{
873 		    /* New line, store it as is */
874 		    *s2 = c;
875 		}
876 		else
877 		{
878 		    /* All other escaped characters leave as is
879 		     * it will be set on the next loop
880 		     */
881 		    i++;
882 		    continue;
883 		}
884 
885 		/* Read next character and increment position */
886 		c = fgetc(fp);
887 		i++;
888 	    }
889 	    /* Regular character */
890 	    else
891 	    {
892 		/* Read next character and increment position */
893 		c = fgetc(fp);
894 		i++;
895 	    }
896 	}
897 
898 	return(s);
899 }
900 
901 /*
902  *      Works just like FGetString() except the string is loaded
903  *	literally and no escape sequences parsed, that would be all
904  *	characters from the current given fp position to the first
905  *	encountered newline ('\n') character (escaped or not).
906  *
907  *	Spaces will not be striped, the fp will be positioned after the
908  *	newline or EOF (whichever is encountered first).
909  */
FGetStringLiteral(FILE * fp)910 char *FGetStringLiteral(FILE *fp)
911 {
912 	int c, i = 0, len = 0;
913 	char *s = NULL, *s2;
914 
915 
916 	if(fp == NULL)
917 	    return(s);
918 
919 	/* Begin reading string from file */
920 
921 	/* Get first character */
922 	c = fgetc(fp);
923 	if(c == EOF)
924 	    return(s);
925 
926 	/* Read string */
927 	while(1)
928 	{
929 	    /* Need to increase allocation? */
930 	    if(i >= len)
931 	    {
932 		len = MAX(len + 128, i + 1);
933 		s = (char *)realloc(s, len * sizeof(char));
934 		if(s == NULL)
935 		{
936 		    len = i = 0;
937 		    break;
938 		}
939 	    }
940 
941 	    /* Get pointer to current position in string */
942 	    s2 = s + i;
943 
944 	    /* Set new character value */
945 	    *s2 = c;
946 
947 	    /* End of file or end of line? */
948 	    if((c == EOF) || ISCR(c))
949 	    {
950 		*s2 = '\0';
951 		break;
952 	    }
953 	    else
954 	    {
955 		/* Read next character and increment position */
956 		c = fgetc(fp);
957 		i++;
958 	    }
959 	}
960 
961 	return(s);
962 }
963 
964 
965 /*
966  *	Returns an allocated string containing the entire
967  *	line or NULL on error or EOF.
968  *
969  *	If comment is '\0' then the next line is read regardless
970  *	if it is a comment or not.
971  *
972  *	Calling function must free() the returned pointer.
973  */
FReadNextLineAlloc(FILE * fp,char comment)974 char *FReadNextLineAlloc(FILE *fp, char comment)
975 {
976 	return(FReadNextLineAllocCount(fp, comment, NULL));
977 }
978 
FReadNextLineAllocCount(FILE * fp,char comment,int * line_count)979 char *FReadNextLineAllocCount(
980 	FILE *fp,
981 	char comment,
982 	int *line_count
983 )
984 {
985 	int i, m, n;
986 	char *strptr;
987 
988 
989 	if(fp == NULL)
990 	    return(NULL);
991 
992 	/* Is comment character specified? */
993 	if(comment != '\0')
994 	{
995 	    /* Comment character is specified */
996 
997 	    /* Read past spaces, newlines, and comments */
998 	    i = fgetc(fp);
999 	    if(i == EOF)
1000 		return(NULL);
1001 
1002 	    while((i == ' ') || (i == '\t') || (i == '\n') || (i == '\r') ||
1003 		  (i == comment)
1004 	    )
1005 	    {
1006 		if(i == EOF)
1007 		    return(NULL);
1008 
1009 		/* If newline, then increment line count */
1010 		if((i == '\n') ||
1011 		   (i == '\r')
1012 		)
1013 		{
1014 		    if(line_count != NULL)
1015 			*line_count += 1;
1016 		}
1017 
1018 		/* If comment, then skip to next line */
1019 		if(i == comment)
1020 		{
1021 		    i = fgetc(fp);
1022 		    while((i != '\n') && (i != '\r'))
1023 		    {
1024 			if(i == EOF)
1025 			    return(NULL);
1026 			i = fgetc(fp);
1027 		    }
1028 		    if(line_count != NULL)
1029 			*line_count += 1;
1030 		}
1031 
1032 		/* Get next character */
1033 		i = fgetc(fp);
1034 	    }
1035 
1036 	    /* Begin adding characters to string */
1037 	    m = 0;	/* mem size */
1038 	    n = 1;	/* chars read */
1039 	    strptr = NULL;
1040 
1041 	    while((i != '\n') && (i != '\r') && (i != '\0'))
1042 	    {
1043 		/* Escape character? */
1044 		if(i == '\\')
1045 		{
1046 		    /* Read next character */
1047 		    i = fgetc(fp);
1048 
1049 		    /* Skip newlines internally */
1050 		    if((i == '\n') || (i == '\r'))
1051 		    {
1052 			i = fgetc(fp);
1053 
1054 			/* Still counts as a line though! */
1055 			if(line_count != NULL)
1056 			    *line_count += 1;
1057 		    }
1058 		}
1059 
1060 		if(i == EOF)
1061 		    break;
1062 
1063 		/* Allocate more memory as needed */
1064 		if(m < n)
1065 		{
1066 		    /* Allocate FREAD_ALLOC_CHUNK_SIZE more bytes */
1067 		    m += FREAD_ALLOC_CHUNK_SIZE;
1068 
1069 		    strptr = (char *)realloc(strptr, m * sizeof(char));
1070 		    if(strptr == NULL)
1071 			return(NULL);
1072 		}
1073 
1074 		strptr[n - 1] = (char)i;
1075 
1076 		/* Read next character from file */
1077 		i = fgetc(fp);
1078 		n++;	/* Increment characters read */
1079 	    }
1080 
1081 	    /* Add newline and null terminate */
1082 	    m += 2;	/* 2 more chars */
1083 	    strptr = (char *)realloc(strptr, m * sizeof(char));
1084 	    if(strptr == NULL)
1085 		return(NULL);
1086 	    strptr[n - 1] = '\n';
1087 	    strptr[n] = '\0';
1088 
1089 	    /* Increment line count */
1090 	    if(line_count != NULL)
1091 		*line_count += 1;
1092 	}
1093 	else
1094 	{
1095 	    /* Comment character is not specified */
1096 
1097 	    i = fgetc(fp);
1098 	    if(i == EOF)
1099 		return(NULL);
1100 
1101 	    /* Begin adding characters to string */
1102 	    m = 0;      /* Memory size */
1103 	    n = 1;      /* Characters read */
1104 	    strptr = NULL;	/* Return string */
1105 
1106 	    while((i != '\n') && (i != '\r') && (i != '\0'))
1107 	    {
1108 		/* Escape character? */
1109 		if(i == '\\')
1110 		{
1111 		    /* Read next character */
1112 		    i = fgetc(fp);
1113 
1114 		    /* Skip newlines internally */
1115 		    if((i == '\n') || (i == '\r'))
1116 		    {
1117 			i = fgetc(fp);
1118 
1119 			/* Still counts as a line though! */
1120 			if(line_count != NULL)
1121 			    *line_count += 1;
1122 		    }
1123 		}
1124 
1125 		if(i == EOF)
1126 		    break;
1127 
1128 		/* Allocate more memory as needed */
1129 		if(m < n)
1130 		{
1131 		    /* Allocate FREAD_ALLOC_CHUNK_SIZE more bytes */
1132 		    m += FREAD_ALLOC_CHUNK_SIZE;
1133 
1134 		    strptr = (char *)realloc(strptr, m * sizeof(char));
1135 		    if(strptr == NULL)
1136 			return(NULL);
1137 		}
1138 
1139 		strptr[n - 1] = (char)i;
1140 
1141 		/* Read next character from file */
1142 		i = fgetc(fp);
1143 		n++;	/* Increment characters read */
1144 	    }
1145 
1146 	    /* Add newline and null terminate */
1147 	    m += 2;	/* 2 more chars */
1148 	    strptr = (char *)realloc(strptr, m * sizeof(char));
1149 	    strptr[n - 1] = '\n';
1150 	    strptr[n] = '\0';
1151 
1152 	    /* Increment line count */
1153 	    if(line_count != NULL)
1154 		*line_count += 1;
1155 	}
1156 
1157 
1158 	return(strptr);
1159 }
1160