xref: /onnv-gate/usr/src/cmd/sort/common/streams.c (revision 5836:a07f85025dc3)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*5836Snakanon  * Common Development and Distribution License (the "License").
6*5836Snakanon  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*5836Snakanon  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include "streams.h"
290Sstevel@tonic-gate 
300Sstevel@tonic-gate static const stream_ops_t invalid_ops = {
310Sstevel@tonic-gate 	NULL,
320Sstevel@tonic-gate 	NULL,
330Sstevel@tonic-gate 	NULL,
340Sstevel@tonic-gate 	NULL,
350Sstevel@tonic-gate 	NULL,
360Sstevel@tonic-gate 	NULL,
370Sstevel@tonic-gate 	NULL,
380Sstevel@tonic-gate 	NULL,
390Sstevel@tonic-gate 	NULL,
400Sstevel@tonic-gate 	NULL
410Sstevel@tonic-gate };
420Sstevel@tonic-gate 
430Sstevel@tonic-gate stream_t *
stream_new(int src)440Sstevel@tonic-gate stream_new(int src)
450Sstevel@tonic-gate {
460Sstevel@tonic-gate 	stream_t *str = safe_realloc(NULL, sizeof (stream_t));
470Sstevel@tonic-gate 
480Sstevel@tonic-gate 	stream_clear(str);
490Sstevel@tonic-gate 	stream_set(str, src);
500Sstevel@tonic-gate 
510Sstevel@tonic-gate 	return (str);
520Sstevel@tonic-gate }
530Sstevel@tonic-gate 
540Sstevel@tonic-gate void
stream_set(stream_t * str,flag_t flags)550Sstevel@tonic-gate stream_set(stream_t *str, flag_t flags)
560Sstevel@tonic-gate {
570Sstevel@tonic-gate 	if (flags & STREAM_SOURCE_MASK) {
580Sstevel@tonic-gate 		ASSERT((flags & STREAM_SOURCE_MASK) == STREAM_ARRAY ||
590Sstevel@tonic-gate 		    (flags & STREAM_SOURCE_MASK) == STREAM_SINGLE ||
600Sstevel@tonic-gate 		    (flags & STREAM_SOURCE_MASK) == STREAM_MMAP ||
610Sstevel@tonic-gate 		    (flags & STREAM_SOURCE_MASK) == STREAM_WIDE);
620Sstevel@tonic-gate 
630Sstevel@tonic-gate 		str->s_status &= ~STREAM_SOURCE_MASK;
640Sstevel@tonic-gate 		str->s_status |= flags & STREAM_SOURCE_MASK;
650Sstevel@tonic-gate 
660Sstevel@tonic-gate 		switch (flags & STREAM_SOURCE_MASK) {
670Sstevel@tonic-gate 			case STREAM_NO_SOURCE:
680Sstevel@tonic-gate 				str->s_element_size = 0;
690Sstevel@tonic-gate 				str->s_ops = invalid_ops;
700Sstevel@tonic-gate 				return;
710Sstevel@tonic-gate 			case STREAM_ARRAY:
720Sstevel@tonic-gate 				/*
730Sstevel@tonic-gate 				 * Array streams inherit element size.
740Sstevel@tonic-gate 				 */
750Sstevel@tonic-gate 				str->s_ops = stream_array_ops;
760Sstevel@tonic-gate 				break;
770Sstevel@tonic-gate 			case STREAM_MMAP:
780Sstevel@tonic-gate 				str->s_element_size = sizeof (char);
790Sstevel@tonic-gate 				str->s_ops = stream_mmap_ops;
800Sstevel@tonic-gate 				break;
810Sstevel@tonic-gate 			case STREAM_SINGLE:
820Sstevel@tonic-gate 				str->s_element_size = sizeof (char);
830Sstevel@tonic-gate 				str->s_ops = stream_stdio_ops;
840Sstevel@tonic-gate 				break;
850Sstevel@tonic-gate 			case STREAM_WIDE:
860Sstevel@tonic-gate 				str->s_element_size = sizeof (wchar_t);
870Sstevel@tonic-gate 				str->s_ops = stream_wide_ops;
880Sstevel@tonic-gate 				break;
890Sstevel@tonic-gate 			default:
900Sstevel@tonic-gate 				die(EMSG_UNKN_STREAM, str->s_status);
910Sstevel@tonic-gate 		}
920Sstevel@tonic-gate 	}
930Sstevel@tonic-gate 
940Sstevel@tonic-gate 	str->s_status |= (flags & ~STREAM_SOURCE_MASK);
950Sstevel@tonic-gate 
960Sstevel@tonic-gate 	if (str->s_status & STREAM_UNIQUE)
970Sstevel@tonic-gate 		switch (str->s_status & STREAM_SOURCE_MASK) {
980Sstevel@tonic-gate 			case STREAM_SINGLE :
990Sstevel@tonic-gate 				str->s_ops.sop_put_line =
1000Sstevel@tonic-gate 				    stream_stdio_put_line_unique;
1010Sstevel@tonic-gate 				break;
1020Sstevel@tonic-gate 			case STREAM_WIDE :
1030Sstevel@tonic-gate 				str->s_ops.sop_put_line =
1040Sstevel@tonic-gate 				    stream_wide_put_line_unique;
1050Sstevel@tonic-gate 				break;
1060Sstevel@tonic-gate 			default :
1070Sstevel@tonic-gate 				break;
1080Sstevel@tonic-gate 		}
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate 	if (str->s_status & STREAM_INSTANT)
1110Sstevel@tonic-gate 		switch (str->s_status & STREAM_SOURCE_MASK) {
1120Sstevel@tonic-gate 			case STREAM_SINGLE :
1130Sstevel@tonic-gate 				str->s_ops.sop_fetch =
1140Sstevel@tonic-gate 				    stream_stdio_fetch_overwrite;
1150Sstevel@tonic-gate 				break;
1160Sstevel@tonic-gate 			case STREAM_WIDE :
1170Sstevel@tonic-gate 				str->s_ops.sop_fetch =
1180Sstevel@tonic-gate 				    stream_wide_fetch_overwrite;
1190Sstevel@tonic-gate 				break;
1200Sstevel@tonic-gate 			default :
1210Sstevel@tonic-gate 				break;
1220Sstevel@tonic-gate 		}
1230Sstevel@tonic-gate }
1240Sstevel@tonic-gate 
1250Sstevel@tonic-gate void
stream_unset(stream_t * streamp,flag_t flags)1260Sstevel@tonic-gate stream_unset(stream_t *streamp, flag_t flags)
1270Sstevel@tonic-gate {
1280Sstevel@tonic-gate 	ASSERT(!(flags & STREAM_SOURCE_MASK));
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate 	streamp->s_status &= ~(flags & ~STREAM_SOURCE_MASK);
1310Sstevel@tonic-gate }
1320Sstevel@tonic-gate 
1330Sstevel@tonic-gate int
stream_is_primed(stream_t * streamp)1340Sstevel@tonic-gate stream_is_primed(stream_t *streamp)
1350Sstevel@tonic-gate {
1360Sstevel@tonic-gate 	return (streamp->s_status & STREAM_PRIMED);
1370Sstevel@tonic-gate }
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate void
stream_clear(stream_t * str)1400Sstevel@tonic-gate stream_clear(stream_t *str)
1410Sstevel@tonic-gate {
1420Sstevel@tonic-gate 	(void) memset(str, 0, sizeof (stream_t));
1430Sstevel@tonic-gate }
1440Sstevel@tonic-gate 
1450Sstevel@tonic-gate static void
stream_copy(stream_t * dest,stream_t * src)1460Sstevel@tonic-gate stream_copy(stream_t *dest, stream_t *src)
1470Sstevel@tonic-gate {
1480Sstevel@tonic-gate 	(void) memcpy(dest, src, sizeof (stream_t));
1490Sstevel@tonic-gate }
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate void
stream_stat_chain(stream_t * strp)1520Sstevel@tonic-gate stream_stat_chain(stream_t *strp)
1530Sstevel@tonic-gate {
1540Sstevel@tonic-gate 	struct stat buf;
1550Sstevel@tonic-gate 	stream_t *cur_strp = strp;
1560Sstevel@tonic-gate 
1570Sstevel@tonic-gate 	while (cur_strp != NULL) {
1580Sstevel@tonic-gate 		if (cur_strp->s_status & STREAM_NOTFILE ||
1590Sstevel@tonic-gate 		    cur_strp->s_status & STREAM_ARRAY) {
1600Sstevel@tonic-gate 			cur_strp = cur_strp->s_next;
1610Sstevel@tonic-gate 			continue;
1620Sstevel@tonic-gate 		}
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate 		if (stat(cur_strp->s_filename, &buf) < 0)
1650Sstevel@tonic-gate 			die(EMSG_STAT, cur_strp->s_filename);
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate 		cur_strp->s_dev = buf.st_dev;
1680Sstevel@tonic-gate 		cur_strp->s_ino = buf.st_ino;
1690Sstevel@tonic-gate 		cur_strp->s_filesize = buf.st_size;
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate 		cur_strp = cur_strp->s_next;
1720Sstevel@tonic-gate 	}
1730Sstevel@tonic-gate }
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate uint_t
stream_count_chain(stream_t * str)1760Sstevel@tonic-gate stream_count_chain(stream_t *str)
1770Sstevel@tonic-gate {
1780Sstevel@tonic-gate 	uint_t n = 0;
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate 	while (str != NULL) {
1810Sstevel@tonic-gate 		n++;
1820Sstevel@tonic-gate 		str = str->s_next;
1830Sstevel@tonic-gate 	}
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate 	return (n);
1860Sstevel@tonic-gate }
1870Sstevel@tonic-gate 
1880Sstevel@tonic-gate int
stream_open_for_read(sort_t * S,stream_t * str)1890Sstevel@tonic-gate stream_open_for_read(sort_t *S, stream_t *str)
1900Sstevel@tonic-gate {
1910Sstevel@tonic-gate 	int fd;
1920Sstevel@tonic-gate 
1930Sstevel@tonic-gate 	ASSERT(!(str->s_status & STREAM_OUTPUT));
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 	/*
1960Sstevel@tonic-gate 	 * STREAM_ARRAY streams are open by definition.
1970Sstevel@tonic-gate 	 */
1980Sstevel@tonic-gate 	if ((str->s_status & STREAM_SOURCE_MASK) == STREAM_ARRAY) {
1990Sstevel@tonic-gate 		stream_set(str, STREAM_ARRAY | STREAM_OPEN);
2000Sstevel@tonic-gate 		return (1);
2010Sstevel@tonic-gate 	}
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate 	/*
2040Sstevel@tonic-gate 	 * Set data type according to locale for input from stdin.
2050Sstevel@tonic-gate 	 */
2060Sstevel@tonic-gate 	if (str->s_status & STREAM_NOTFILE) {
2070Sstevel@tonic-gate 		str->s_type.BF.s_fp = stdin;
2080Sstevel@tonic-gate 		stream_set(str, STREAM_OPEN | (S->m_single_byte_locale ?
2090Sstevel@tonic-gate 		    STREAM_SINGLE : STREAM_WIDE));
2100Sstevel@tonic-gate 		return (1);
2110Sstevel@tonic-gate 	}
2120Sstevel@tonic-gate 
2130Sstevel@tonic-gate 	ASSERT(str->s_filename);
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate #ifndef DEBUG_DISALLOW_MMAP
2160Sstevel@tonic-gate 	if (S->m_single_byte_locale &&
2170Sstevel@tonic-gate 	    str->s_filesize > 0 &&
2180Sstevel@tonic-gate 	    str->s_filesize < SSIZE_MAX) {
2190Sstevel@tonic-gate 		/*
2200Sstevel@tonic-gate 		 * make mmap() attempt; set s_status and return if successful
2210Sstevel@tonic-gate 		 */
2220Sstevel@tonic-gate 		fd = open(str->s_filename, O_RDONLY);
2230Sstevel@tonic-gate 		if (fd < 0) {
2240Sstevel@tonic-gate 			if (errno == EMFILE || errno == ENFILE)
2250Sstevel@tonic-gate 				return (-1);
2260Sstevel@tonic-gate 			else
2270Sstevel@tonic-gate 				die(EMSG_OPEN, str->s_filename);
2280Sstevel@tonic-gate 		}
2290Sstevel@tonic-gate 		str->s_buffer = mmap(0, str->s_filesize, PROT_READ,
2300Sstevel@tonic-gate 		    MAP_SHARED, fd, 0);
2310Sstevel@tonic-gate 
2320Sstevel@tonic-gate 		if (str->s_buffer != MAP_FAILED) {
2330Sstevel@tonic-gate 			str->s_buffer_size = str->s_filesize;
2340Sstevel@tonic-gate 			str->s_type.SF.s_fd = fd;
2350Sstevel@tonic-gate 
2360Sstevel@tonic-gate 			stream_set(str, STREAM_MMAP | STREAM_OPEN);
2370Sstevel@tonic-gate 			stream_unset(str, STREAM_PRIMED);
2380Sstevel@tonic-gate 			return (1);
2390Sstevel@tonic-gate 		}
2400Sstevel@tonic-gate 
2410Sstevel@tonic-gate 		/*
2420Sstevel@tonic-gate 		 * Otherwise the mmap() failed due to address space exhaustion;
2430Sstevel@tonic-gate 		 * since we have already opened the file, we close it and drop
2440Sstevel@tonic-gate 		 * into the normal (STDIO) case.
2450Sstevel@tonic-gate 		 */
2460Sstevel@tonic-gate 		(void) close(fd);
2470Sstevel@tonic-gate 		str->s_buffer = NULL;
2480Sstevel@tonic-gate 	}
2490Sstevel@tonic-gate #endif /* DEBUG_DISALLOW_MMAP */
2500Sstevel@tonic-gate 
2510Sstevel@tonic-gate 	if ((str->s_type.BF.s_fp = fopen(str->s_filename, "r")) == NULL) {
2520Sstevel@tonic-gate 		if (errno == EMFILE || errno == ENFILE)
2530Sstevel@tonic-gate 			return (-1);
2540Sstevel@tonic-gate 		else
2550Sstevel@tonic-gate 			die(EMSG_OPEN, str->s_filename);
2560Sstevel@tonic-gate 	}
2570Sstevel@tonic-gate 
2580Sstevel@tonic-gate 	str->s_type.BF.s_vbuf = safe_realloc(NULL, STDIO_VBUF_SIZE);
2590Sstevel@tonic-gate 	if (setvbuf(str->s_type.BF.s_fp, str->s_type.BF.s_vbuf, _IOFBF,
2600Sstevel@tonic-gate 	    STDIO_VBUF_SIZE) != 0) {
2610Sstevel@tonic-gate 		safe_free(str->s_type.BF.s_vbuf);
2620Sstevel@tonic-gate 		str->s_type.BF.s_vbuf = NULL;
2630Sstevel@tonic-gate 	}
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 	stream_set(str, STREAM_OPEN | (S->m_single_byte_locale ? STREAM_SINGLE :
2660Sstevel@tonic-gate 	    STREAM_WIDE));
2670Sstevel@tonic-gate 	stream_unset(str, STREAM_PRIMED);
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate 	return (1);
2700Sstevel@tonic-gate }
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate void
stream_set_size(stream_t * str,size_t new_size)2730Sstevel@tonic-gate stream_set_size(stream_t *str, size_t new_size)
2740Sstevel@tonic-gate {
2750Sstevel@tonic-gate 	/*
2760Sstevel@tonic-gate 	 * p_new_size is new_size rounded upwards to nearest multiple of
2770Sstevel@tonic-gate 	 * PAGESIZE, since mmap() is going to reserve it in any case.  This
2780Sstevel@tonic-gate 	 * ensures that the far end of the buffer is also aligned, such that we
2790Sstevel@tonic-gate 	 * obtain aligned pointers if we choose to subtract from it.
2800Sstevel@tonic-gate 	 */
2810Sstevel@tonic-gate 	size_t p_new_size = (new_size + PAGESIZE) & ~(PAGESIZE - 1);
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	if (str->s_buffer_size == p_new_size)
2840Sstevel@tonic-gate 		return;
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate 	if (str->s_buffer != NULL)
2870Sstevel@tonic-gate 		(void) munmap(str->s_buffer, str->s_buffer_size);
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	if (new_size == 0) {
2900Sstevel@tonic-gate 		str->s_buffer = NULL;
2910Sstevel@tonic-gate 		str->s_buffer_size = 0;
2920Sstevel@tonic-gate 		return;
2930Sstevel@tonic-gate 	}
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate 	str->s_buffer = xzmap(0, p_new_size, PROT_READ | PROT_WRITE,
2960Sstevel@tonic-gate 	    MAP_PRIVATE, 0);
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate 	if (str->s_buffer == MAP_FAILED)
2990Sstevel@tonic-gate 		die(EMSG_MMAP);
3000Sstevel@tonic-gate 
3010Sstevel@tonic-gate 	str->s_buffer_size = p_new_size;
3020Sstevel@tonic-gate }
3030Sstevel@tonic-gate 
3040Sstevel@tonic-gate void
stream_add_file_to_chain(stream_t ** str_chain,char * filename)3050Sstevel@tonic-gate stream_add_file_to_chain(stream_t **str_chain, char *filename)
3060Sstevel@tonic-gate {
3070Sstevel@tonic-gate 	stream_t *str;
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 	str = stream_new(STREAM_NO_SOURCE);
3100Sstevel@tonic-gate 
3110Sstevel@tonic-gate 	str->s_filename = filename;
3120Sstevel@tonic-gate 	str->s_type.SF.s_fd = -1;
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate 	stream_push_to_chain(str_chain, str);
3150Sstevel@tonic-gate }
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate void
stream_push_to_chain(stream_t ** str_chain,stream_t * streamp)3180Sstevel@tonic-gate stream_push_to_chain(stream_t **str_chain, stream_t *streamp)
3190Sstevel@tonic-gate {
3200Sstevel@tonic-gate 	stream_t *cur_streamp = *str_chain;
3210Sstevel@tonic-gate 
3220Sstevel@tonic-gate 	if (cur_streamp == NULL) {
3230Sstevel@tonic-gate 		*str_chain = streamp;
3240Sstevel@tonic-gate 		streamp->s_next = NULL;
3250Sstevel@tonic-gate 		return;
3260Sstevel@tonic-gate 	}
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate 	while (cur_streamp->s_next != NULL)
3290Sstevel@tonic-gate 		cur_streamp = cur_streamp->s_next;
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate 	cur_streamp->s_next = streamp;
3320Sstevel@tonic-gate 	streamp->s_previous = cur_streamp;
3330Sstevel@tonic-gate 	streamp->s_next = NULL;
3340Sstevel@tonic-gate }
3350Sstevel@tonic-gate 
3360Sstevel@tonic-gate static void
stream_dump(stream_t * str_in,stream_t * str_out)3370Sstevel@tonic-gate stream_dump(stream_t *str_in, stream_t *str_out)
3380Sstevel@tonic-gate {
3390Sstevel@tonic-gate 	ASSERT(!(str_in->s_status & STREAM_OUTPUT));
3400Sstevel@tonic-gate 	ASSERT(str_out->s_status & STREAM_OUTPUT);
3410Sstevel@tonic-gate 
342*5836Snakanon 	SOP_PUT_LINE(str_out, &str_in->s_current);
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 	while (!SOP_EOS(str_in)) {
3450Sstevel@tonic-gate 		SOP_FETCH(str_in);
346*5836Snakanon 		SOP_PUT_LINE(str_out, &str_in->s_current);
3470Sstevel@tonic-gate 	}
3480Sstevel@tonic-gate }
3490Sstevel@tonic-gate 
3500Sstevel@tonic-gate /*
3510Sstevel@tonic-gate  * stream_push_to_temporary() with flags set to ST_CACHE merely copies the
3520Sstevel@tonic-gate  * stream_t pointer onto the chain.  With flags set to ST_NOCACHE, the stream is
3530Sstevel@tonic-gate  * written out to a file.  Stream pointers passed to stream_push_to_temporary()
3540Sstevel@tonic-gate  * must refer to allocated objects, and not to objects created on function
3550Sstevel@tonic-gate  * stacks.  Finally, if strp == NULL, stream_push_to_temporary() creates and
3560Sstevel@tonic-gate  * pushes the new stream; the output stream is left open if ST_OPEN is set.
3570Sstevel@tonic-gate  */
3580Sstevel@tonic-gate stream_t *
stream_push_to_temporary(stream_t ** str_chain,stream_t * streamp,int flags)3590Sstevel@tonic-gate stream_push_to_temporary(stream_t **str_chain, stream_t *streamp, int flags)
3600Sstevel@tonic-gate {
3610Sstevel@tonic-gate 	stream_t *out_streamp;
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate 	if (flags & ST_CACHE) {
3640Sstevel@tonic-gate 		ASSERT(streamp->s_status & STREAM_ARRAY);
3650Sstevel@tonic-gate 		stream_set(streamp, STREAM_NOT_FREEABLE | STREAM_TEMPORARY);
3660Sstevel@tonic-gate 		stream_push_to_chain(str_chain, streamp);
3670Sstevel@tonic-gate 		return (streamp);
3680Sstevel@tonic-gate 	}
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate 	out_streamp = safe_realloc(NULL, sizeof (stream_t));
3710Sstevel@tonic-gate 
3720Sstevel@tonic-gate 	if (streamp != NULL) {
3730Sstevel@tonic-gate 		stream_copy(out_streamp, streamp);
3740Sstevel@tonic-gate 		stream_unset(out_streamp, STREAM_OPEN);
3750Sstevel@tonic-gate 		ASSERT(streamp->s_element_size == sizeof (char) ||
3760Sstevel@tonic-gate 		    streamp->s_element_size == sizeof (wchar_t));
3770Sstevel@tonic-gate 		stream_set(out_streamp,
3780Sstevel@tonic-gate 		    streamp->s_element_size == 1 ? STREAM_SINGLE : STREAM_WIDE);
3790Sstevel@tonic-gate 		out_streamp->s_buffer = NULL;
3800Sstevel@tonic-gate 		out_streamp->s_buffer_size = 0;
3810Sstevel@tonic-gate 	} else {
3820Sstevel@tonic-gate 		stream_clear(out_streamp);
3830Sstevel@tonic-gate 		stream_set(out_streamp, flags & ST_WIDE ? STREAM_WIDE :
3840Sstevel@tonic-gate 		    STREAM_SINGLE);
3850Sstevel@tonic-gate 	}
3860Sstevel@tonic-gate 
3870Sstevel@tonic-gate 	(void) bump_file_template();
3880Sstevel@tonic-gate 	out_streamp->s_filename = strdup(get_file_template());
3890Sstevel@tonic-gate 
3900Sstevel@tonic-gate 	if (SOP_OPEN_FOR_WRITE(out_streamp) == -1)
3910Sstevel@tonic-gate 		return (NULL);
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate 	stream_set(out_streamp, STREAM_TEMPORARY);
3940Sstevel@tonic-gate 	stream_push_to_chain(str_chain, out_streamp);
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate 	if (streamp != NULL) {
3970Sstevel@tonic-gate 		/*
3980Sstevel@tonic-gate 		 * We reset the input stream to the beginning, and copy it in
3990Sstevel@tonic-gate 		 * sequence to the output stream, freeing the raw_collate field
4000Sstevel@tonic-gate 		 * as we go.
4010Sstevel@tonic-gate 		 */
4020Sstevel@tonic-gate 		if (SOP_PRIME(streamp) != PRIME_SUCCEEDED)
4030Sstevel@tonic-gate 			die(EMSG_BADPRIME);
4040Sstevel@tonic-gate 		stream_dump(streamp, out_streamp);
4050Sstevel@tonic-gate 	}
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate 	if (!(flags & ST_OPEN)) {
4080Sstevel@tonic-gate 		SOP_FREE(out_streamp);
4090Sstevel@tonic-gate 		(void) SOP_CLOSE(out_streamp);
4100Sstevel@tonic-gate 	}
4110Sstevel@tonic-gate 
4120Sstevel@tonic-gate 	/*
4130Sstevel@tonic-gate 	 * Now that we've written this stream to disk, we needn't protect any
4140Sstevel@tonic-gate 	 * in-memory consumer.
4150Sstevel@tonic-gate 	 */
4160Sstevel@tonic-gate 	if (streamp != NULL)
4170Sstevel@tonic-gate 		streamp->s_consumer = NULL;
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate 	return (out_streamp);
4200Sstevel@tonic-gate }
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate void
stream_close_all_previous(stream_t * tail_streamp)4230Sstevel@tonic-gate stream_close_all_previous(stream_t *tail_streamp)
4240Sstevel@tonic-gate {
4250Sstevel@tonic-gate 	stream_t *cur_streamp;
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate 	ASSERT(tail_streamp != NULL);
4280Sstevel@tonic-gate 
4290Sstevel@tonic-gate 	cur_streamp = tail_streamp->s_previous;
4300Sstevel@tonic-gate 	while (cur_streamp != NULL) {
4310Sstevel@tonic-gate 		(void) SOP_FREE(cur_streamp);
4320Sstevel@tonic-gate 		if (SOP_IS_CLOSABLE(cur_streamp))
4330Sstevel@tonic-gate 			(void) SOP_CLOSE(cur_streamp);
4340Sstevel@tonic-gate 
4350Sstevel@tonic-gate 		cur_streamp = cur_streamp->s_previous;
4360Sstevel@tonic-gate 	}
4370Sstevel@tonic-gate }
4380Sstevel@tonic-gate 
4390Sstevel@tonic-gate void
stream_unlink_temporary(stream_t * streamp)4400Sstevel@tonic-gate stream_unlink_temporary(stream_t *streamp)
4410Sstevel@tonic-gate {
4420Sstevel@tonic-gate 	if (streamp->s_status & STREAM_TEMPORARY) {
4430Sstevel@tonic-gate 		(void) SOP_FREE(streamp);
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate 		if (streamp->s_ops.sop_unlink)
4460Sstevel@tonic-gate 			(void) SOP_UNLINK(streamp);
4470Sstevel@tonic-gate 	}
4480Sstevel@tonic-gate }
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate /*
4510Sstevel@tonic-gate  * stream_insert() takes input from src stream, converts to each line to
4520Sstevel@tonic-gate  * collatable form, and places a line_rec_t in dest stream, which is of type
4530Sstevel@tonic-gate  * STREAM_ARRAY.
4540Sstevel@tonic-gate  */
4550Sstevel@tonic-gate int
stream_insert(sort_t * S,stream_t * src,stream_t * dest)4560Sstevel@tonic-gate stream_insert(sort_t *S, stream_t *src, stream_t *dest)
4570Sstevel@tonic-gate {
4580Sstevel@tonic-gate 	ssize_t i = dest->s_type.LA.s_array_size;
4590Sstevel@tonic-gate 	line_rec_t *l_series;
4600Sstevel@tonic-gate 	char *l_convert = dest->s_buffer;
4610Sstevel@tonic-gate 	int return_val = ST_MEM_AVAIL;
4620Sstevel@tonic-gate 	int fetch_result = NEXT_LINE_COMPLETE;
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate 	/*
4650Sstevel@tonic-gate 	 * Scan through until total bytes allowed accumulated, and return.
4660Sstevel@tonic-gate 	 * Use SOP_FETCH(src) so that this works for all stream types,
4670Sstevel@tonic-gate 	 * and so that we can repeat until eos.
4680Sstevel@tonic-gate 	 *
4690Sstevel@tonic-gate 	 * For each new line, we move back sizeof (line_rec_t) from the end of
4700Sstevel@tonic-gate 	 * the array buffer, and copy into the start of the array buffer.  When
4710Sstevel@tonic-gate 	 * the pointers meet, or when we exhaust the current stream, we return.
4720Sstevel@tonic-gate 	 * If we have not filled the current memory allocation, we return
4730Sstevel@tonic-gate 	 * ST_MEM_AVAIL, else we return ST_MEM_FILLED.
4740Sstevel@tonic-gate 	 */
4750Sstevel@tonic-gate 	ASSERT(stream_is_primed(src));
4760Sstevel@tonic-gate 	ASSERT(dest->s_status & STREAM_ARRAY);
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate 	/*LINTED ALIGNMENT*/
4790Sstevel@tonic-gate 	l_series = (line_rec_t *)((caddr_t)dest->s_buffer
4800Sstevel@tonic-gate 	    + dest->s_buffer_size) - dest->s_type.LA.s_array_size;
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 	if (dest->s_type.LA.s_array_size)
4830Sstevel@tonic-gate 		l_convert = l_series->l_collate.sp +
4840Sstevel@tonic-gate 		    l_series->l_collate_length + src->s_element_size;
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate 	/*
4870Sstevel@tonic-gate 	 * current line has been set prior to entry
4880Sstevel@tonic-gate 	 */
4890Sstevel@tonic-gate 	src->s_current.l_collate.sp = l_convert;
4900Sstevel@tonic-gate 	src->s_current.l_collate_bufsize = (caddr_t)l_series
4910Sstevel@tonic-gate 	    - (caddr_t)l_convert - sizeof (line_rec_t);
4920Sstevel@tonic-gate 	src->s_current.l_raw_collate.sp = NULL;
4930Sstevel@tonic-gate 
4940Sstevel@tonic-gate 	if (src->s_current.l_collate_bufsize <= 0)
4950Sstevel@tonic-gate 		return (ST_MEM_FILLED);
4960Sstevel@tonic-gate 
4970Sstevel@tonic-gate 	src->s_consumer = dest;
4980Sstevel@tonic-gate 
4990Sstevel@tonic-gate 	while (src->s_current.l_collate_bufsize > 0 &&
5000Sstevel@tonic-gate 	    (src->s_current.l_collate_length = S->m_coll_convert(
5010Sstevel@tonic-gate 	    S->m_fields_head, &src->s_current, FCV_FAIL,
5020Sstevel@tonic-gate 	    S->m_field_separator)) >= 0) {
5030Sstevel@tonic-gate 		ASSERT((char *)l_series > l_convert);
5040Sstevel@tonic-gate 		l_series--;
5050Sstevel@tonic-gate 		l_convert += src->s_current.l_collate_length;
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate 		if ((char *)l_series <= l_convert) {
5080Sstevel@tonic-gate 			__S(stats_incr_insert_filled_downward());
5090Sstevel@tonic-gate 			l_series++;
5100Sstevel@tonic-gate 			return_val = ST_MEM_FILLED;
5110Sstevel@tonic-gate 			break;
5120Sstevel@tonic-gate 		}
5130Sstevel@tonic-gate 
5140Sstevel@tonic-gate 		/*
5150Sstevel@tonic-gate 		 * There's no collision with the lower part of the buffer, so we
5160Sstevel@tonic-gate 		 * can safely begin processing the line.  In the debug case, we
5170Sstevel@tonic-gate 		 * test for uninitialized data by copying a non-zero pattern.
5180Sstevel@tonic-gate 		 */
5190Sstevel@tonic-gate #ifdef DEBUG
5200Sstevel@tonic-gate 		memset(l_series, 0x1ff11ff1, sizeof (line_rec_t));
5210Sstevel@tonic-gate #endif
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate 		copy_line_rec(&src->s_current, l_series);
5240Sstevel@tonic-gate 		i++;
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate 		if (SOP_EOS(src) ||
5270Sstevel@tonic-gate 		    (fetch_result = SOP_FETCH(src)) == NEXT_LINE_INCOMPLETE)
5280Sstevel@tonic-gate 			break;
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate 		src->s_current.l_collate.sp = l_convert;
5310Sstevel@tonic-gate 		src->s_current.l_collate_bufsize = (caddr_t)l_series
5320Sstevel@tonic-gate 		    - (caddr_t)l_convert - sizeof (line_rec_t);
5330Sstevel@tonic-gate 		src->s_current.l_raw_collate.sp = NULL;
5340Sstevel@tonic-gate 	}
5350Sstevel@tonic-gate 
5360Sstevel@tonic-gate 	if (fetch_result == NEXT_LINE_INCOMPLETE) {
5370Sstevel@tonic-gate 		__S(stats_incr_insert_filled_input());
5380Sstevel@tonic-gate 		return_val = ST_MEM_FILLED;
5390Sstevel@tonic-gate 	} else if (src->s_current.l_collate_length < 0 ||
5400Sstevel@tonic-gate 	    src->s_current.l_collate_bufsize <= 0) {
5410Sstevel@tonic-gate 		__S(stats_incr_insert_filled_upward());
5420Sstevel@tonic-gate 		return_val = ST_MEM_FILLED;
5430Sstevel@tonic-gate 	}
5440Sstevel@tonic-gate 
5450Sstevel@tonic-gate 	if (fetch_result != NEXT_LINE_INCOMPLETE &&
5460Sstevel@tonic-gate 	    src->s_current.l_collate_length < 0 &&
5470Sstevel@tonic-gate 	    i == 0)
5480Sstevel@tonic-gate 		/*
5490Sstevel@tonic-gate 		 * There's no room for conversion of our only line; need to
5500Sstevel@tonic-gate 		 * execute with larger memory.
5510Sstevel@tonic-gate 		 */
5520Sstevel@tonic-gate 		die(EMSG_MEMORY);
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 	/*
5550Sstevel@tonic-gate 	 * Set up pointer array to line records.
5560Sstevel@tonic-gate 	 */
5570Sstevel@tonic-gate 	if (i > dest->s_type.LA.s_array_size)
5580Sstevel@tonic-gate 		dest->s_type.LA.s_array = safe_realloc(dest->s_type.LA.s_array,
5590Sstevel@tonic-gate 		    sizeof (line_rec_t *) * i);
5600Sstevel@tonic-gate 	dest->s_type.LA.s_array_size = i;
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 	i = 0;
5630Sstevel@tonic-gate 	while (i < dest->s_type.LA.s_array_size) {
5640Sstevel@tonic-gate 		dest->s_type.LA.s_array[i] = l_series;
5650Sstevel@tonic-gate 		l_series++;
5660Sstevel@tonic-gate 		i++;
5670Sstevel@tonic-gate 	}
5680Sstevel@tonic-gate 
5690Sstevel@tonic-gate 	/*
5700Sstevel@tonic-gate 	 * LINES_ARRAY streams are always open.
5710Sstevel@tonic-gate 	 */
5720Sstevel@tonic-gate 	stream_set(dest, STREAM_OPEN);
5730Sstevel@tonic-gate 
5740Sstevel@tonic-gate 	return (return_val);
5750Sstevel@tonic-gate }
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate /*
5780Sstevel@tonic-gate  * stream_swap_buffer() exchanges the stream's buffer with the proffered one;
5790Sstevel@tonic-gate  * s_current is not adjusted so this is safe only for STREAM_INSTANT.
5800Sstevel@tonic-gate  */
5810Sstevel@tonic-gate void
stream_swap_buffer(stream_t * str,char ** buf,size_t * size)5820Sstevel@tonic-gate stream_swap_buffer(stream_t *str, char **buf, size_t *size)
5830Sstevel@tonic-gate {
5840Sstevel@tonic-gate 	void *tb = *buf;
5850Sstevel@tonic-gate 	size_t ts = *size;
5860Sstevel@tonic-gate 
5870Sstevel@tonic-gate 	*buf = str->s_buffer;
5880Sstevel@tonic-gate 	*size = str->s_buffer_size;
5890Sstevel@tonic-gate 
5900Sstevel@tonic-gate 	str->s_buffer = tb;
5910Sstevel@tonic-gate 	str->s_buffer_size = ts;
5920Sstevel@tonic-gate }
593