xref: /onnv-gate/usr/src/cmd/sort/common/utility.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include "utility.h"
30*0Sstevel@tonic-gate 
31*0Sstevel@tonic-gate #include "initialize.h"
32*0Sstevel@tonic-gate #include "statistics.h"
33*0Sstevel@tonic-gate #include "streams_common.h"
34*0Sstevel@tonic-gate #include "streams.h"
35*0Sstevel@tonic-gate 
36*0Sstevel@tonic-gate /*
37*0Sstevel@tonic-gate  * utility
38*0Sstevel@tonic-gate  *
39*0Sstevel@tonic-gate  * Overview
40*0Sstevel@tonic-gate  *   utility.c contains the general purpose routines used in various locations
41*0Sstevel@tonic-gate  *   throughout sort.  It provides a number of interfaces that maintain local
42*0Sstevel@tonic-gate  *   state relevant to this instance of sort.  We discuss the more significant
43*0Sstevel@tonic-gate  *   of these interfaces below.
44*0Sstevel@tonic-gate  *
45*0Sstevel@tonic-gate  * Output guard
46*0Sstevel@tonic-gate  *   sort is one of the few Unix utilities that is capable of working "in
47*0Sstevel@tonic-gate  *   place"; that is, sort can manipulate an input file and place its output in
48*0Sstevel@tonic-gate  *   a file of the same name safely.  This is handled in this implementation by
49*0Sstevel@tonic-gate  *   the output guard facility.  In the case of an interrupt or other fatal
50*0Sstevel@tonic-gate  *   signal, sort essays to restore the original input file.
51*0Sstevel@tonic-gate  *
52*0Sstevel@tonic-gate  * Temporary file cleanup
53*0Sstevel@tonic-gate  *   Similar to the output guard facility, sort cleans up its temporary files in
54*0Sstevel@tonic-gate  *   the case of interruption (or normal exit, for that matter); this is handled
55*0Sstevel@tonic-gate  *   by registering a list of file pointers for later use by the atexit handler.
56*0Sstevel@tonic-gate  *
57*0Sstevel@tonic-gate  * Temporary filename security
58*0Sstevel@tonic-gate  *   sort protects against "open-through-link" security attacks by verifying
59*0Sstevel@tonic-gate  *   that the selected temporary file name is unused.  If the file name is in
60*0Sstevel@tonic-gate  *   use, the pattern is readjusted until an available name pattern is
61*0Sstevel@tonic-gate  *   discovered.
62*0Sstevel@tonic-gate  *
63*0Sstevel@tonic-gate  * Buffered I/O
64*0Sstevel@tonic-gate  *   sort has a simple buffered I/O facility of its own, to facilitate writing
65*0Sstevel@tonic-gate  *   data in large quantities (particularly for multibyte locales).  cxwrite()
66*0Sstevel@tonic-gate  *   is the base routine, while wxwrite(), which handles multibyte buffers, is
67*0Sstevel@tonic-gate  *   built on top of cxwrite().
68*0Sstevel@tonic-gate  */
69*0Sstevel@tonic-gate 
70*0Sstevel@tonic-gate #define	XBUFFER_SIZE	(32 * KILOBYTE)
71*0Sstevel@tonic-gate 
72*0Sstevel@tonic-gate #define	EXIT_OK		0
73*0Sstevel@tonic-gate #define	EXIT_FAILURE	1
74*0Sstevel@tonic-gate #define	EXIT_ERROR	2
75*0Sstevel@tonic-gate #define	EXIT_INTERNAL	3
76*0Sstevel@tonic-gate 
77*0Sstevel@tonic-gate static int held_fd = -1;
78*0Sstevel@tonic-gate 
79*0Sstevel@tonic-gate static stream_t	**cleanup_chain = NULL;
80*0Sstevel@tonic-gate 
81*0Sstevel@tonic-gate static char *output_guard_tempname = NULL;
82*0Sstevel@tonic-gate static ssize_t output_guard_size = 0;
83*0Sstevel@tonic-gate static char *output_guard_filename = NULL;
84*0Sstevel@tonic-gate static int output_guard_copy_complete = 0;
85*0Sstevel@tonic-gate 
86*0Sstevel@tonic-gate static const char *default_tmpdir = "/var/tmp";
87*0Sstevel@tonic-gate static const char *default_template = "/stmAAAXXXXXX";
88*0Sstevel@tonic-gate static const char *default_template_count = ".00000000";
89*0Sstevel@tonic-gate static char *current_tmpdir;
90*0Sstevel@tonic-gate static char *current_template;
91*0Sstevel@tonic-gate 
92*0Sstevel@tonic-gate static const char PNAME_FMT[] = "%s: ";
93*0Sstevel@tonic-gate static const char ERRNO_FMT[] = ": %s\n";
94*0Sstevel@tonic-gate static const char *pname = "sort";
95*0Sstevel@tonic-gate 
96*0Sstevel@tonic-gate void
swap(void ** a,void ** b)97*0Sstevel@tonic-gate swap(void **a, void **b)
98*0Sstevel@tonic-gate {
99*0Sstevel@tonic-gate 	void *t;
100*0Sstevel@tonic-gate 
101*0Sstevel@tonic-gate 	t = *a;
102*0Sstevel@tonic-gate 	*a = *b;
103*0Sstevel@tonic-gate 	*b = t;
104*0Sstevel@tonic-gate 
105*0Sstevel@tonic-gate 	__S(stats_incr_swaps());
106*0Sstevel@tonic-gate }
107*0Sstevel@tonic-gate 
108*0Sstevel@tonic-gate /*
109*0Sstevel@tonic-gate  * Temporary file name template handling.
110*0Sstevel@tonic-gate  */
111*0Sstevel@tonic-gate static void
reset_file_template()112*0Sstevel@tonic-gate reset_file_template()
113*0Sstevel@tonic-gate {
114*0Sstevel@tonic-gate 	struct stat s;
115*0Sstevel@tonic-gate 
116*0Sstevel@tonic-gate 	do {
117*0Sstevel@tonic-gate 		(void) strcpy(current_template, current_tmpdir);
118*0Sstevel@tonic-gate 		(void) strcat(current_template, default_template);
119*0Sstevel@tonic-gate 		(void) mktemp(current_template);
120*0Sstevel@tonic-gate 		(void) strcat(current_template, default_template_count);
121*0Sstevel@tonic-gate 	} while (lstat(current_template, &s) != -1);
122*0Sstevel@tonic-gate }
123*0Sstevel@tonic-gate 
124*0Sstevel@tonic-gate int
bump_file_template()125*0Sstevel@tonic-gate bump_file_template()
126*0Sstevel@tonic-gate {
127*0Sstevel@tonic-gate 	struct stat s;
128*0Sstevel@tonic-gate 	int n = strlen(current_template);
129*0Sstevel@tonic-gate 	int i;
130*0Sstevel@tonic-gate 
131*0Sstevel@tonic-gate 	for (i = n - 1; isdigit((uchar_t)current_template[i]); i--) {
132*0Sstevel@tonic-gate 		current_template[i]++;
133*0Sstevel@tonic-gate 		if (current_template[i] > '9')
134*0Sstevel@tonic-gate 			current_template[i] = '0';
135*0Sstevel@tonic-gate 		else
136*0Sstevel@tonic-gate 			break;
137*0Sstevel@tonic-gate 	}
138*0Sstevel@tonic-gate 
139*0Sstevel@tonic-gate 	if (!isdigit((uchar_t)current_template[i])) {
140*0Sstevel@tonic-gate 		/*
141*0Sstevel@tonic-gate 		 * Template has been exhausted, so reset.
142*0Sstevel@tonic-gate 		 */
143*0Sstevel@tonic-gate 		reset_file_template();
144*0Sstevel@tonic-gate 	}
145*0Sstevel@tonic-gate 
146*0Sstevel@tonic-gate 	if (lstat(current_template, &s) == 0) {
147*0Sstevel@tonic-gate 		/*
148*0Sstevel@tonic-gate 		 * Our newly bumped template has been anticipated; reset to
149*0Sstevel@tonic-gate 		 * avoid possible "link-through" attack.
150*0Sstevel@tonic-gate 		 */
151*0Sstevel@tonic-gate 		reset_file_template();
152*0Sstevel@tonic-gate 	}
153*0Sstevel@tonic-gate 
154*0Sstevel@tonic-gate 	return (0);
155*0Sstevel@tonic-gate }
156*0Sstevel@tonic-gate 
157*0Sstevel@tonic-gate void
set_file_template(char ** T)158*0Sstevel@tonic-gate set_file_template(char **T)
159*0Sstevel@tonic-gate {
160*0Sstevel@tonic-gate 	struct stat s;
161*0Sstevel@tonic-gate 	int check_tmpdir = 0;
162*0Sstevel@tonic-gate 
163*0Sstevel@tonic-gate 	if (*T != NULL) {
164*0Sstevel@tonic-gate 		current_tmpdir = strdup(*T);
165*0Sstevel@tonic-gate 		check_tmpdir = 1;
166*0Sstevel@tonic-gate 	} else if ((current_tmpdir = getenv("TMPDIR")) != NULL) {
167*0Sstevel@tonic-gate 		check_tmpdir = 1;
168*0Sstevel@tonic-gate 	} else {
169*0Sstevel@tonic-gate 		current_tmpdir = (char *)default_tmpdir;
170*0Sstevel@tonic-gate 	}
171*0Sstevel@tonic-gate 
172*0Sstevel@tonic-gate 	/*
173*0Sstevel@tonic-gate 	 * Check that the temporary directory given exists, and is a directory.
174*0Sstevel@tonic-gate 	 */
175*0Sstevel@tonic-gate 	if (check_tmpdir) {
176*0Sstevel@tonic-gate 		if (stat(current_tmpdir, &s) != 0) {
177*0Sstevel@tonic-gate 			warn(gettext("cannot stat temporary directory %s"),
178*0Sstevel@tonic-gate 			    current_tmpdir);
179*0Sstevel@tonic-gate 
180*0Sstevel@tonic-gate 			current_tmpdir = (char *)default_tmpdir;
181*0Sstevel@tonic-gate 		} else if (!S_ISDIR(s.st_mode)) {
182*0Sstevel@tonic-gate 			warn(gettext("%s is not a directory; "
183*0Sstevel@tonic-gate 			    "using default temporary directory"),
184*0Sstevel@tonic-gate 			    current_tmpdir);
185*0Sstevel@tonic-gate 
186*0Sstevel@tonic-gate 			current_tmpdir = (char *)default_tmpdir;
187*0Sstevel@tonic-gate 		}
188*0Sstevel@tonic-gate 	}
189*0Sstevel@tonic-gate 
190*0Sstevel@tonic-gate 	ASSERT(current_tmpdir != NULL);
191*0Sstevel@tonic-gate 
192*0Sstevel@tonic-gate 	current_template = safe_realloc(NULL, strlen(current_tmpdir)
193*0Sstevel@tonic-gate 	    + strlen(default_template) + strlen(default_template_count) + 1);
194*0Sstevel@tonic-gate 
195*0Sstevel@tonic-gate 	reset_file_template();
196*0Sstevel@tonic-gate }
197*0Sstevel@tonic-gate 
198*0Sstevel@tonic-gate char *
get_file_template()199*0Sstevel@tonic-gate get_file_template()
200*0Sstevel@tonic-gate {
201*0Sstevel@tonic-gate 	return (current_template);
202*0Sstevel@tonic-gate }
203*0Sstevel@tonic-gate 
204*0Sstevel@tonic-gate /*
205*0Sstevel@tonic-gate  * Output guard routines.
206*0Sstevel@tonic-gate  */
207*0Sstevel@tonic-gate void
establish_output_guard(sort_t * S)208*0Sstevel@tonic-gate establish_output_guard(sort_t *S)
209*0Sstevel@tonic-gate {
210*0Sstevel@tonic-gate 	struct stat output_stat;
211*0Sstevel@tonic-gate 
212*0Sstevel@tonic-gate 	if (S->m_output_to_stdout)
213*0Sstevel@tonic-gate 		return;
214*0Sstevel@tonic-gate 
215*0Sstevel@tonic-gate 	if (stat(S->m_output_filename, &output_stat) == 0) {
216*0Sstevel@tonic-gate 		stream_t *strp = S->m_input_streams;
217*0Sstevel@tonic-gate 
218*0Sstevel@tonic-gate 		while (strp != NULL) {
219*0Sstevel@tonic-gate 			/*
220*0Sstevel@tonic-gate 			 * We needn't protect an empty file.
221*0Sstevel@tonic-gate 			 */
222*0Sstevel@tonic-gate 			if (!(strp->s_status & STREAM_NOTFILE) &&
223*0Sstevel@tonic-gate 			    strp->s_dev == output_stat.st_dev &&
224*0Sstevel@tonic-gate 			    strp->s_ino == output_stat.st_ino &&
225*0Sstevel@tonic-gate 			    strp->s_filesize > 0) {
226*0Sstevel@tonic-gate 				output_guard_filename = S->m_output_filename;
227*0Sstevel@tonic-gate 				output_guard_size = strp->s_filesize;
228*0Sstevel@tonic-gate 
229*0Sstevel@tonic-gate 				ASSERT(output_guard_filename != NULL);
230*0Sstevel@tonic-gate 
231*0Sstevel@tonic-gate 				if (bump_file_template() < 0)
232*0Sstevel@tonic-gate 					die(EMSG_TEMPORARY);
233*0Sstevel@tonic-gate 
234*0Sstevel@tonic-gate 				if ((strp->s_filename = output_guard_tempname =
235*0Sstevel@tonic-gate 				    strdup(get_file_template())) == NULL)
236*0Sstevel@tonic-gate 					die(EMSG_ALLOC);
237*0Sstevel@tonic-gate 
238*0Sstevel@tonic-gate 				xcp(output_guard_tempname,
239*0Sstevel@tonic-gate 				    output_guard_filename, output_guard_size);
240*0Sstevel@tonic-gate 
241*0Sstevel@tonic-gate 				output_guard_copy_complete = 1;
242*0Sstevel@tonic-gate 
243*0Sstevel@tonic-gate 				return;
244*0Sstevel@tonic-gate 			}
245*0Sstevel@tonic-gate 			strp = strp->s_next;
246*0Sstevel@tonic-gate 		}
247*0Sstevel@tonic-gate 	}
248*0Sstevel@tonic-gate }
249*0Sstevel@tonic-gate 
250*0Sstevel@tonic-gate void
remove_output_guard()251*0Sstevel@tonic-gate remove_output_guard()
252*0Sstevel@tonic-gate {
253*0Sstevel@tonic-gate 	if (output_guard_tempname && unlink(output_guard_tempname) == -1)
254*0Sstevel@tonic-gate 		warn(gettext("unable to unlink %s"), output_guard_tempname);
255*0Sstevel@tonic-gate 
256*0Sstevel@tonic-gate 	output_guard_tempname = NULL;
257*0Sstevel@tonic-gate }
258*0Sstevel@tonic-gate 
259*0Sstevel@tonic-gate void
set_cleanup_chain(stream_t ** strp)260*0Sstevel@tonic-gate set_cleanup_chain(stream_t **strp)
261*0Sstevel@tonic-gate {
262*0Sstevel@tonic-gate 	ASSERT(strp != NULL);
263*0Sstevel@tonic-gate 
264*0Sstevel@tonic-gate 	cleanup_chain = strp;
265*0Sstevel@tonic-gate }
266*0Sstevel@tonic-gate 
267*0Sstevel@tonic-gate /*
268*0Sstevel@tonic-gate  * atexit_handler() cleans up any temporary files outstanding after a fatal
269*0Sstevel@tonic-gate  * signal, a call to die() or at exit().  To preserve the input file under low
270*0Sstevel@tonic-gate  * storage conditions (and both the output file and the temporary files are
271*0Sstevel@tonic-gate  * directed at the same filesystem), we remove all temporary files but the
272*0Sstevel@tonic-gate  * output guard first, and then restore the original file.  Of course, this is
273*0Sstevel@tonic-gate  * not foolproof, as another writer may have exhausted storage.
274*0Sstevel@tonic-gate  */
275*0Sstevel@tonic-gate void
atexit_handler()276*0Sstevel@tonic-gate atexit_handler()
277*0Sstevel@tonic-gate {
278*0Sstevel@tonic-gate 	stream_t *strp;
279*0Sstevel@tonic-gate 
280*0Sstevel@tonic-gate 	if (cleanup_chain && *cleanup_chain)
281*0Sstevel@tonic-gate 		for (strp = *cleanup_chain; strp != NULL; strp = strp->s_next)
282*0Sstevel@tonic-gate 			stream_unlink_temporary(strp);
283*0Sstevel@tonic-gate 
284*0Sstevel@tonic-gate 	if (output_guard_tempname) {
285*0Sstevel@tonic-gate 		if (output_guard_copy_complete)
286*0Sstevel@tonic-gate 			xcp(output_guard_filename, output_guard_tempname,
287*0Sstevel@tonic-gate 			    output_guard_size);
288*0Sstevel@tonic-gate 
289*0Sstevel@tonic-gate 		remove_output_guard();
290*0Sstevel@tonic-gate 	}
291*0Sstevel@tonic-gate 
292*0Sstevel@tonic-gate 	__S(stats_display());
293*0Sstevel@tonic-gate }
294*0Sstevel@tonic-gate 
295*0Sstevel@tonic-gate size_t
strtomem(char * S)296*0Sstevel@tonic-gate strtomem(char *S)
297*0Sstevel@tonic-gate {
298*0Sstevel@tonic-gate 	const char *format_str = "%lf%c";
299*0Sstevel@tonic-gate 	double val = 0.0;
300*0Sstevel@tonic-gate 	size_t retval;
301*0Sstevel@tonic-gate 	char units = 'k';
302*0Sstevel@tonic-gate 	size_t phys_total = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE);
303*0Sstevel@tonic-gate 
304*0Sstevel@tonic-gate 	if (sscanf(S, format_str, &val, &units) < 1 || val < 0)
305*0Sstevel@tonic-gate 		return (0);
306*0Sstevel@tonic-gate 
307*0Sstevel@tonic-gate 	if (units == '%') {
308*0Sstevel@tonic-gate 		if (val < 0 || val > 100)
309*0Sstevel@tonic-gate 			return (0);
310*0Sstevel@tonic-gate 		val *= phys_total / 100;
311*0Sstevel@tonic-gate 	} else
312*0Sstevel@tonic-gate 		switch (units) {
313*0Sstevel@tonic-gate 			case 't' : /* terabytes */
314*0Sstevel@tonic-gate 			case 'T' :
315*0Sstevel@tonic-gate 				val *= 1024;
316*0Sstevel@tonic-gate 				/*FALLTHROUGH*/
317*0Sstevel@tonic-gate 			case 'g' : /* gigabytes */
318*0Sstevel@tonic-gate 			case 'G' :
319*0Sstevel@tonic-gate 				val *= 1024;
320*0Sstevel@tonic-gate 				/*FALLTHROUGH*/
321*0Sstevel@tonic-gate 			case 'm' : /* megabytes */
322*0Sstevel@tonic-gate 			case 'M' :
323*0Sstevel@tonic-gate 				val *= 1024;
324*0Sstevel@tonic-gate 				/*FALLTHROUGH*/
325*0Sstevel@tonic-gate 			case 'k' : /* kilobytes */
326*0Sstevel@tonic-gate 			case 'K' :
327*0Sstevel@tonic-gate 				val *= 1024;
328*0Sstevel@tonic-gate 				/*FALLTHROUGH*/
329*0Sstevel@tonic-gate 			case 'b' : /* bytes */
330*0Sstevel@tonic-gate 			case 'B' :
331*0Sstevel@tonic-gate 				break;
332*0Sstevel@tonic-gate 			default :
333*0Sstevel@tonic-gate 				/*
334*0Sstevel@tonic-gate 				 * default is kilobytes
335*0Sstevel@tonic-gate 				 */
336*0Sstevel@tonic-gate 				val *= 1024;
337*0Sstevel@tonic-gate 				break;
338*0Sstevel@tonic-gate 		}
339*0Sstevel@tonic-gate 
340*0Sstevel@tonic-gate 	if (val > SIZE_MAX)
341*0Sstevel@tonic-gate 		return (0);
342*0Sstevel@tonic-gate 
343*0Sstevel@tonic-gate 	retval = (size_t)val;
344*0Sstevel@tonic-gate 
345*0Sstevel@tonic-gate 	return (retval);
346*0Sstevel@tonic-gate }
347*0Sstevel@tonic-gate 
348*0Sstevel@tonic-gate size_t
available_memory(size_t mem_limit)349*0Sstevel@tonic-gate available_memory(size_t mem_limit)
350*0Sstevel@tonic-gate {
351*0Sstevel@tonic-gate 	size_t phys_avail = sysconf(_SC_AVPHYS_PAGES) * sysconf(_SC_PAGESIZE);
352*0Sstevel@tonic-gate 	size_t avail;
353*0Sstevel@tonic-gate 
354*0Sstevel@tonic-gate 	if (mem_limit != 0) {
355*0Sstevel@tonic-gate #ifdef DEBUG
356*0Sstevel@tonic-gate 		/*
357*0Sstevel@tonic-gate 		 * In the debug case, we want to test the temporary files
358*0Sstevel@tonic-gate 		 * handling, so no lower bound on the memory limit is imposed.
359*0Sstevel@tonic-gate 		 */
360*0Sstevel@tonic-gate 		avail = mem_limit;
361*0Sstevel@tonic-gate #else
362*0Sstevel@tonic-gate 		avail = MAX(64 * KILOBYTE, mem_limit);
363*0Sstevel@tonic-gate #endif /* DEBUG */
364*0Sstevel@tonic-gate 	} else {
365*0Sstevel@tonic-gate 		avail = MAX(64 * KILOBYTE, MIN(AV_MEM_MULTIPLIER * phys_avail /
366*0Sstevel@tonic-gate 		    AV_MEM_DIVISOR, 16 * MEGABYTE));
367*0Sstevel@tonic-gate 	}
368*0Sstevel@tonic-gate 
369*0Sstevel@tonic-gate 	__S(stats_set_available_memory(avail));
370*0Sstevel@tonic-gate 
371*0Sstevel@tonic-gate 	return (avail);
372*0Sstevel@tonic-gate }
373*0Sstevel@tonic-gate 
374*0Sstevel@tonic-gate void
set_memory_ratio(sort_t * S,int * numerator,int * denominator)375*0Sstevel@tonic-gate set_memory_ratio(sort_t *S, int *numerator, int *denominator)
376*0Sstevel@tonic-gate {
377*0Sstevel@tonic-gate 	if (S->m_c_locale) {
378*0Sstevel@tonic-gate 		*numerator = CHAR_AVG_LINE;
379*0Sstevel@tonic-gate 		*denominator = sizeof (line_rec_t) + sizeof (line_rec_t *) +
380*0Sstevel@tonic-gate 		    CHAR_AVG_LINE + CHAR_AVG_LINE;
381*0Sstevel@tonic-gate 		return;
382*0Sstevel@tonic-gate 	}
383*0Sstevel@tonic-gate 
384*0Sstevel@tonic-gate 	if (S->m_single_byte_locale) {
385*0Sstevel@tonic-gate 		*numerator = CHAR_AVG_LINE;
386*0Sstevel@tonic-gate 		*denominator = sizeof (line_rec_t) + sizeof (line_rec_t *) +
387*0Sstevel@tonic-gate 		    CHAR_AVG_LINE + XFRM_MULTIPLIER * CHAR_AVG_LINE;
388*0Sstevel@tonic-gate 		return;
389*0Sstevel@tonic-gate 	}
390*0Sstevel@tonic-gate 
391*0Sstevel@tonic-gate 	*numerator = WCHAR_AVG_LINE;
392*0Sstevel@tonic-gate 	*denominator = sizeof (line_rec_t) + sizeof (line_rec_t *) +
393*0Sstevel@tonic-gate 	    WCHAR_AVG_LINE + WCHAR_AVG_LINE;
394*0Sstevel@tonic-gate }
395*0Sstevel@tonic-gate 
396*0Sstevel@tonic-gate void *
safe_realloc(void * ptr,size_t sz)397*0Sstevel@tonic-gate safe_realloc(void *ptr, size_t sz)
398*0Sstevel@tonic-gate {
399*0Sstevel@tonic-gate 	/*
400*0Sstevel@tonic-gate 	 * safe_realloc() is not meant as an alternative free() mechanism--we
401*0Sstevel@tonic-gate 	 * disallow reallocations to size zero.
402*0Sstevel@tonic-gate 	 */
403*0Sstevel@tonic-gate 	ASSERT(sz != 0);
404*0Sstevel@tonic-gate 
405*0Sstevel@tonic-gate 	if ((ptr = realloc(ptr, sz)) != NULL)
406*0Sstevel@tonic-gate 		return (ptr);
407*0Sstevel@tonic-gate 
408*0Sstevel@tonic-gate 	die(gettext("unable to reallocate buffer"));
409*0Sstevel@tonic-gate 	/*NOTREACHED*/
410*0Sstevel@tonic-gate 	return (NULL);	/* keep gcc happy */
411*0Sstevel@tonic-gate }
412*0Sstevel@tonic-gate 
413*0Sstevel@tonic-gate void
safe_free(void * ptr)414*0Sstevel@tonic-gate safe_free(void *ptr)
415*0Sstevel@tonic-gate {
416*0Sstevel@tonic-gate 	if (ptr)
417*0Sstevel@tonic-gate 		free(ptr);
418*0Sstevel@tonic-gate }
419*0Sstevel@tonic-gate 
420*0Sstevel@tonic-gate void *
xzmap(void * addr,size_t len,int prot,int flags,off_t off)421*0Sstevel@tonic-gate xzmap(void *addr, size_t len, int prot, int flags, off_t off)
422*0Sstevel@tonic-gate {
423*0Sstevel@tonic-gate 	void *pa;
424*0Sstevel@tonic-gate 
425*0Sstevel@tonic-gate 	pa = mmap(addr, len, prot, flags | MAP_ANON, -1, off);
426*0Sstevel@tonic-gate 	if (pa == MAP_FAILED)
427*0Sstevel@tonic-gate 		die(gettext("can't mmap anonymous memory"));
428*0Sstevel@tonic-gate 
429*0Sstevel@tonic-gate 	return (pa);
430*0Sstevel@tonic-gate }
431*0Sstevel@tonic-gate 
432*0Sstevel@tonic-gate void
usage()433*0Sstevel@tonic-gate usage()
434*0Sstevel@tonic-gate {
435*0Sstevel@tonic-gate 	(void) fprintf(stderr,
436*0Sstevel@tonic-gate 	    gettext("usage: %s [-cmu] [-o output] [-T directory] [-S mem]"
437*0Sstevel@tonic-gate 	    " [-z recsz]\n\t[-dfiMnr] [-b] [-t char] [-k keydef]"
438*0Sstevel@tonic-gate 	    " [+pos1 [-pos2]] files...\n"), CMDNAME);
439*0Sstevel@tonic-gate 	exit(E_USAGE);
440*0Sstevel@tonic-gate }
441*0Sstevel@tonic-gate 
442*0Sstevel@tonic-gate /*
443*0Sstevel@tonic-gate  * hold_file_descriptor() and release_file_descriptor() reserve a single file
444*0Sstevel@tonic-gate  * descriptor entry for later use.  We issue the hold prior to any loop that has
445*0Sstevel@tonic-gate  * an exit condition based on the receipt of EMFILE from an open() call; once we
446*0Sstevel@tonic-gate  * have exited, we can release, typically prior to opening a file for output.
447*0Sstevel@tonic-gate  */
448*0Sstevel@tonic-gate void
hold_file_descriptor()449*0Sstevel@tonic-gate hold_file_descriptor()
450*0Sstevel@tonic-gate {
451*0Sstevel@tonic-gate 	ASSERT(held_fd == -1);
452*0Sstevel@tonic-gate 
453*0Sstevel@tonic-gate 	if ((held_fd = open("/dev/null", O_RDONLY)) == -1)
454*0Sstevel@tonic-gate 		die(gettext("insufficient available file descriptors\n"));
455*0Sstevel@tonic-gate }
456*0Sstevel@tonic-gate 
457*0Sstevel@tonic-gate void
release_file_descriptor()458*0Sstevel@tonic-gate release_file_descriptor()
459*0Sstevel@tonic-gate {
460*0Sstevel@tonic-gate 	ASSERT(held_fd != -1);
461*0Sstevel@tonic-gate 
462*0Sstevel@tonic-gate 	(void) close(held_fd);
463*0Sstevel@tonic-gate 	held_fd = -1;
464*0Sstevel@tonic-gate }
465*0Sstevel@tonic-gate 
466*0Sstevel@tonic-gate void
copy_line_rec(const line_rec_t * a,line_rec_t * b)467*0Sstevel@tonic-gate copy_line_rec(const line_rec_t *a, line_rec_t *b)
468*0Sstevel@tonic-gate {
469*0Sstevel@tonic-gate 	(void) memcpy(b, a, sizeof (line_rec_t));
470*0Sstevel@tonic-gate }
471*0Sstevel@tonic-gate 
472*0Sstevel@tonic-gate void
trip_eof(FILE * f)473*0Sstevel@tonic-gate trip_eof(FILE *f)
474*0Sstevel@tonic-gate {
475*0Sstevel@tonic-gate 	if (feof(f))
476*0Sstevel@tonic-gate 		return;
477*0Sstevel@tonic-gate 
478*0Sstevel@tonic-gate 	(void) ungetc(fgetc(f), f);
479*0Sstevel@tonic-gate }
480*0Sstevel@tonic-gate 
481*0Sstevel@tonic-gate /*
482*0Sstevel@tonic-gate  * int cxwrite(int, char *, size_t)
483*0Sstevel@tonic-gate  *
484*0Sstevel@tonic-gate  * Overview
485*0Sstevel@tonic-gate  *   cxwrite() implements a buffered version of fwrite(ptr, nbytes, 1, .) on
486*0Sstevel@tonic-gate  *   file descriptors.  It returns -1 in the case that the write() fails to
487*0Sstevel@tonic-gate  *   write the current buffer contents.  cxwrite() must be flushed before being
488*0Sstevel@tonic-gate  *   applied to a new file descriptor.
489*0Sstevel@tonic-gate  *
490*0Sstevel@tonic-gate  * Return values
491*0Sstevel@tonic-gate  *   0 on success, -1 on error.
492*0Sstevel@tonic-gate  */
493*0Sstevel@tonic-gate int
cxwrite(int fd,char * ptr,size_t nbytes)494*0Sstevel@tonic-gate cxwrite(int fd, char *ptr, size_t nbytes)
495*0Sstevel@tonic-gate {
496*0Sstevel@tonic-gate 	static char buffer[XBUFFER_SIZE];
497*0Sstevel@tonic-gate 	static size_t offset = 0;
498*0Sstevel@tonic-gate 	size_t mbytes;
499*0Sstevel@tonic-gate 
500*0Sstevel@tonic-gate 	if (ptr == NULL) {
501*0Sstevel@tonic-gate 		errno = 0;
502*0Sstevel@tonic-gate 		while (offset -= write(fd, buffer, offset)) {
503*0Sstevel@tonic-gate 			if (errno)
504*0Sstevel@tonic-gate 				break;
505*0Sstevel@tonic-gate 		}
506*0Sstevel@tonic-gate 
507*0Sstevel@tonic-gate 		if (offset)
508*0Sstevel@tonic-gate 			return (-1);
509*0Sstevel@tonic-gate 
510*0Sstevel@tonic-gate 		return (0);
511*0Sstevel@tonic-gate 	}
512*0Sstevel@tonic-gate 
513*0Sstevel@tonic-gate 	while (nbytes != 0) {
514*0Sstevel@tonic-gate 		if (offset + nbytes > XBUFFER_SIZE)
515*0Sstevel@tonic-gate 			mbytes = XBUFFER_SIZE - offset;
516*0Sstevel@tonic-gate 		else
517*0Sstevel@tonic-gate 			mbytes = nbytes;
518*0Sstevel@tonic-gate 
519*0Sstevel@tonic-gate 		(void) memcpy(buffer + offset, ptr, mbytes);
520*0Sstevel@tonic-gate 		nbytes -= mbytes;
521*0Sstevel@tonic-gate 		offset += mbytes;
522*0Sstevel@tonic-gate 		ptr += mbytes;
523*0Sstevel@tonic-gate 
524*0Sstevel@tonic-gate 		if (nbytes) {
525*0Sstevel@tonic-gate 			errno = 0;
526*0Sstevel@tonic-gate 			while (offset -= write(fd, buffer, offset)) {
527*0Sstevel@tonic-gate 				if (errno)
528*0Sstevel@tonic-gate 					break;
529*0Sstevel@tonic-gate 			}
530*0Sstevel@tonic-gate 
531*0Sstevel@tonic-gate 			if (offset)
532*0Sstevel@tonic-gate 				return (-1);
533*0Sstevel@tonic-gate 		}
534*0Sstevel@tonic-gate 	}
535*0Sstevel@tonic-gate 
536*0Sstevel@tonic-gate 	return (0);
537*0Sstevel@tonic-gate }
538*0Sstevel@tonic-gate 
539*0Sstevel@tonic-gate /*
540*0Sstevel@tonic-gate  * int wxwrite(int, wchar_t *)
541*0Sstevel@tonic-gate  *
542*0Sstevel@tonic-gate  * Overview
543*0Sstevel@tonic-gate  *   wxwrite() implements a buffered write() function for null-terminated wide
544*0Sstevel@tonic-gate  *   character buffers with similar calling semantics to cxwrite().  It returns
545*0Sstevel@tonic-gate  *   -1 in the case that it fails to write the current buffer contents.
546*0Sstevel@tonic-gate  *   wxwrite() must be flushed before being applied to a new file descriptor.
547*0Sstevel@tonic-gate  *
548*0Sstevel@tonic-gate  * Return values
549*0Sstevel@tonic-gate  *   0 on success, -1 on error.
550*0Sstevel@tonic-gate  */
551*0Sstevel@tonic-gate int
wxwrite(int fd,wchar_t * ptr)552*0Sstevel@tonic-gate wxwrite(int fd, wchar_t *ptr)
553*0Sstevel@tonic-gate {
554*0Sstevel@tonic-gate 	static char *convert_buffer;
555*0Sstevel@tonic-gate 	static size_t convert_bufsize = 1024;
556*0Sstevel@tonic-gate 	size_t req_bufsize;
557*0Sstevel@tonic-gate 
558*0Sstevel@tonic-gate 	if (ptr == NULL)
559*0Sstevel@tonic-gate 		return (cxwrite(NULL, 0, 1));
560*0Sstevel@tonic-gate 
561*0Sstevel@tonic-gate 	if (convert_buffer == NULL)
562*0Sstevel@tonic-gate 		convert_buffer = safe_realloc(NULL, convert_bufsize);
563*0Sstevel@tonic-gate 	/*
564*0Sstevel@tonic-gate 	 * We use wcstombs(NULL, ., .) to verify that we have an adequate
565*0Sstevel@tonic-gate 	 * buffer size for the conversion.  Since this buffer was converted into
566*0Sstevel@tonic-gate 	 * wide character format earlier, we can safely assume that the buffer
567*0Sstevel@tonic-gate 	 * can be converted back to the external multibyte form.
568*0Sstevel@tonic-gate 	 */
569*0Sstevel@tonic-gate 	req_bufsize = wcstombs(NULL, ptr, convert_bufsize);
570*0Sstevel@tonic-gate 	if (req_bufsize > convert_bufsize) {
571*0Sstevel@tonic-gate 		convert_bufsize = req_bufsize + 1;
572*0Sstevel@tonic-gate 		convert_buffer = safe_realloc(convert_buffer, convert_bufsize);
573*0Sstevel@tonic-gate 	}
574*0Sstevel@tonic-gate 
575*0Sstevel@tonic-gate 	(void) wcstombs(convert_buffer, ptr, convert_bufsize);
576*0Sstevel@tonic-gate 
577*0Sstevel@tonic-gate 	return (cxwrite(fd, convert_buffer, req_bufsize));
578*0Sstevel@tonic-gate }
579*0Sstevel@tonic-gate 
580*0Sstevel@tonic-gate int
xstreql(const char * a,const char * b)581*0Sstevel@tonic-gate xstreql(const char *a, const char *b)
582*0Sstevel@tonic-gate {
583*0Sstevel@tonic-gate 	return (strcmp(a, b) == 0);
584*0Sstevel@tonic-gate }
585*0Sstevel@tonic-gate 
586*0Sstevel@tonic-gate int
xstrneql(const char * a,const char * b,const size_t l)587*0Sstevel@tonic-gate xstrneql(const char *a, const char *b, const size_t l)
588*0Sstevel@tonic-gate {
589*0Sstevel@tonic-gate 	return (strncmp(a, b, l) == 0);
590*0Sstevel@tonic-gate }
591*0Sstevel@tonic-gate 
592*0Sstevel@tonic-gate char *
xstrnchr(const char * S,const int c,const size_t n)593*0Sstevel@tonic-gate xstrnchr(const char *S, const int c, const size_t n)
594*0Sstevel@tonic-gate {
595*0Sstevel@tonic-gate 	const char	*eS = S + n;
596*0Sstevel@tonic-gate 
597*0Sstevel@tonic-gate 	do {
598*0Sstevel@tonic-gate 		if (*S == (char)c)
599*0Sstevel@tonic-gate 			return ((char *)S);
600*0Sstevel@tonic-gate 	} while (++S < eS);
601*0Sstevel@tonic-gate 
602*0Sstevel@tonic-gate 	return (NULL);
603*0Sstevel@tonic-gate }
604*0Sstevel@tonic-gate 
605*0Sstevel@tonic-gate void
xstrninv(char * s,ssize_t start,ssize_t length)606*0Sstevel@tonic-gate xstrninv(char *s, ssize_t start, ssize_t length)
607*0Sstevel@tonic-gate {
608*0Sstevel@tonic-gate 	ssize_t i;
609*0Sstevel@tonic-gate 
610*0Sstevel@tonic-gate 	for (i = start; i < start + length; i++)
611*0Sstevel@tonic-gate 		s[i] = UCHAR_MAX - s[i];
612*0Sstevel@tonic-gate }
613*0Sstevel@tonic-gate 
614*0Sstevel@tonic-gate int
xwcsneql(const wchar_t * a,const wchar_t * b,const size_t length)615*0Sstevel@tonic-gate xwcsneql(const wchar_t *a, const wchar_t *b, const size_t length)
616*0Sstevel@tonic-gate {
617*0Sstevel@tonic-gate 	return (wcsncmp(a, b, length) == 0);
618*0Sstevel@tonic-gate }
619*0Sstevel@tonic-gate 
620*0Sstevel@tonic-gate wchar_t *
xwsnchr(const wchar_t * ws,const wint_t wc,const size_t n)621*0Sstevel@tonic-gate xwsnchr(const wchar_t *ws, const wint_t wc, const size_t n)
622*0Sstevel@tonic-gate {
623*0Sstevel@tonic-gate 	const wchar_t	*ews = ws + n;
624*0Sstevel@tonic-gate 
625*0Sstevel@tonic-gate 	do {
626*0Sstevel@tonic-gate 		if (*ws == (wchar_t)wc)
627*0Sstevel@tonic-gate 			return ((wchar_t *)ws);
628*0Sstevel@tonic-gate 	} while (++ws < ews);
629*0Sstevel@tonic-gate 
630*0Sstevel@tonic-gate 	return (NULL);
631*0Sstevel@tonic-gate }
632*0Sstevel@tonic-gate 
633*0Sstevel@tonic-gate void
xwcsninv(wchar_t * s,ssize_t start,ssize_t length)634*0Sstevel@tonic-gate xwcsninv(wchar_t *s, ssize_t start, ssize_t length)
635*0Sstevel@tonic-gate {
636*0Sstevel@tonic-gate 	ssize_t	i;
637*0Sstevel@tonic-gate 
638*0Sstevel@tonic-gate 	for (i = start; i < start + length; i++)
639*0Sstevel@tonic-gate 		s[i] = WCHAR_MAX - s[i];
640*0Sstevel@tonic-gate }
641*0Sstevel@tonic-gate 
642*0Sstevel@tonic-gate #ifdef _LITTLE_ENDIAN
643*0Sstevel@tonic-gate void
xwcsntomsb(wchar_t * s,ssize_t length)644*0Sstevel@tonic-gate xwcsntomsb(wchar_t *s, ssize_t length)
645*0Sstevel@tonic-gate {
646*0Sstevel@tonic-gate 	ssize_t i;
647*0Sstevel@tonic-gate 
648*0Sstevel@tonic-gate 	ASSERT(sizeof (wchar_t) == sizeof (uint32_t));
649*0Sstevel@tonic-gate 
650*0Sstevel@tonic-gate 	for (i = 0; i < length; i++, s++) {
651*0Sstevel@tonic-gate 		char *t = (char *)s;
652*0Sstevel@tonic-gate 		char u;
653*0Sstevel@tonic-gate 
654*0Sstevel@tonic-gate 		u = *t;
655*0Sstevel@tonic-gate 		*t = *(t + 3);
656*0Sstevel@tonic-gate 		*(t + 3) = u;
657*0Sstevel@tonic-gate 
658*0Sstevel@tonic-gate 		u = *(t + 1);
659*0Sstevel@tonic-gate 		*(t + 1) = *(t + 2);
660*0Sstevel@tonic-gate 		*(t + 2) = u;
661*0Sstevel@tonic-gate 	}
662*0Sstevel@tonic-gate }
663*0Sstevel@tonic-gate #endif /* _LITTLE_ENDIAN */
664*0Sstevel@tonic-gate 
665*0Sstevel@tonic-gate wchar_t *
xmemwchar(wchar_t * s,wchar_t w,ssize_t length)666*0Sstevel@tonic-gate xmemwchar(wchar_t *s, wchar_t w, ssize_t length)
667*0Sstevel@tonic-gate {
668*0Sstevel@tonic-gate 	ssize_t i = length;
669*0Sstevel@tonic-gate 
670*0Sstevel@tonic-gate 	while (--i > 0) {
671*0Sstevel@tonic-gate 		if (*s == w)
672*0Sstevel@tonic-gate 			return (s);
673*0Sstevel@tonic-gate 		s++;
674*0Sstevel@tonic-gate 	}
675*0Sstevel@tonic-gate 
676*0Sstevel@tonic-gate 	return (NULL);
677*0Sstevel@tonic-gate }
678*0Sstevel@tonic-gate 
679*0Sstevel@tonic-gate void
xcp(char * dst,char * src,off_t size)680*0Sstevel@tonic-gate xcp(char *dst, char *src, off_t size)
681*0Sstevel@tonic-gate {
682*0Sstevel@tonic-gate 	int fd_in, fd_out;
683*0Sstevel@tonic-gate 	void *mm_in;
684*0Sstevel@tonic-gate 	size_t chunksize = 2 * MEGABYTE;
685*0Sstevel@tonic-gate 	int i;
686*0Sstevel@tonic-gate 	ssize_t nchunks = size / chunksize;
687*0Sstevel@tonic-gate 	ssize_t lastchunk = size % chunksize;
688*0Sstevel@tonic-gate 
689*0Sstevel@tonic-gate 	if (dst == NULL || src == NULL)
690*0Sstevel@tonic-gate 		return;
691*0Sstevel@tonic-gate 
692*0Sstevel@tonic-gate 	if ((fd_in = open(src, O_RDONLY)) < 0)
693*0Sstevel@tonic-gate 		die(EMSG_OPEN, src);
694*0Sstevel@tonic-gate 	if ((fd_out = open(dst, O_RDWR | O_CREAT | O_TRUNC, OUTPUT_MODE)) < 0)
695*0Sstevel@tonic-gate 		die(EMSG_OPEN, dst);
696*0Sstevel@tonic-gate 
697*0Sstevel@tonic-gate 	for (i = 0; i < nchunks; i++) {
698*0Sstevel@tonic-gate 		if ((mm_in = mmap(0, chunksize, PROT_READ, MAP_SHARED, fd_in,
699*0Sstevel@tonic-gate 		    i * chunksize)) == MAP_FAILED)
700*0Sstevel@tonic-gate 			die(EMSG_MMAP, src);
701*0Sstevel@tonic-gate 
702*0Sstevel@tonic-gate 		if (write(fd_out, mm_in, chunksize) != chunksize)
703*0Sstevel@tonic-gate 			die(EMSG_WRITE, dst);
704*0Sstevel@tonic-gate 
705*0Sstevel@tonic-gate 		(void) munmap(mm_in, chunksize);
706*0Sstevel@tonic-gate 	}
707*0Sstevel@tonic-gate 
708*0Sstevel@tonic-gate 	if (lastchunk) {
709*0Sstevel@tonic-gate 		if ((mm_in = mmap(0, lastchunk, PROT_READ, MAP_SHARED, fd_in,
710*0Sstevel@tonic-gate 		    nchunks * chunksize)) == MAP_FAILED)
711*0Sstevel@tonic-gate 			die(EMSG_MMAP, src);
712*0Sstevel@tonic-gate 
713*0Sstevel@tonic-gate 		if (write(fd_out, mm_in, lastchunk) != lastchunk)
714*0Sstevel@tonic-gate 			die(EMSG_WRITE, dst);
715*0Sstevel@tonic-gate 
716*0Sstevel@tonic-gate 		(void) munmap(mm_in, lastchunk);
717*0Sstevel@tonic-gate 	}
718*0Sstevel@tonic-gate 
719*0Sstevel@tonic-gate 	(void) close(fd_in);
720*0Sstevel@tonic-gate 
721*0Sstevel@tonic-gate 	if (close(fd_out) == -1)
722*0Sstevel@tonic-gate 		die(EMSG_CLOSE, dst);
723*0Sstevel@tonic-gate }
724*0Sstevel@tonic-gate 
725*0Sstevel@tonic-gate /*PRINTFLIKE1*/
726*0Sstevel@tonic-gate void
warn(const char * format,...)727*0Sstevel@tonic-gate warn(const char *format, ...)
728*0Sstevel@tonic-gate {
729*0Sstevel@tonic-gate 	int err = errno;
730*0Sstevel@tonic-gate 	va_list alist;
731*0Sstevel@tonic-gate 
732*0Sstevel@tonic-gate 	if (pname != NULL)
733*0Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(PNAME_FMT), pname);
734*0Sstevel@tonic-gate 
735*0Sstevel@tonic-gate 	va_start(alist, format);
736*0Sstevel@tonic-gate 	(void) vfprintf(stderr, format, alist);
737*0Sstevel@tonic-gate 	va_end(alist);
738*0Sstevel@tonic-gate 
739*0Sstevel@tonic-gate 	if (strrchr(format, '\n') == NULL)
740*0Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERRNO_FMT), strerror(err));
741*0Sstevel@tonic-gate }
742*0Sstevel@tonic-gate 
743*0Sstevel@tonic-gate /*PRINTFLIKE1*/
744*0Sstevel@tonic-gate void
die(const char * format,...)745*0Sstevel@tonic-gate die(const char *format, ...)
746*0Sstevel@tonic-gate {
747*0Sstevel@tonic-gate 	int err = errno;
748*0Sstevel@tonic-gate 	va_list alist;
749*0Sstevel@tonic-gate 
750*0Sstevel@tonic-gate 	if (pname != NULL)
751*0Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(PNAME_FMT), pname);
752*0Sstevel@tonic-gate 
753*0Sstevel@tonic-gate 	va_start(alist, format);
754*0Sstevel@tonic-gate 	(void) vfprintf(stderr, format, alist);
755*0Sstevel@tonic-gate 	va_end(alist);
756*0Sstevel@tonic-gate 
757*0Sstevel@tonic-gate 	if (strrchr(format, '\n') == NULL)
758*0Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERRNO_FMT), strerror(err));
759*0Sstevel@tonic-gate 
760*0Sstevel@tonic-gate 	exit(E_ERROR);
761*0Sstevel@tonic-gate }
762*0Sstevel@tonic-gate 
763*0Sstevel@tonic-gate #ifdef DEBUG
764*0Sstevel@tonic-gate /*
765*0Sstevel@tonic-gate  * pprintc() is called only by xdump().
766*0Sstevel@tonic-gate  */
767*0Sstevel@tonic-gate #define	BYTES_PER_LINE	16
768*0Sstevel@tonic-gate static void
pprintc(FILE * fp,char c)769*0Sstevel@tonic-gate pprintc(FILE *fp, char c)
770*0Sstevel@tonic-gate {
771*0Sstevel@tonic-gate 	if (isspace((uchar_t)c))
772*0Sstevel@tonic-gate 		(void) fprintf(fp, " ");
773*0Sstevel@tonic-gate 	else if (isprint((uchar_t)c))
774*0Sstevel@tonic-gate 		(void) fprintf(fp, "%c", c);
775*0Sstevel@tonic-gate 	else
776*0Sstevel@tonic-gate 		(void) fprintf(fp, ".");
777*0Sstevel@tonic-gate }
778*0Sstevel@tonic-gate 
779*0Sstevel@tonic-gate static void
pprintwc(FILE * fp,wchar_t c)780*0Sstevel@tonic-gate pprintwc(FILE *fp, wchar_t c)
781*0Sstevel@tonic-gate {
782*0Sstevel@tonic-gate 	if (iswspace(c))
783*0Sstevel@tonic-gate 		(void) fprintf(fp, " ");
784*0Sstevel@tonic-gate 	else if (iswprint(c))
785*0Sstevel@tonic-gate 		(void) fprintf(fp, "%wc", c);
786*0Sstevel@tonic-gate 	else
787*0Sstevel@tonic-gate 		(void) fprintf(fp, ".");
788*0Sstevel@tonic-gate }
789*0Sstevel@tonic-gate 
790*0Sstevel@tonic-gate /*
791*0Sstevel@tonic-gate  * xdump() is used only for debugging purposes.
792*0Sstevel@tonic-gate  */
793*0Sstevel@tonic-gate void
xdump(FILE * fp,uchar_t * buf,size_t bufsize,int wide)794*0Sstevel@tonic-gate xdump(FILE *fp, uchar_t *buf, size_t bufsize, int wide)
795*0Sstevel@tonic-gate {
796*0Sstevel@tonic-gate 	int i;
797*0Sstevel@tonic-gate 	size_t nc = 0;
798*0Sstevel@tonic-gate 	uchar_t d[BYTES_PER_LINE];
799*0Sstevel@tonic-gate 
800*0Sstevel@tonic-gate 	for (; nc < bufsize; buf++) {
801*0Sstevel@tonic-gate 		d[nc % BYTES_PER_LINE] = *buf;
802*0Sstevel@tonic-gate 		if (nc % BYTES_PER_LINE == 0) {
803*0Sstevel@tonic-gate 			(void) fprintf(fp, "%08x:", nc);
804*0Sstevel@tonic-gate 		}
805*0Sstevel@tonic-gate 		(void) fprintf(fp, " %02x", *buf);
806*0Sstevel@tonic-gate 		nc++;
807*0Sstevel@tonic-gate 		if (nc % BYTES_PER_LINE == 0) {
808*0Sstevel@tonic-gate 			(void) fprintf(fp, "  ");
809*0Sstevel@tonic-gate 			if (wide) {
810*0Sstevel@tonic-gate 				for (i = 0; i < BYTES_PER_LINE;
811*0Sstevel@tonic-gate 				    i += sizeof (wchar_t))
812*0Sstevel@tonic-gate 					pprintwc(fp, *(wchar_t *)(d + i));
813*0Sstevel@tonic-gate 			} else {
814*0Sstevel@tonic-gate 				for (i = 0; i < BYTES_PER_LINE; i++)
815*0Sstevel@tonic-gate 					pprintc(fp, d[i]);
816*0Sstevel@tonic-gate 			}
817*0Sstevel@tonic-gate 			(void) fprintf(fp, "\n");
818*0Sstevel@tonic-gate 		}
819*0Sstevel@tonic-gate 	}
820*0Sstevel@tonic-gate 
821*0Sstevel@tonic-gate 	for (i = nc % BYTES_PER_LINE; i < BYTES_PER_LINE; i++)
822*0Sstevel@tonic-gate 		(void) fprintf(fp, "   ");
823*0Sstevel@tonic-gate 
824*0Sstevel@tonic-gate 	(void) fprintf(fp, "  ");
825*0Sstevel@tonic-gate 
826*0Sstevel@tonic-gate 	if (wide) {
827*0Sstevel@tonic-gate 		for (i = 0; i < nc % BYTES_PER_LINE; i += sizeof (wchar_t))
828*0Sstevel@tonic-gate 			pprintwc(fp, *(wchar_t *)(d + i));
829*0Sstevel@tonic-gate 	} else {
830*0Sstevel@tonic-gate 		for (i = 0; i < nc % BYTES_PER_LINE; i++)
831*0Sstevel@tonic-gate 			pprintc(fp, d[i]);
832*0Sstevel@tonic-gate 	}
833*0Sstevel@tonic-gate 
834*0Sstevel@tonic-gate 	(void) fprintf(fp, "\n");
835*0Sstevel@tonic-gate }
836*0Sstevel@tonic-gate #endif /* DEBUG */
837