xref: /illumos-gate/usr/src/cmd/pr/pr.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
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 /*
28  *	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
29  *	All Rights Reserved
30  */
31 
32 /*
33  * University Copyright- Copyright (c) 1982, 1986, 1988
34  * The Regents of the University of California
35  * All Rights Reserved
36  *
37  * University Acknowledgment- Portions of this document are derived from
38  * software developed by the University of California, Berkeley, and its
39  * contributors.
40  */
41 
42 /*
43  *	PR command (print files in pages and columns, with headings)
44  *	2+head+2+page[56]+5
45  */
46 
47 #include <stdio.h>
48 #include <signal.h>
49 #include <ctype.h>
50 #include <sys/types.h>
51 #include <sys/stat.h>
52 #include <unistd.h>
53 #include <stdlib.h>
54 #include <locale.h>
55 #include <string.h>
56 #include <limits.h>
57 #include <wchar.h>
58 #include <errno.h>
59 
60 #define	ESC		'\033'
61 #define	LENGTH		66
62 #define	LINEW		72
63 #define	NUMW		5
64 #define	MARGIN		10
65 #define	DEFTAB		8
66 #define	NFILES		10
67 #define	STDINNAME()	nulls
68 #define	PROMPT()	(void) putc('\7', stderr) /* BEL */
69 #define	NOFILE		nulls
70 #define	ETABS		(Inpos % Etabn)
71 #define	NSEPC		'\t'
72 #define	HEAD		gettext("%s  %s Page %d\n\n\n"), date, head, Page
73 #define	cerror(S)	(void) fprintf(stderr, "pr: %s", gettext(S))
74 #define	done()		if (Ttyout) (void) chmod(Ttyout, Mode)
75 #define	ALL_NUMS(s)	(strspn(s, "0123456789") == strlen(s))
76 #define	REMOVE_ARG(argc, argp)					\
77 			{					\
78 				char	**p = argp;		\
79 				while (*p != NULL)		\
80 				{				\
81 					*p = *(p + 1);		\
82 					p++;			\
83 				}				\
84 				argc--;				\
85 			}
86 #define	SQUEEZE_ARG(argp, ind, n)					\
87 			{					\
88 				int	i;			\
89 				for (i = ind; argp[i]; i++)	\
90 					argp[i] = argp[i + n];	\
91 			}
92 
93 /*
94  *   ---date time format---
95  *   b -- abbreviated month name
96  *   e -- day of month
97  *   H -- Hour (24 hour version)
98  *   M -- Minute
99  *   Y -- Year in the form ccyy
100  */
101 #define	FORMAT		"%b %e %H:%M %Y"
102 
103 typedef	int	ANY;
104 typedef	unsigned	int	UNS;
105 typedef	struct	{ FILE *f_f; char *f_name; wchar_t f_nextc; } FILS;
106 typedef	struct	{int fold; int skip; int eof; } foldinf;
107 typedef	struct	{ wchar_t *c_ptr, *c_ptr0; long c_lno; int c_skip; } *COLP;
108 typedef struct	err { struct err *e_nextp; char *e_mess; } ERR;
109 
110 /*
111  * Global data.
112  */
113 static	FILS	*Files;
114 static	mode_t	Mode;
115 static	int	Multi = 0;
116 static	int	Nfiles = 0;
117 static	int	Error = 0;
118 static	char	nulls[] = "";
119 static	char	*Ttyout;
120 static	char	obuf[BUFSIZ];
121 static	char	time_buf[50];	/* array to hold the time and date */
122 static	long	Lnumb = 0;
123 static	FILE	*Ttyin = stdin;
124 static	int	Dblspace = 1;
125 static	int	Fpage = 1;
126 static	int	Formfeed = 0;
127 static	int	Length = LENGTH;
128 static	int	Linew = 0;
129 static	int	Offset = 0;
130 static	int	Ncols = 0;
131 static	int	Pause = 0;
132 static	wchar_t	Sepc = 0;
133 static	int	Colw;
134 static	int	Plength;
135 static	int	Margin = MARGIN;
136 static	int	Numw;
137 static	int	Nsepc = NSEPC;
138 static	int	Report = 1;
139 static	int	Etabn = 0;
140 static	wchar_t	Etabc = '\t';
141 static	int	Itabn = 0;
142 static	wchar_t	Itabc = '\t';
143 static	int	fold = 0;
144 static	int	foldcol = 0;
145 static	int	alleof = 0;
146 static	char	*Head = NULL;
147 static	wchar_t *Buffer = NULL, *Bufend, *Bufptr;
148 static	UNS	Buflen;
149 static	COLP	Colpts;
150 static	foldinf	*Fcol;
151 static	int	Page;
152 static	wchar_t	C = '\0';
153 static	int	Nspace;
154 static	int	Inpos;
155 static	int	Outpos;
156 static	int	Lcolpos;
157 static	int	Pcolpos;
158 static	int	Line;
159 static	ERR	*Err = NULL;
160 static	ERR	*Lasterr = (ERR *)&Err;
161 static	int	mbcurmax = 1;
162 
163 /*
164  * Function prototypes.
165  */
166 static	void	onintr();
167 static	ANY	*getspace();
168 static	int	findopt(int, char **);
169 static	void	fixtty();
170 static	char 	*GETDATE();
171 static	char	*ffiler(char *);
172 static	int	print(char *);
173 static	void	putpage();
174 static	void	foldpage();
175 static	void	nexbuf();
176 static	void	foldbuf();
177 static	void	balance(int);
178 static	int	readbuf(wchar_t **, int, COLP);
179 static	wint_t	get(int);
180 static	int	put(wchar_t);
181 static	void	putspace();
182 static	void	unget(int);
183 static	FILE	*mustopen(char *, FILS *);
184 static	void	die(char *);
185 static	void	errprint();
186 static	void	usage(int);
187 static wint_t	_fgetwc_pr(FILE *, int *);
188 static size_t	freadw(wchar_t *, size_t, FILE *);
189 
190 
191 int
192 main(int argc, char **argv)
193 {
194 	FILS	fstr[NFILES];
195 	int	nfdone = 0;
196 
197 
198 	/* Get locale variables for environment */
199 	(void) setlocale(LC_ALL, "");
200 
201 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
202 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
203 #endif
204 	(void) textdomain(TEXT_DOMAIN);
205 
206 	mbcurmax = MB_CUR_MAX;
207 	Files = fstr;
208 	for (argc = findopt(argc, argv); argc > 0; --argc, ++argv) {
209 		if (Multi == 'm') {
210 			if (Nfiles >= NFILES - 1) die("too many files");
211 			if (mustopen(*argv, &Files[Nfiles++]) == NULL)
212 				++nfdone;	/* suppress printing */
213 		} else {
214 			if (print(*argv))
215 				(void) fclose(Files->f_f);
216 			++nfdone;
217 		}
218 	}
219 	if (!nfdone)	/* no files named, use stdin */
220 		(void) print(NOFILE);	/* on GCOS, use current file, if any */
221 
222 	if (Report) {
223 		errprint();	/* print accumulated error reports */
224 		exit(Error);
225 	}
226 
227 	return (Error);
228 }
229 
230 
231 /*
232  * findopt() returns argc modified to be the number of explicitly supplied
233  * filenames, including '-', the explicit request to use stdin.
234  * argc == 0 implies that no filenames were supplied and stdin should be used.
235  * Options are striped from argv and only file names are returned.
236  */
237 
238 static	int
239 findopt(int argc, char **argv)
240 {
241 	int	eargc = 0;
242 	int	c;
243 	int	mflg = 0;
244 	int	aflg = 0;
245 	int	optnum;
246 	int	argv_ind;
247 	int	end_opt;
248 	int	i;
249 	int	isarg = 0;
250 
251 	fixtty();
252 
253 	/* Handle page number option */
254 	for (optnum = 1, end_opt = 0; optnum < argc && !end_opt; optnum++) {
255 		switch (*argv[optnum]) {
256 		case '+':
257 			/* check for all digits */
258 			if (strlen(&argv[optnum][1]) !=
259 			    strspn(&argv[optnum][1], "0123456789")) {
260 				(void) fprintf(stderr, gettext(
261 				    "pr: Badly formed number\n"));
262 				exit(1);
263 			}
264 
265 			if ((Fpage = (int)strtol(&argv[optnum][1],
266 			    (char **)NULL, 10)) < 0) {
267 				(void) fprintf(stderr, gettext(
268 				    "pr: Badly formed number\n"));
269 				exit(1);
270 			}
271 			REMOVE_ARG(argc, &argv[optnum]);
272 			optnum--;
273 			break;
274 
275 		case '-':
276 			/* Check for end of options */
277 			if (argv[optnum][1] == '-') {
278 				end_opt++;
279 				break;
280 			}
281 
282 			if (argv[optnum][1] == 'h' || argv[optnum][1] == 'l' ||
283 			    argv[optnum][1] == 'o' || argv[optnum][1] == 'w')
284 				isarg = 1;
285 			else
286 				isarg = 0;
287 
288 			break;
289 
290 		default:
291 			if (isarg == 0)
292 				end_opt++;
293 			else
294 				isarg = 0;
295 			break;
296 		}
297 	}
298 
299 	/*
300 	 * Handle options with optional arguments.
301 	 * If optional arguments are present they may not be separated
302 	 * from the option letter.
303 	 */
304 
305 	for (optnum = 1; optnum < argc; optnum++) {
306 		if (argv[optnum][0] == '-' && argv[optnum][1] == '-')
307 			/* End of options */
308 			break;
309 
310 		if (argv[optnum][0] == '-' && argv[optnum][1] == '\0')
311 			/* stdin file name */
312 			continue;
313 
314 		if (argv[optnum][0] != '-')
315 			/* not option */
316 			continue;
317 
318 		for (argv_ind = 1; argv[optnum][argv_ind] != '\0'; argv_ind++) {
319 			switch (argv[optnum][argv_ind]) {
320 			case 'e':
321 				SQUEEZE_ARG(argv[optnum], argv_ind, 1);
322 				if ((c = argv[optnum][argv_ind]) != '\0' &&
323 				    !isdigit(c)) {
324 					int	r;
325 					wchar_t	wc;
326 					r = mbtowc(&wc, &argv[optnum][argv_ind],
327 						mbcurmax);
328 					if (r == -1) {
329 						(void) fprintf(stderr, gettext(
330 "pr: Illegal character in -e option\n"));
331 						exit(1);
332 					}
333 					Etabc = wc;
334 					SQUEEZE_ARG(argv[optnum], argv_ind, r);
335 				}
336 				if (isdigit(argv[optnum][argv_ind])) {
337 					Etabn = (int)strtol(&argv[optnum]
338 					    [argv_ind], (char **)NULL, 10);
339 					while (isdigit(argv[optnum][argv_ind]))
340 					    SQUEEZE_ARG(argv[optnum],
341 							argv_ind, 1);
342 				}
343 				if (Etabn <= 0)
344 					Etabn = DEFTAB;
345 				argv_ind--;
346 				break;
347 
348 			case 'i':
349 				SQUEEZE_ARG(argv[optnum], argv_ind, 1);
350 				if ((c = argv[optnum][argv_ind]) != '\0' &&
351 				    !isdigit(c)) {
352 					int	r;
353 					wchar_t	wc;
354 					r = mbtowc(&wc, &argv[optnum][argv_ind],
355 						mbcurmax);
356 					if (r == -1) {
357 						(void) fprintf(stderr, gettext(
358 "pr: Illegal character in -i option\n"));
359 						exit(1);
360 					}
361 					Itabc = wc;
362 					SQUEEZE_ARG(argv[optnum], argv_ind, r);
363 				}
364 				if (isdigit(argv[optnum][argv_ind])) {
365 					Itabn = (int)strtol(&argv[optnum]
366 					    [argv_ind], (char **)NULL, 10);
367 					while (isdigit(argv[optnum][argv_ind]))
368 					    SQUEEZE_ARG(argv[optnum],
369 							argv_ind, 1);
370 				}
371 				if (Itabn <= 0)
372 					Itabn = DEFTAB;
373 				argv_ind--;
374 				break;
375 
376 
377 			case 'n':
378 				++Lnumb;
379 				SQUEEZE_ARG(argv[optnum], argv_ind, 1);
380 				if ((c = argv[optnum][argv_ind]) != '\0' &&
381 				    !isdigit(c)) {
382 					int	r;
383 					wchar_t	wc;
384 					r = mbtowc(&wc, &argv[optnum][argv_ind],
385 						mbcurmax);
386 					if (r == -1) {
387 						(void) fprintf(stderr, gettext(
388 "pr: Illegal character in -n option\n"));
389 						exit(1);
390 					}
391 					Nsepc = wc;
392 					SQUEEZE_ARG(argv[optnum], argv_ind, r);
393 				}
394 				if (isdigit(argv[optnum][argv_ind])) {
395 					Numw = (int)strtol(&argv[optnum]
396 					    [argv_ind], (char **)NULL, 10);
397 					while (isdigit(argv[optnum][argv_ind]))
398 					    SQUEEZE_ARG(argv[optnum],
399 							argv_ind, 1);
400 				}
401 				argv_ind--;
402 				if (!Numw)
403 					Numw = NUMW;
404 				break;
405 
406 			case 's':
407 				SQUEEZE_ARG(argv[optnum], argv_ind, 1);
408 				if ((Sepc = argv[optnum][argv_ind]) == '\0')
409 					Sepc = '\t';
410 				else {
411 					int	r;
412 					wchar_t	wc;
413 					r = mbtowc(&wc, &argv[optnum][argv_ind],
414 						mbcurmax);
415 					if (r == -1) {
416 						(void) fprintf(stderr, gettext(
417 "pr: Illegal character in -s option\n"));
418 						exit(1);
419 					}
420 					Sepc = wc;
421 					SQUEEZE_ARG(argv[optnum], argv_ind, r);
422 				}
423 				argv_ind--;
424 				break;
425 
426 			default:
427 				break;
428 			}
429 		}
430 		if (argv[optnum][0] == '-' && argv[optnum][1] == '\0') {
431 			REMOVE_ARG(argc, &argv[optnum]);
432 			optnum--;
433 		}
434 	}
435 
436 	/* Now get the other options */
437 	while ((c = getopt(argc, argv, "0123456789adfFh:l:mo:prtw:"))
438 	    != EOF) {
439 		switch (c) {
440 		case '0':
441 		case '1':
442 		case '2':
443 		case '3':
444 		case '4':
445 		case '5':
446 		case '6':
447 		case '7':
448 		case '8':
449 		case '9':
450 			Ncols *= 10;
451 			Ncols += c - '0';
452 			break;
453 
454 		case 'a':
455 			aflg++;
456 			if (!Multi)
457 				Multi = c;
458 			break;
459 
460 		case 'd':
461 			Dblspace = 2;
462 			break;
463 
464 		case 'f':
465 			++Formfeed;
466 			++Pause;
467 			break;
468 
469 		case 'h':
470 			Head = optarg;
471 			break;
472 
473 		case 'l':
474 			if (strlen(optarg) != strspn(optarg, "0123456789"))
475 				usage(1);
476 			Length = (int)strtol(optarg, (char **)NULL, 10);
477 			break;
478 
479 		case 'm':
480 			mflg++;
481 			Multi = c;
482 			break;
483 
484 		case 'o':
485 			if (strlen(optarg) != strspn(optarg, "0123456789"))
486 				usage(1);
487 			Offset = (int)strtol(optarg, (char **)NULL, 10);
488 			break;
489 
490 		case 'p':
491 			++Pause;
492 			break;
493 
494 		case 'r':
495 			Report = 0;
496 			break;
497 
498 		case 't':
499 			Margin = 0;
500 			break;
501 
502 		case 'w':
503 			if (strlen(optarg) != strspn(optarg, "0123456789"))
504 				usage(1);
505 			Linew = (int)strtol(optarg, (char **)NULL, 10);
506 			break;
507 
508 		case 'F':
509 #ifdef	XPG4
510 			++Formfeed;
511 #else
512 			fold++;
513 #endif
514 			break;
515 
516 		case '?':
517 			usage(2);
518 			break;
519 
520 		default :
521 			usage(2);
522 		}
523 	}
524 
525 	/* Count the file names and strip options */
526 	for (i = 1; i < argc; i++) {
527 		/* Check for explicit stdin */
528 		if ((argv[i][0] == '-') && (argv[i][1] == '\0')) {
529 			argv[eargc++][0] = '\0';
530 			REMOVE_ARG(argc, &argv[i]);
531 			if (i < optind)
532 				optind--;
533 		}
534 	}
535 	for (i = eargc; optind < argc; i++, optind++) {
536 		argv[i] = argv[optind];
537 		eargc++;
538 	}
539 
540 	/* Check options */
541 	if (Ncols == 0)
542 		Ncols = 1;
543 
544 	if (mflg && (Ncols > 1)) {
545 		(void) fprintf(stderr,
546 		    gettext("pr: only one of either -m or -column allowed\n"));
547 		usage(1);
548 	}
549 
550 	if (Ncols == 1 && fold)
551 		Multi = 'm';
552 
553 	if (Length <= 0)
554 		Length = LENGTH;
555 
556 	if (Length <= Margin)
557 		Margin = 0;
558 
559 	Plength = Length - Margin/2;
560 
561 	if (Multi == 'm')
562 		Ncols = eargc;
563 
564 	switch (Ncols) {
565 	case 0:
566 		Ncols = 1;
567 		break;
568 
569 	case 1:
570 		break;
571 
572 	default:
573 		if (Etabn == 0)	/* respect explicit tab specification */
574 			Etabn = DEFTAB;
575 		if (Itabn == 0)
576 			Itabn = DEFTAB;
577 	}
578 
579 	if ((Fcol = (foldinf *) malloc(sizeof (foldinf) * Ncols)) == NULL) {
580 		(void) fprintf(stderr, gettext("pr: malloc failed\n"));
581 		exit(1);
582 	}
583 	for (i = 0; i < Ncols; i++)
584 		Fcol[i].fold = Fcol[i].skip = 0;
585 
586 	if (Linew == 0)
587 		Linew = Ncols != 1 && Sepc == 0 ? LINEW : 512;
588 
589 	if (Lnumb) {
590 		int numw;
591 
592 		if (Nsepc == '\t') {
593 			if (Itabn == 0)
594 				numw = Numw + DEFTAB - (Numw % DEFTAB);
595 			else
596 				numw = Numw + Itabn - (Numw % Itabn);
597 		} else {
598 			numw = Numw + ((iswprint(Nsepc)) ? 1 : 0);
599 		}
600 		Linew -= (Multi == 'm') ? numw : numw * Ncols;
601 	}
602 
603 	if ((Colw = (Linew - Ncols + 1)/Ncols) < 1)
604 		die("width too small");
605 
606 	if (Ncols != 1 && Multi == 0) {
607 		/* Buflen should take the number of wide characters */
608 		/* Not the size for Buffer */
609 		Buflen = ((UNS) (Plength / Dblspace + 1)) *
610 		    2 * (Linew + 1);
611 		/* Should allocate Buflen * sizeof (wchar_t) */
612 		Buffer = (wchar_t *)getspace(Buflen * sizeof (wchar_t));
613 		Bufptr = Bufend = &Buffer[Buflen];
614 		Colpts = (COLP) getspace((UNS) ((Ncols + 1) *
615 		    sizeof (*Colpts)));
616 		Colpts[0].c_lno = 0;
617 	}
618 
619 	/* is stdin not a tty? */
620 	if (Ttyout && (Pause || Formfeed) && !ttyname(fileno(stdin)))
621 		Ttyin = fopen("/dev/tty", "r");
622 
623 	return (eargc);
624 }
625 
626 
627 static	int
628 print(char *name)
629 {
630 	static	int	notfirst = 0;
631 	char	*date = NULL;
632 	char	*head = NULL;
633 	int	c;
634 
635 	if (Multi != 'm' && mustopen(name, &Files[0]) == NULL)
636 		return (0);
637 	if (Multi == 'm' && Nfiles == 0 && mustopen(name, &Files[0]) == NULL)
638 		die("cannot open stdin");
639 	if (Buffer)
640 		(void) ungetwc(Files->f_nextc, Files->f_f);
641 	if (Lnumb)
642 		Lnumb = 1;
643 	for (Page = 0; ; putpage()) {
644 		if (C == WEOF && !(fold && Buffer))
645 			break;
646 		if (Buffer)
647 			nexbuf();
648 		Inpos = 0;
649 		if (get(0) == WEOF)
650 			break;
651 		(void) fflush(stdout);
652 		if (++Page >= Fpage) {
653 			/* Pause if -p and not first page */
654 			if (Ttyout && Pause && !notfirst++) {
655 				PROMPT();	/* prompt with bell and pause */
656 				while ((c = getc(Ttyin)) != EOF && c != '\n')
657 					;
658 			}
659 			if (Margin == 0)
660 				continue;
661 			if (date == NULL)
662 				date = GETDATE();
663 			if (head == NULL)
664 				head = Head != NULL ? Head :
665 				    Nfiles < 2 ? Files->f_name : nulls;
666 			(void) printf("\n\n");
667 			Nspace = Offset;
668 			putspace();
669 			(void) printf(HEAD);
670 		}
671 	}
672 	C = '\0';
673 	return (1);
674 }
675 
676 
677 static	void
678 putpage()
679 {
680 	int	colno;
681 
682 	if (fold) {
683 		foldpage();
684 		return;
685 	}
686 	for (Line = Margin / 2; ; (void) get(0)) {
687 		for (Nspace = Offset, colno = 0, Outpos = 0; C != '\f'; ) {
688 			if (Lnumb && (C != WEOF) &&
689 			    (((colno == 0) && (Multi == 'm')) ||
690 			    (Multi != 'm'))) {
691 				if (Page >= Fpage) {
692 					putspace();
693 					(void) printf("%*ld%wc", Numw, Buffer ?
694 					    Colpts[colno].c_lno++ :
695 					    Lnumb, Nsepc);
696 
697 					/* Move Outpos for number field */
698 					Outpos += Numw;
699 					if (Nsepc == '\t')
700 						Outpos +=
701 						    DEFTAB - (Outpos % DEFTAB);
702 					else
703 						Outpos++;
704 				}
705 				++Lnumb;
706 			}
707 			for (Lcolpos = 0, Pcolpos = 0;
708 			    C != '\n' && C != '\f' && C != WEOF;
709 			    (void) get(colno))
710 				(void) put(C);
711 
712 			if ((C == WEOF) || (++colno == Ncols) ||
713 			    ((C == '\n') && (get(colno) == WEOF)))
714 				break;
715 
716 			if (Sepc)
717 				(void) put(Sepc);
718 			else if ((Nspace += Colw - Lcolpos + 1) < 1)
719 				Nspace = 1;
720 		}
721 
722 		if (C == WEOF) {
723 			if (Margin != 0)
724 				break;
725 			if (colno != 0)
726 				(void) put('\n');
727 			return;
728 		}
729 		if (C == '\f')
730 			break;
731 		(void) put('\n');
732 		if (Dblspace == 2 && Line < Plength)
733 			(void) put('\n');
734 		if (Line >= Plength)
735 			break;
736 	}
737 	if (Formfeed)
738 		(void) put('\f');
739 	else
740 		while (Line < Length)
741 			(void) put('\n');
742 }
743 
744 
745 static	void
746 foldpage()
747 {
748 	int	colno;
749 	int	keep;
750 	int	i;
751 	int	pLcolpos;
752 	static	int	sl;
753 
754 	for (Line = Margin / 2; ; (void) get(0)) {
755 		for (Nspace = Offset, colno = 0, Outpos = 0; C != '\f'; ) {
756 			if (Lnumb && Multi == 'm' && foldcol) {
757 				if (!Fcol[colno].skip) {
758 					unget(colno);
759 					putspace();
760 					if (!colno) {
761 						for (i = 0; i <= Numw; i++)
762 							(void) printf(" ");
763 						(void) printf("%wc", Nsepc);
764 					}
765 					for (i = 0; i <= Colw; i++)
766 						(void) printf(" ");
767 					(void) put(Sepc);
768 					if (++colno == Ncols)
769 						break;
770 					(void) get(colno);
771 					continue;
772 				} else if (!colno)
773 					Lnumb = sl;
774 			}
775 
776 			if (Lnumb && (C != WEOF) &&
777 			    ((colno == 0 && Multi == 'm') || (Multi != 'm'))) {
778 				if (Page >= Fpage) {
779 					putspace();
780 					if ((foldcol &&
781 					    Fcol[colno].skip && Multi != 'a') ||
782 					    (Fcol[0].fold && Multi == 'a') ||
783 					    (Buffer && Colpts[colno].c_skip)) {
784 						for (i = 0; i < Numw; i++)
785 							(void) printf(" ");
786 						(void) printf("%wc", Nsepc);
787 						if (Buffer) {
788 							Colpts[colno].c_lno++;
789 							Colpts[colno].c_skip =
790 							    0;
791 						}
792 					}
793 					else
794 					(void) printf("%*ld%wc", Numw, Buffer ?
795 					    Colpts[colno].c_lno++ :
796 					    Lnumb, Nsepc);
797 				}
798 				sl = Lnumb++;
799 			}
800 			pLcolpos = 0;
801 			for (Lcolpos = 0, Pcolpos = 0;
802 			    C != '\n' && C != '\f' && C != WEOF;
803 			    (void) get(colno)) {
804 				if (put(C)) {
805 					unget(colno);
806 					Fcol[(Multi == 'a') ? 0 : colno].fold
807 					    = 1;
808 					break;
809 				} else if (Multi == 'a') {
810 					Fcol[0].fold = 0;
811 				}
812 				pLcolpos = Lcolpos;
813 			}
814 			if (Buffer) {
815 				alleof = 1;
816 				for (i = 0; i < Ncols; i++)
817 					if (!Fcol[i].eof)
818 						alleof = 0;
819 				if (alleof || ++colno == Ncols)
820 					break;
821 			} else if (C == EOF || ++colno == Ncols)
822 				break;
823 			keep = C;
824 			(void) get(colno);
825 			if (keep == '\n' && C == WEOF)
826 				break;
827 			if (Sepc)
828 				(void) put(Sepc);
829 			else if ((Nspace += Colw - pLcolpos + 1) < 1)
830 				Nspace = 1;
831 		}
832 		foldcol = 0;
833 		if (Lnumb && Multi != 'a') {
834 			for (i = 0; i < Ncols; i++) {
835 				Fcol[i].skip = Fcol[i].fold;
836 				foldcol +=  Fcol[i].fold;
837 				Fcol[i].fold = 0;
838 			}
839 		}
840 		if (C == WEOF) {
841 			if (Margin != 0)
842 				break;
843 			if (colno != 0)
844 				(void) put('\n');
845 			return;
846 		}
847 		if (C == '\f')
848 			break;
849 		(void) put('\n');
850 		(void) fflush(stdout);
851 		if (Dblspace == 2 && Line < Plength)
852 			(void) put('\n');
853 		if (Line >= Plength)
854 			break;
855 	}
856 	if (Formfeed)
857 		(void) put('\f');
858 	else while (Line < Length)
859 		(void) put('\n');
860 }
861 
862 
863 static	void
864 nexbuf()
865 {
866 	wchar_t	*s = Buffer;
867 	COLP	p = Colpts;
868 	int	j;
869 	int	c;
870 	int	bline = 0;
871 	wchar_t	wc;
872 
873 	if (fold) {
874 		foldbuf();
875 		return;
876 	}
877 	for (; ; ) {
878 		p->c_ptr0 = p->c_ptr = s;
879 		if (p == &Colpts[Ncols])
880 			return;
881 		(p++)->c_lno = Lnumb + bline;
882 		for (j = (Length - Margin)/Dblspace; --j >= 0; ++bline) {
883 			for (Inpos = 0; ; ) {
884 				errno = 0;
885 				wc = _fgetwc_pr(Files->f_f, &c);
886 				if (wc == WEOF) {
887 					/* If there is an illegal character, */
888 					/* handle it as a byte sequence. */
889 					if (errno == EILSEQ) {
890 						if (Inpos < Colw - 1) {
891 							*s = c;
892 							if (++s >= Bufend)
893 die("page-buffer overflow");
894 						}
895 						Inpos++;
896 						Error++;
897 						return;
898 					} else {
899 						/* Real EOF */
900 for (*s = WEOF; p <= &Colpts[Ncols]; ++p)
901 	p->c_ptr0 = p->c_ptr = s;
902 						balance(bline);
903 						return;
904 					}
905 				}
906 
907 				if (isascii(wc)) {
908 					if (isprint(wc))
909 						Inpos++;
910 				} else if (iswprint(wc)) {
911 					Inpos += wcwidth(wc);
912 				}
913 
914 				if (Inpos <= Colw || wc == '\n') {
915 					*s = wc;
916 					if (++s >= Bufend)
917 						die("page-buffer overflow");
918 				}
919 				if (wc == '\n')
920 					break;
921 				switch (wc) {
922 				case '\b':
923 					if (Inpos == 0)
924 						--s;
925 
926 					/*FALLTHROUGH*/
927 
928 				case ESC:
929 					if (Inpos > 0)
930 						--Inpos;
931 				}
932 			}
933 		}
934 	}
935 }
936 
937 
938 static	void
939 foldbuf()
940 {
941 	int	num;
942 	int	i;
943 	int	colno = 0;
944 	int	size = Buflen;
945 	wchar_t	*s;
946 	wchar_t	*d;
947 	COLP	p = Colpts;
948 
949 	for (i = 0; i < Ncols; i++)
950 		Fcol[i].eof = 0;
951 	d = Buffer;
952 	if (Bufptr != Bufend) {
953 		s = Bufptr;
954 		while (s < Bufend)
955 			*d++ = *s++;
956 		size -= (Bufend - Bufptr);
957 	}
958 	Bufptr = Buffer;
959 	p->c_ptr0 = p->c_ptr = Buffer;
960 	if (p->c_lno == 0) {
961 		p->c_lno = Lnumb;
962 		p->c_skip = 0;
963 	} else {
964 		p->c_lno = Colpts[Ncols-1].c_lno;
965 		p->c_skip = Colpts[Ncols].c_skip;
966 		if (p->c_skip)
967 			p->c_lno--;
968 	}
969 	if ((num = freadw(d, size, Files->f_f)) != size) {
970 		for (*(d+num) = WEOF; (++p) <= &Colpts[Ncols]; ) {
971 			p->c_ptr0 = p->c_ptr = (d+num);
972 		}
973 		balance(0);
974 		return;
975 	}
976 	i = (Length - Margin) / Dblspace;
977 	do {
978 		(void) readbuf(&Bufptr, i, p++);
979 	} while (++colno < Ncols);
980 }
981 
982 
983 static	void
984 balance(int bline)	/* line balancing for last page */
985 {
986 	wchar_t	*s = Buffer;
987 	COLP	p = Colpts;
988 	int	colno = 0;
989 	int	j;
990 	int	c;
991 	int	l;
992 	int	lines;
993 
994 	if (!fold) {
995 		c = bline % Ncols;
996 		l = (bline + Ncols - 1)/Ncols;
997 		bline = 0;
998 		do {
999 			for (j = 0; j < l; ++j)
1000 				while (*s++ != '\n')
1001 					;
1002 			(++p)->c_lno = Lnumb + (bline += l);
1003 			p->c_ptr0 = p->c_ptr = s;
1004 			if (++colno == c)
1005 				--l;
1006 		} while (colno < Ncols - 1);
1007 	} else {
1008 		lines = readbuf(&s, 0, 0);
1009 		l = (lines + Ncols - 1)/Ncols;
1010 		if (l > ((Length - Margin) / Dblspace)) {
1011 			l = (Length - Margin) / Dblspace;
1012 			c = Ncols;
1013 		} else {
1014 			c = lines % Ncols;
1015 		}
1016 		s = Buffer;
1017 		do {
1018 			(void) readbuf(&s, l, p++);
1019 			if (++colno == c)
1020 				--l;
1021 		} while (colno < Ncols);
1022 		Bufptr = s;
1023 	}
1024 }
1025 
1026 
1027 static	int
1028 readbuf(wchar_t **s, int lincol, COLP p)
1029 {
1030 	int	lines = 0;
1031 	int	chars = 0;
1032 	int	width;
1033 	int	nls = 0;
1034 	int	move;
1035 	int	skip = 0;
1036 	int	decr = 0;
1037 
1038 	width = (Ncols == 1) ? Linew : Colw;
1039 	while (**s != WEOF) {
1040 		switch (**s) {
1041 			case '\n':
1042 				lines++; nls++; chars = 0; skip = 0;
1043 				break;
1044 
1045 			case '\b':
1046 			case ESC:
1047 				if (chars) chars--;
1048 				break;
1049 
1050 			case '\t':
1051 				move = Itabn - ((chars + Itabn) % Itabn);
1052 				move = (move < width-chars) ? move :
1053 				    width-chars;
1054 				chars += move;
1055 				/* FALLTHROUGH */
1056 
1057 			default:
1058 				if (isascii(**s)) {
1059 					if (isprint(**s))
1060 						chars++;
1061 				} else if (iswprint(**s)) {
1062 					chars += wcwidth(**s);
1063 				}
1064 		}
1065 		if (chars > width) {
1066 			lines++;
1067 			skip++;
1068 			decr++;
1069 			chars = 0;
1070 		}
1071 		if (lincol && lines == lincol) {
1072 			(p+1)->c_lno = p->c_lno + nls;
1073 			(++p)->c_skip = skip;
1074 			if (**s == '\n') (*s)++;
1075 			p->c_ptr0 = p->c_ptr = (wchar_t *)*s;
1076 			return (0);
1077 		}
1078 		if (decr)
1079 			decr = 0;
1080 		else
1081 			(*s)++;
1082 	}
1083 	return (lines);
1084 }
1085 
1086 
1087 static	wint_t
1088 get(int colno)
1089 {
1090 	static	int	peekc = 0;
1091 	COLP	p;
1092 	FILS	*q;
1093 	int	c;
1094 	wchar_t		wc, w;
1095 
1096 	if (peekc) {
1097 		peekc = 0;
1098 		wc = Etabc;
1099 	} else if (Buffer) {
1100 		p = &Colpts[colno];
1101 		if (p->c_ptr >= (p+1)->c_ptr0)
1102 			wc = WEOF;
1103 		else if ((wc = *p->c_ptr) != WEOF)
1104 			++p->c_ptr;
1105 		if (fold && wc == WEOF)
1106 			Fcol[colno].eof = 1;
1107 	} else if ((wc =
1108 		(q = &Files[Multi == 'a' ? 0 : colno])->f_nextc) == WEOF) {
1109 		for (q = &Files[Nfiles]; --q >= Files && q->f_nextc == WEOF; )
1110 			;
1111 		if (q >= Files)
1112 			wc = '\n';
1113 	} else {
1114 		errno = 0;
1115 		w = _fgetwc_pr(q->f_f, &c);
1116 		if (w == WEOF && errno == EILSEQ) {
1117 			q->f_nextc = (wchar_t)c;
1118 		} else {
1119 			q->f_nextc = w;
1120 		}
1121 	}
1122 
1123 	if (Etabn != 0 && wc == Etabc) {
1124 		++Inpos;
1125 		peekc = ETABS;
1126 		wc = ' ';
1127 		return (C = wc);
1128 	}
1129 
1130 	if (wc == WEOF)
1131 		return (C = wc);
1132 
1133 	if (isascii(wc)) {
1134 		if (isprint(wc)) {
1135 			Inpos++;
1136 			return (C = wc);
1137 		}
1138 	} else if (iswprint(wc)) {
1139 		Inpos += wcwidth(wc);
1140 		return (C = wc);
1141 	}
1142 
1143 	switch (wc) {
1144 	case '\b':
1145 	case ESC:
1146 		if (Inpos > 0)
1147 			--Inpos;
1148 		break;
1149 	case '\f':
1150 		if (Ncols == 1)
1151 			break;
1152 		wc = '\n';
1153 		/* FALLTHROUGH */
1154 	case '\n':
1155 	case '\r':
1156 		Inpos = 0;
1157 		break;
1158 	}
1159 	return (C = wc);
1160 }
1161 
1162 
1163 static	int
1164 put(wchar_t wc)
1165 {
1166 	int	move = 0;
1167 	int	width = Colw;
1168 	int	sp = Lcolpos;
1169 
1170 	if (fold && Ncols == 1)
1171 		width = Linew;
1172 
1173 	switch (wc) {
1174 	case ' ':
1175 		/* If column not full or this is separator char */
1176 		if ((!fold && Ncols < 2) || (Lcolpos < width) ||
1177 		    ((Sepc == wc) && (Lcolpos == width))) {
1178 			++Nspace;
1179 			++Lcolpos;
1180 		}
1181 		if (fold && sp == Lcolpos)
1182 			if (Lcolpos >= width)
1183 				return (1);
1184 
1185 		return (0);
1186 
1187 	case '\t':
1188 		if (Itabn == 0)
1189 			break;
1190 
1191 		/* If column not full or this is separator char */
1192 		if ((Lcolpos < width) ||
1193 		    ((Sepc == wc) && (Lcolpos == width))) {
1194 			move = Itabn - ((Lcolpos + Itabn) % Itabn);
1195 			move = (move < width-Lcolpos) ? move : width-Lcolpos;
1196 			Nspace += move;
1197 			Lcolpos += move;
1198 		}
1199 		if (fold && sp == Lcolpos)
1200 			if (Lcolpos >= width)
1201 				return (1);
1202 		return (0);
1203 
1204 	case '\b':
1205 		if (Lcolpos == 0)
1206 			return (0);
1207 		if (Nspace > 0) {
1208 			--Nspace;
1209 			--Lcolpos;
1210 			return (0);
1211 		}
1212 		if (Lcolpos > Pcolpos) {
1213 			--Lcolpos;
1214 			return (0);
1215 		}
1216 
1217 		/*FALLTHROUGH*/
1218 
1219 	case ESC:
1220 		move = -1;
1221 		break;
1222 
1223 	case '\n':
1224 		++Line;
1225 
1226 		/*FALLTHROUGH*/
1227 
1228 	case '\r':
1229 	case '\f':
1230 		Pcolpos = 0;
1231 		Lcolpos = 0;
1232 		Nspace = 0;
1233 		Outpos = 0;
1234 		/* FALLTHROUGH */
1235 	default:
1236 		if (isascii(wc)) {
1237 			if (isprint(wc))
1238 				move = 1;
1239 			else
1240 				move = 0;
1241 		} else if (iswprint(wc)) {
1242 			move = wcwidth(wc);
1243 		} else {
1244 			move = 0;
1245 		}
1246 		break;
1247 	}
1248 	if (Page < Fpage)
1249 		return (0);
1250 	if (Lcolpos > 0 || move > 0)
1251 		Lcolpos += move;
1252 
1253 	putspace();
1254 
1255 	/* If column not full or this is separator char */
1256 	if ((!fold && Ncols < 2) || (Lcolpos <= width) ||
1257 	    ((Sepc == wc) && (Lcolpos > width))) {
1258 		(void) fputwc(wc, stdout);
1259 		Outpos += move;
1260 		Pcolpos = Lcolpos;
1261 	}
1262 
1263 	if (fold && Lcolpos > width)
1264 		return (1);
1265 
1266 	return (0);
1267 }
1268 
1269 
1270 static	void
1271 putspace(void)
1272 {
1273 	int nc = 0;
1274 
1275 	for (; Nspace > 0; Outpos += nc, Nspace -= nc) {
1276 #ifdef XPG4
1277 		/* XPG4:  -i:  replace multiple SPACE chars with tab chars */
1278 		if ((Nspace >= 2 && Itabn > 0 &&
1279 			Nspace >= (nc = Itabn - Outpos % Itabn)) && !fold) {
1280 #else
1281 		/* Solaris:  -i:  replace white space with tab chars */
1282 		if ((Itabn > 0 && Nspace >= (nc = Itabn - Outpos % Itabn)) &&
1283 			!fold) {
1284 #endif
1285 			(void) fputwc(Itabc, stdout);
1286 		} else {
1287 			nc = 1;
1288 			(void) putchar(' ');
1289 		}
1290 	}
1291 }
1292 
1293 
1294 static	void
1295 unget(int colno)
1296 {
1297 	if (Buffer) {
1298 		if (*(Colpts[colno].c_ptr-1) != '\t')
1299 			--(Colpts[colno].c_ptr);
1300 		if (Colpts[colno].c_lno)
1301 			Colpts[colno].c_lno--;
1302 	} else {
1303 		if ((Multi == 'm' && colno == 0) || Multi != 'm')
1304 			if (Lnumb && !foldcol)
1305 				Lnumb--;
1306 		colno = (Multi == 'a') ? 0 : colno;
1307 		(void) ungetwc(Files[colno].f_nextc, Files[colno].f_f);
1308 		Files[colno].f_nextc = C;
1309 	}
1310 }
1311 
1312 
1313 /*
1314  * Defer message about failure to open file to prevent messing up
1315  * alignment of page with tear perforations or form markers.
1316  * Treat empty file as special case and report as diagnostic.
1317  */
1318 
1319 static	FILE *
1320 mustopen(char *s, FILS *f)
1321 {
1322 	char	*empty_file_msg = gettext("%s -- empty file");
1323 	int	c;
1324 
1325 	if (*s == '\0') {
1326 		f->f_name = STDINNAME();
1327 		f->f_f = stdin;
1328 	} else if ((f->f_f = fopen(f->f_name = s, "r")) == NULL) {
1329 		s = ffiler(f->f_name);
1330 		s = strcpy((char *)getspace((UNS) strlen(s) + 1), s);
1331 	}
1332 	if (f->f_f != NULL) {
1333 		errno = 0;
1334 		f->f_nextc = _fgetwc_pr(f->f_f, &c);
1335 		if (f->f_nextc != WEOF) {
1336 			return (f->f_f);
1337 		} else {	/* WEOF */
1338 			if (errno == EILSEQ) {
1339 				f->f_nextc = (wchar_t)c;
1340 				return (f->f_f);
1341 			}
1342 			if (Multi == 'm')
1343 				return (f->f_f);
1344 		}
1345 		(void) sprintf(s = (char *)getspace((UNS) strlen(f->f_name)
1346 		    + 1 + (UNS) strlen(empty_file_msg)),
1347 		    empty_file_msg, f->f_name);
1348 		(void) fclose(f->f_f);
1349 	}
1350 	Error = 1;
1351 	if (Report)
1352 		if (Ttyout) {	/* accumulate error reports */
1353 			Lasterr = Lasterr->e_nextp =
1354 			    (ERR *) getspace((UNS) sizeof (ERR));
1355 			Lasterr->e_nextp = NULL;
1356 			Lasterr->e_mess = s;
1357 		} else {	/* ok to print error report now */
1358 			cerror(s);
1359 			(void) putc('\n', stderr);
1360 		}
1361 	return ((FILE *)NULL);
1362 }
1363 
1364 
1365 static	ANY *
1366 getspace(UNS n)
1367 {
1368 	ANY *t;
1369 
1370 	if ((t = (ANY *) malloc(n)) == NULL)
1371 		die("out of space");
1372 	return (t);
1373 }
1374 
1375 
1376 static	void
1377 die(char *s)
1378 {
1379 	++Error;
1380 	errprint();
1381 	cerror(s);
1382 	(void) putc('\n', stderr);
1383 	exit(1);
1384 
1385 	/*NOTREACHED*/
1386 }
1387 
1388 
1389 static	void
1390 errprint()	/* print accumulated error reports */
1391 {
1392 	(void) fflush(stdout);
1393 	for (; Err != NULL; Err = Err->e_nextp) {
1394 		cerror(Err->e_mess);
1395 		(void) putc('\n', stderr);
1396 	}
1397 	done();
1398 }
1399 
1400 
1401 static	void
1402 fixtty()
1403 {
1404 	struct stat sbuf;
1405 
1406 	setbuf(stdout, obuf);
1407 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1408 		(void) signal(SIGINT, onintr);
1409 	if (Ttyout = ttyname(fileno(stdout))) {	/* is stdout a tty? */
1410 		(void) stat(Ttyout, &sbuf);
1411 		Mode = sbuf.st_mode;		/* save permissions */
1412 		(void) chmod(Ttyout, (S_IREAD|S_IWRITE));
1413 	}
1414 }
1415 
1416 
1417 static	void
1418 onintr()
1419 {
1420 	++Error;
1421 	errprint();
1422 	_exit(1);
1423 }
1424 
1425 
1426 static	char *
1427 GETDATE()	/* return date file was last modified */
1428 {
1429 	static	char	*now = NULL;
1430 	static	struct	stat	sbuf;
1431 	static	struct	stat	nbuf;
1432 
1433 	if (Nfiles > 1 || Files->f_name == nulls) {
1434 		if (now == NULL) {
1435 			(void) time(&nbuf.st_mtime);
1436 			(void) cftime(time_buf,
1437 				dcgettext(NULL, FORMAT, LC_TIME),
1438 			    &nbuf.st_mtime);
1439 			now = time_buf;
1440 		}
1441 		return (now);
1442 	} else {
1443 		(void) stat(Files->f_name, &sbuf);
1444 		(void) cftime(time_buf, dcgettext(NULL, FORMAT, LC_TIME),
1445 			&sbuf.st_mtime);
1446 		return (time_buf);
1447 	}
1448 }
1449 
1450 
1451 static	char *
1452 ffiler(char *s)
1453 {
1454 	static char buf[100];
1455 
1456 	(void) sprintf(buf, gettext("can't open %s"), s);
1457 	return (buf);
1458 }
1459 
1460 
1461 static	void
1462 usage(int rc)
1463 {
1464 	(void) fprintf(stderr, gettext(
1465 "usage: pr [-# [-w #] [-a]] [-e[c][#]] [-i[c][#]] [-drtfp] [-n[c][#]]  \\\n"
1466 "          [-o #] [-l #] [-s[char]] [-h header] [-F] [+#] [file ...]\n\n"
1467 "       pr [-m [-w #]] [-e[c][#]] [-i[c][#]] [-drtfp] [-n[c][#]] [-0 #] \\\n"
1468 "          [-l #] [-s[char]] [-h header] [-F] [+#] file1 file2 ...\n"
1469 ));
1470 	exit(rc);
1471 }
1472 
1473 static wint_t
1474 _fgetwc_pr(FILE *f, int *ic)
1475 {
1476 	int	i;
1477 	int	len;
1478 	char	mbuf[MB_LEN_MAX];
1479 	int	c;
1480 	wchar_t	wc;
1481 
1482 	c = getc(f);
1483 
1484 	if (c == EOF)
1485 		return (WEOF);
1486 	if (mbcurmax == 1 || isascii(c)) {
1487 		return ((wint_t)c);
1488 	}
1489 	mbuf[0] = (char)c;
1490 	for (i = 1; i < mbcurmax; i++) {
1491 		c = getc(f);
1492 		if (c == EOF) {
1493 			break;
1494 		} else {
1495 			mbuf[i] = (char)c;
1496 		}
1497 	}
1498 	mbuf[i] = 0;
1499 
1500 	len = mbtowc(&wc, mbuf, i);
1501 	if (len == -1) {
1502 		/* Illegal character */
1503 		/* Set the first byte to *ic */
1504 		*ic = mbuf[0];
1505 		/* Push back remaining characters */
1506 		for (i--; i > 0; i--) {
1507 			(void) ungetc(mbuf[i], f);
1508 		}
1509 		errno = EILSEQ;
1510 		return (WEOF);
1511 	} else {
1512 		/* Push back over-read characters */
1513 		for (i--; i >= len; i--) {
1514 			(void) ungetc(mbuf[i], f);
1515 		}
1516 		return ((wint_t)wc);
1517 	}
1518 }
1519 
1520 static size_t
1521 freadw(wchar_t *ptr, size_t nitems, FILE *f)
1522 {
1523 	size_t	i;
1524 	size_t	ret;
1525 	int	c;
1526 	wchar_t	*p;
1527 	wint_t	wc;
1528 
1529 	if (feof(f)) {
1530 		return (0);
1531 	}
1532 
1533 	p = ptr;
1534 	ret = 0;
1535 	for (i = 0; i < nitems; i++) {
1536 		errno = 0;
1537 		wc = _fgetwc_pr(f, &c);
1538 		if (wc == WEOF) {
1539 			if (errno == EILSEQ) {
1540 				*p++ = (wchar_t)c;
1541 				ret++;
1542 			} else {
1543 				return (ret);
1544 			}
1545 		} else {
1546 			*p++ = (wchar_t)wc;
1547 			ret++;
1548 		}
1549 	}
1550 	return (ret);
1551 }
1552