xref: /titanic_52/usr/src/cmd/sdiff/sdiff.c (revision bdfc6d18da790deeec2e0eb09c625902defe2498)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 	/*
34 	 *	sdiff [-l] [-s] [-w #] [-o output] file1 file2
35 	 *	does side by side diff listing
36 	 *	-l leftside only for identical lines
37 	 *	-s silent; only print differences
38 	 *	-w # width of output
39 	 *	-o output  interactive creation of new output commands:
40 	 *		s	silent; do not print identical lines
41 	 *		v	turn off silent
42 	 *		l	copy left side to output
43 	 *		r	copy right side to output
44 	 *		e l	call ed with left side
45 	 *		e r	call ed with right side
46 	 *		e b	call ed with cat of left and right
47 	 *		e	call ed with empty file
48 	 *		q	exit from program
49 	 *
50 	 *	functions:
51 	 *	cmd	decode diff commands
52 	 *	put1	output left side
53 	 *	put2	output right side
54 	 *	putmid	output gutter
55 	 *	putline	output n chars to indicated file
56 	 *	getlen	calculate length of strings with tabs
57 	 *	cmdin	read and process interactive cmds
58 	 *	cpp	copy from file to file
59 	 *	edit	call ed with file
60 	 */
61 
62 #include <stdio.h>
63 #include <ctype.h>
64 #include <signal.h>
65 #include <sys/types.h>
66 #include <sys/stat.h>
67 #include <sys/wait.h>
68 #include <unistd.h>
69 #include <stdlib.h>
70 #include <locale.h>
71 #include <limits.h>
72 #include <string.h>
73 #include <wchar.h>
74 
75 #define	LMAX	BUFSIZ
76 #define	BMAX	BUFSIZ
77 #define	STDOUT	1
78 #define	WGUTTER	6
79 #define	WLEN	(WGUTTER * 2 + WGUTTER + 2)
80 #define	PROMPT	'%'
81 
82 static const char	twoblanks[3] = "  ";
83 
84 static const char	*DIFF	= "diff -b ";
85 static char	diffcmd[BMAX];
86 static char	inbuf[10];
87 
88 static int	llen = 130;	/* Default maximum line length written out */
89 static int	hlen;		/* Half line length with space for gutter */
90 static int	len1;		/* Calculated length of left side */
91 static int	nchars;		/* Number of characters in left side - */
92 					/* used for tab expansion */
93 static char	change = ' ';
94 static int	leftonly = 0;	/* if set print left side only for */
95 					/* identical lines */
96 static int	silent = 0;	/* if set do not print identical lines */
97 static int	midflg = 0;	/* set after middle was output */
98 static int	rcode = 0;	/* return code */
99 
100 
101 static char	*file1;
102 static FILE	*fdes1;
103 
104 static char	*file2;
105 static FILE	*fdes2;
106 
107 static FILE	*diffdes;
108 
109 static int oflag;
110 static char	*ofile;
111 static FILE	*odes;
112 
113 static char	*ltemp;
114 static FILE	*left;
115 
116 static char	*rtemp;
117 static FILE	*right;
118 
119 static FILE *tempdes;
120 static char *temp;
121 
122 /* decoded diff cmd- left side from to; right side from, to */
123 
124 static int from1, to1, from2, to2;
125 
126 static int num1, num2;		/* line count for left side file and right */
127 static int tempfd = -1;
128 
129 static char	*filename(char *, char *);
130 static char	*fgetline(FILE *);
131 static int	put1(void);
132 static int	put2(void);
133 static void	putline(FILE *, char *, int);
134 static int	cmd(char *);
135 static int	getlen(int, char *);
136 static void	putmid(int);
137 static void	error(char *, char *);
138 static void	onintr(void);
139 static void	sremove(void);
140 static void	cmdin(void);
141 static void	cpp(char *, FILE *, FILE *);
142 static void	edit(char *);
143 
144 int
145 main(int argc, char **argv)
146 {
147 	int	com;
148 	int	n1, n2, n;
149 	char	*bp;
150 	int	lfd = -1;
151 	int	rfd = -1;
152 
153 	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
154 		(void) signal((int)SIGHUP, (void (*)(int))onintr);
155 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
156 		(void) signal((int)SIGINT, (void (*)(int))onintr);
157 	if (signal(SIGPIPE, SIG_IGN) != SIG_IGN)
158 		(void) signal((int)SIGPIPE, (void (*)(int))onintr);
159 	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
160 		(void) signal((int)SIGTERM, (void (*)(int))onintr);
161 
162 	(void) setlocale(LC_ALL, "");
163 #if	!defined(TEXT_DOMAIN)
164 #define	TEXT_DOMAIN	"SYS_TEST"
165 #endif
166 	(void) textdomain(TEXT_DOMAIN);
167 
168 	while (--argc > 1 && **++argv == '-') {
169 		switch (*++*argv) {
170 
171 		case 'w':
172 			/* -w# instead of -w # */
173 			if (*++*argv)
174 				llen = atoi(*argv);
175 			else {
176 				argc--;
177 				llen = atoi(*++argv);
178 			}
179 			if (llen < WLEN)
180 				error(gettext("Wrong line length %s"), *argv);
181 			if (llen > LMAX)
182 				llen = LMAX;
183 			break;
184 
185 		case 'l':
186 			leftonly++;
187 			break;
188 
189 		case 's':
190 			silent++;
191 			break;
192 		case 'o':
193 			oflag++;
194 			argc--;
195 			ofile = *++argv;
196 			break;
197 		default:
198 			error(gettext("Illegal argument: %s"), *argv);
199 		}
200 	}
201 	if (argc != 2) {
202 		(void) fprintf(stderr, gettext(
203 		"Usage: sdiff [-l] [-s] [-o output] [-w #] file1 file2\n"));
204 		return (2);
205 	}
206 
207 	file1 = *argv++;
208 	file2 = *argv;
209 	file1 = filename(file1, file2);
210 	file2 = filename(file2, file1);
211 	hlen = (llen - WGUTTER +1)/2;
212 
213 	if ((fdes1 = fopen(file1, "r")) == NULL)
214 		error(gettext("Cannot open: %s"), file1);
215 
216 	if ((fdes2 = fopen(file2, "r")) == NULL)
217 		error(gettext("Cannot open: %s"), file2);
218 
219 	if (oflag) {
220 		if (tempfd == -1) {
221 			temp = strdup("/tmp/sdiffXXXXXX");
222 			tempfd = mkstemp(temp);
223 			if (tempfd == -1) {
224 				error(gettext(
225 					"Cannot open/create temp %s"), temp);
226 				free(temp);
227 				temp = 0;
228 			}
229 		}
230 		ltemp = strdup("/tmp/sdifflXXXXXX");
231 		if ((lfd = mkstemp(ltemp)) == -1 ||
232 			(left = fdopen(lfd, "w")) == NULL)
233 				error(gettext(
234 					"Cannot open/create temp %s"),
235 					ltemp);
236 		rtemp = strdup("/tmp/sdiffrXXXXXX");
237 		if ((rfd = mkstemp(rtemp)) == -1 ||
238 			(right = fdopen(rfd, "w")) == NULL)
239 				error(gettext(
240 					"Cannot open/create temp file %s"),
241 					rtemp);
242 		if ((odes = fopen(ofile, "w")) == NULL)
243 			error(gettext("Cannot open output %s"), ofile);
244 	}
245 	/* Call DIFF command */
246 	(void) strcpy(diffcmd, DIFF);
247 	(void) strcat(diffcmd, file1);
248 	(void) strcat(diffcmd, " ");
249 	(void) strcat(diffcmd, file2);
250 	diffdes = popen(diffcmd, "r");
251 
252 	num1 = num2 = 0;
253 
254 	/*
255 	 * Read in diff output and decode commands
256 	 * "change" is used to determine character to put in gutter
257 	 *  num1 and num2 counts the number of lines in file1 and 2
258 	 */
259 
260 	n = 0;
261 	while ((bp = fgetline(diffdes)) != NULL) {
262 		change = ' ';
263 		com = cmd(bp);
264 
265 	/*
266 	 * handles all diff output that is not cmd
267 	 * lines starting with <, >, ., ---
268 	 */
269 		if (com == 0)
270 			continue;
271 
272 	/* Catch up to from1 and from2 */
273 		rcode = 1;
274 		n1 = from1 - num1;
275 		n2 = from2 - num2;
276 		n = n1 > n2 ? n2 : n1;
277 		if (com == 'c' && n > 0)
278 			n--;
279 		if (silent)
280 			(void) fputs(bp, stdout);
281 		while (n-- > 0) {
282 			(void) put1();
283 			(void) put2();
284 			if (!silent)
285 				(void) putc('\n', stdout);
286 			midflg = 0;
287 		}
288 
289 	/* Process diff cmd */
290 		switch (com) {
291 
292 		case 'a':
293 			change = '>';
294 			while (num2 < to2) {
295 				(void) put2();
296 				(void) putc('\n', stdout);
297 				midflg = 0;
298 			}
299 			break;
300 
301 		case 'd':
302 			change = '<';
303 			while (num1 < to1) {
304 				(void) put1();
305 				(void) putc('\n', stdout);
306 				midflg = 0;
307 			}
308 			break;
309 
310 		case 'c':
311 			n1 = to1 - from1;
312 			n2 = to2 - from2;
313 			n = n1 > n2 ? n2 : n1;
314 			change = '|';
315 			do {
316 				(void) put1();
317 				(void) put2();
318 				(void) putc('\n', stdout);
319 				midflg = 0;
320 			} while (n--);
321 
322 			change = '<';
323 			while (num1 < to1) {
324 				(void) put1();
325 				(void) putc('\n', stdout);
326 				midflg = 0;
327 			}
328 
329 			change = '>';
330 			while (num2 < to2) {
331 				(void) put2();
332 				(void) putc('\n', stdout);
333 				midflg = 0;
334 			}
335 			break;
336 
337 		default:
338 			(void) fprintf(stderr, gettext(
339 				"%c: cmd not found\n"), cmd);
340 			break;
341 		}
342 
343 		if (oflag == 1 && com != 0) {
344 			cmdin();
345 			if ((left = fopen(ltemp, "w")) == NULL)
346 				error(gettext(
347 					"main: Cannot open temp %s"), ltemp);
348 			if ((right = fopen(rtemp, "w")) == NULL)
349 				error(gettext(
350 					"main: Cannot open temp %s"), rtemp);
351 		}
352 	}
353 	/* put out remainder of input files */
354 
355 	while (put1()) {
356 		(void) put2();
357 		if (!silent)
358 			(void) putc('\n', stdout);
359 		midflg = 0;
360 	}
361 	if (odes)
362 		(void) fclose(odes);
363 	sremove();
364 	return (rcode);
365 }
366 
367 static int
368 put1(void)
369 {
370 	/* len1 = length of left side */
371 	/* nchars = num of chars including tabs */
372 
373 	char	*bp;
374 
375 
376 	if ((bp = fgetline(fdes1)) != NULL) {
377 		len1 = getlen(0, bp);
378 		if ((!silent || change != ' ') && len1 != 0)
379 			putline(stdout, bp, nchars);
380 
381 		if (oflag) {
382 		/*
383 		 * put left side either to output file
384 		 * if identical to right
385 		 * or left temp file if not
386 		 */
387 
388 			if (change == ' ')
389 				putline(odes, bp, strlen(bp));
390 			else
391 				putline(left, bp, strlen(bp));
392 		}
393 		if (change != ' ')
394 			putmid(1);
395 		num1++;
396 		return (1);
397 	} else
398 		return (0);
399 }
400 
401 static int
402 put2(void)
403 {
404 	char	*bp;
405 
406 	if ((bp = fgetline(fdes2)) != NULL) {
407 		(void) getlen((hlen + WGUTTER) % 8, bp);
408 
409 		/*
410 		 * if the left and right are different they are always
411 		 * printed.
412 		 * If the left and right are identical
413 		 * right is only printed if leftonly is not specified
414 		 * or silent mode is not specified
415 		 * or the right contains other than white space (len1 !=0)
416 		 */
417 		if (change != ' ') {
418 
419 		/*
420 		 * put right side to right temp file only
421 		 * because left side was written to output for
422 		 * identical lines
423 		 */
424 
425 			if (oflag)
426 				putline(right, bp, strlen(bp));
427 
428 			if (midflg == 0)
429 				putmid(1);
430 			putline(stdout, bp, nchars);
431 		} else
432 			if (!silent && !leftonly && len1 != 0) {
433 				if (midflg == 0)
434 					putmid(1);
435 				putline(stdout, bp, nchars);
436 			}
437 		num2++;
438 		len1 = 0;
439 		return (1);
440 	} else {
441 		len1 = 0;
442 		return (0);
443 	}
444 }
445 
446 static void
447 putline(FILE *file, char *start, int num)
448 {
449 	char	*cp, *end;
450 	int	i, len, d_col;
451 	wchar_t	wc;
452 
453 	cp = start;
454 	end = cp + num;
455 	while (cp < end) {
456 		if (isascii(*cp)) {
457 			(void) putc(*cp++, file);
458 			continue;
459 		}
460 
461 		if ((len = end - cp) > MB_LEN_MAX)
462 			len = MB_LEN_MAX;
463 
464 		if ((len = mbtowc(&wc, cp, len)) <= 0) {
465 			(void) putc(*cp++, file);
466 			continue;
467 		}
468 
469 		if ((d_col = wcwidth(wc)) <= 0)
470 			d_col = len;
471 
472 		if ((cp + d_col) > end)
473 			return;
474 
475 		for (i = 0; i < len; i++)
476 			(void) putc(*cp++, file);
477 	}
478 }
479 
480 static int
481 cmd(char *start)
482 {
483 	unsigned char	*cp;
484 	char	*cps;
485 	int	com;
486 
487 	if (*start == '>' || *start == '<' || *start == '-' || *start == '.')
488 		return (0);
489 
490 	cp = (unsigned char *)start;
491 	cps = start;
492 	while (isdigit(*cp))
493 		cp++;
494 	from1 = atoi(cps);
495 	to1 = from1;
496 	if (*cp == ',') {
497 		cp++;
498 		cps = (char *)cp;
499 		while (isdigit(*cp))
500 			cp++;
501 		to1 = atoi(cps);
502 	}
503 
504 	com = *cp++;
505 	cps = (char *)cp;
506 
507 	while (isdigit(*cp))
508 		cp++;
509 	from2 = atoi(cps);
510 	to2 = from2;
511 	if (*cp == ',') {
512 		cp++;
513 		cps = (char *)cp;
514 		while (isdigit(*cp))
515 			cp++;
516 		to2 = atoi(cps);
517 	}
518 	return (com);
519 }
520 
521 static int
522 getlen(int startpos, char *buffer)
523 {
524 	/*
525 	 * get the length of the string in buffer
526 	 *  expand tabs to next multiple of 8
527 	 */
528 	unsigned char	*cp;
529 	int	slen, tlen, len, d_col;
530 	int	notspace;
531 	wchar_t	wc;
532 
533 	nchars = 0;
534 	notspace = 0;
535 	tlen = startpos;
536 	for (cp = (unsigned char *)buffer; (*cp != '\n') && (*cp); cp++) {
537 		if (*cp == '\t') {
538 			slen = tlen;
539 			tlen += 8 - (tlen % 8);
540 			if (tlen >= hlen) {
541 				tlen = slen;
542 				break;
543 			}
544 			nchars++;
545 			continue;
546 		}
547 
548 		if (isascii(*cp)) {
549 			slen = tlen;
550 			tlen++;
551 			if (tlen >= hlen) {
552 				tlen = slen;
553 				break;
554 			}
555 			if (!isspace(*cp))
556 				notspace = 1;
557 			nchars++;
558 			continue;
559 		}
560 
561 		if ((len = mbtowc(&wc, (char *)cp, MB_LEN_MAX)) <= 0) {
562 			slen = tlen;
563 			tlen++;
564 			if (tlen >= hlen) {
565 				tlen = slen;
566 				break;
567 			}
568 			notspace = 1;
569 			nchars++;
570 			continue;
571 		}
572 
573 		if ((d_col = wcwidth(wc)) <= 0)
574 			d_col = len;
575 
576 		slen = tlen;
577 		tlen += d_col;
578 		if (tlen > hlen) {
579 			tlen = slen;
580 			break;
581 		}
582 		notspace = 1;
583 		cp += len - 1;
584 		nchars += len;
585 	}
586 	return (notspace ? tlen : 0);
587 }
588 
589 static void
590 putmid(int bflag)
591 {
592 	int	i;
593 
594 	/*
595 	 * len1 set by getlen to the possibly truncated
596 	 *  length of left side
597 	 *  hlen is length of half line
598 	 */
599 
600 	midflg = 1;
601 	if (bflag) {
602 		for (i = 0; i < hlen - len1; i++)
603 			(void) putc(' ', stdout);
604 	}
605 	(void) fputs(twoblanks, stdout);
606 	(void) putc((int)change, stdout);
607 	(void) fputs(twoblanks, stdout);
608 }
609 
610 static void
611 error(char *s1, char *s2)
612 {
613 	(void) fprintf(stderr, "sdiff: ");
614 	(void) fprintf(stderr, s1, s2);
615 	(void) putc('\n', stderr);
616 	sremove();
617 	exit(2);
618 }
619 
620 static void
621 onintr(void)
622 {
623 	sremove();
624 	exit(rcode);
625 }
626 
627 static void
628 sremove(void)
629 {
630 	if (ltemp) {
631 		(void) unlink(ltemp);
632 		free(ltemp);
633 	}
634 	if (rtemp) {
635 		(void) unlink(rtemp);
636 		free(rtemp);
637 	}
638 	if (temp) {
639 		(void) unlink(temp);
640 		free(temp);
641 	}
642 }
643 
644 static void
645 cmdin(void)
646 {
647 	char	*cp, *ename;
648 	int	notacc;
649 
650 	(void) fclose(left);
651 	(void) fclose(right);
652 	notacc = 1;
653 	while (notacc) {
654 		(void) putc(PROMPT, stdout);
655 		if ((cp = fgets(inbuf, 10, stdin)) == NULL) {
656 			(void) putc('\n', stdout);
657 			break;
658 		}
659 		switch (*cp) {
660 
661 		case 's':
662 			silent = 1;
663 			break;
664 
665 		case 'v':
666 			silent = 0;
667 			break;
668 
669 		case 'q':
670 			sremove();
671 			exit(rcode);
672 			/* NOTREACHED */
673 			break;
674 
675 		case 'l':
676 			cpp(ltemp, left, odes);
677 			notacc = 0;
678 			break;
679 
680 		case 'r':
681 			cpp(rtemp, right, odes);
682 			notacc = 0;
683 			break;
684 
685 		case 'e':
686 			while (*++cp == ' ')
687 				;
688 			switch (*cp) {
689 			case 'l':
690 			case '<':
691 				notacc = 0;
692 				ename = ltemp;
693 				edit(ename);
694 				break;
695 
696 			case 'r':
697 			case '>':
698 				notacc = 0;
699 				ename = rtemp;
700 				edit(ename);
701 				break;
702 
703 			case 'b':
704 			case '|':
705 				if ((tempdes = fopen(temp, "w")) == NULL)
706 					error(gettext(
707 						"Cannot open temp file %s"),
708 						temp);
709 				cpp(ltemp, left, tempdes);
710 				cpp(rtemp, right, tempdes);
711 				(void) fclose(tempdes);
712 				notacc = 0;
713 				ename = temp;
714 				edit(ename);
715 				break;
716 
717 			case '\n':
718 				if ((tempdes = fopen(temp, "w")) == NULL)
719 					error(gettext(
720 						"Cannot open temp file %s"),
721 						temp);
722 				(void) fclose(tempdes);
723 				notacc = 0;
724 				ename = temp;
725 				edit(ename);
726 				break;
727 			default:
728 				(void) fprintf(stderr, gettext(
729 					"Illegal command %s reenter\n"),
730 					cp);
731 				break;
732 			}
733 			if (notacc == 0)
734 				cpp(ename, tempdes, odes);
735 			break;
736 
737 		default:
738 			(void) fprintf(stderr, gettext(
739 				"Illegal command reenter\n"));
740 			break;
741 		}
742 	}
743 }
744 
745 static void
746 cpp(char *from, FILE *fromdes, FILE *todes)
747 {
748 	char	tempbuf[BMAX + 1];
749 
750 	if ((fromdes = fopen(from, "r")) == NULL)
751 		error(gettext(
752 			"cpp: Cannot open %s"), from);
753 	while ((fgets(tempbuf, BMAX, fromdes) != NULL))
754 		(void) fputs(tempbuf, todes);
755 	(void) fclose(fromdes);
756 }
757 
758 static void
759 edit(char *file)
760 {
761 	int	i;
762 	pid_t	pid;
763 	void (*oldintr)(int);
764 
765 	switch (pid = fork()) {
766 	case (pid_t)-1:
767 		error(gettext("Cannot fork"), NULL);
768 		/* NOTREACHED */
769 		break;
770 	case (pid_t)0:
771 		(void) execl("/usr/bin/ed", "ed", file, NULL);
772 	}
773 
774 	oldintr = signal(SIGINT, SIG_IGN);	/* ignore interrupts in ed */
775 	while (pid != wait(&i))
776 		;
777 	/* restore previous interrupt proc */
778 	(void) signal(SIGINT, oldintr);
779 }
780 
781 static char *
782 filename(char *pa1, char *pa2)
783 {
784 	int	c;
785 	char 	*a1, *b1, *a2;
786 	struct stat	stbuf;
787 	a1 = pa1;
788 	a2 = pa2;
789 	if (stat(a1, &stbuf) != -1 && ((stbuf.st_mode&S_IFMT) == S_IFDIR)) {
790 		b1 = pa1 = (char *)malloc(strlen(a1) + strlen(a2) + 2);
791 		while (*b1++ = *a1++);
792 		b1[-1] = '/';
793 		a1 = b1;
794 		while (*a1++ = *a2++)
795 			if (*a2 && *a2 != '/' && a2[-1] == '/')
796 				a1 = b1;
797 	} else if (a1[0] == '-' && a1[1] == 0 && temp == 0) {
798 		if (fstat(fileno(stdin), &stbuf) == -1)
799 			error(gettext("Cannot process stdin"), NULL);
800 		pa1 = temp = strdup("/tmp/sdiffXXXXXX");
801 		if ((tempfd = mkstemp(temp)) == -1 ||
802 			(tempdes = fdopen(tempfd, "w")) == NULL)
803 				error(gettext("Cannot open/create temp %s"),
804 					temp);
805 		while ((c = getc(stdin)) != EOF)
806 			(void) putc(c, tempdes);
807 		(void) fclose(tempdes);
808 	}
809 	return (pa1);
810 }
811 
812 /*
813  * like fgets, but reads upto and including a newline,
814  * the data is stored in a reusable dynamic buffer that grows to fit
815  * the largest line in the file, the buffer is NULL terminated
816  * returns a pointer to the dynamic buffer.
817  */
818 static char *
819 fgetline(FILE *fp)
820 {
821 	static char	*bp = NULL;
822 	static int	blen = 0;
823 	int	sl;
824 
825 	if (bp == NULL) {
826 		/* allocate it for the first time */
827 		bp = (char *)malloc(BUFSIZ);
828 		if (bp == NULL)
829 			error(gettext("fgetline: malloc failed"), NULL);
830 		blen = BUFSIZ;
831 	}
832 
833 	/* check for error or nothing read */
834 	if (fgets(bp, blen, fp) == NULL)
835 		return (NULL);
836 
837 	if (feof(fp))
838 		return (bp);
839 
840 	while ((sl = strlen(bp)) == blen-1 && *(bp+blen-2) != '\n') {
841 		/* still more data, grow the buffer */
842 		blen *= 2;
843 		bp = (char *)realloc(bp, blen);
844 		if (bp == NULL)
845 			error(gettext("fgetline: realloc failed"), NULL);
846 		/* continue reading and add to end of buffer */
847 		(void) fgets(bp+sl, blen-sl, fp);
848 	}
849 	return (bp);
850 }
851