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  * A copy of the CDDL is also available via the Internet at
11  * http://www.opensource.org/licenses/cddl1.txt
12  * See the License for the specific language governing permissions
13  * and limitations under the License.
14  *
15  * When distributing Covered Code, include this CDDL HEADER in each
16  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17  * If applicable, add the following below this CDDL HEADER, with the
18  * fields enclosed by brackets "[]" replaced with your own identifying
19  * information: Portions Copyright [yyyy] [name of copyright owner]
20  *
21  * CDDL HEADER END
22  */
23 
24 /*
25  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
26  * Use is subject to license terms.
27  */
28 
29 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
30 /*	  All Rights Reserved  	*/
31 
32 #if defined(sun)
33 #pragma ident	"@(#)print.c	1.18	06/06/16 SMI"
34 #endif
35 
36 /*
37  * Copyright 2008-2020 J. Schilling
38  *
39  * @(#)print.c	1.47 21/02/24 2008-2020 J. Schilling
40  */
41 #ifdef	SCHILY_INCLUDES
42 #include <schily/mconfig.h>
43 #endif
44 #ifndef lint
45 static	UConst char sccsid[] =
46 	"@(#)print.c	1.47 21/02/24 2008-2020 J. Schilling";
47 #endif
48 
49 /*
50  * UNIX shell
51  *
52  */
53 
54 #ifdef	SCHILY_INCLUDES
55 #include	<schily/mconfig.h>
56 #include	<stdio.h>
57 #undef	feof
58 #include	"defs.h"
59 #include	<schily/param.h>
60 #include	<schily/wchar.h>
61 #include	<schily/wctype.h>
62 #else
63 #include	"defs.h"
64 #include	<sys/param.h>
65 #include	<locale.h>
66 #include	<wctype.h>	/* iswprint() */
67 #endif
68 
69 #define		BUFLEN		256
70 
71 unsigned char numbuf[NUMBUFLEN+1];	/* Add one for sign */
72 
73 static unsigned char buffer[BUFLEN];
74 static unsigned char *bufp = buffer;
75 static int bindex = 0;
76 static int buffd = 1;
77 
78 	void	prp	__PR((void));
79 	void	prs	__PR((unsigned char *as));
80 	void	prc	__PR((unsigned char c));
81 	void	prwc	__PR((wchar_t c));
82 	void	prt	__PR((long t));
83 	void	prtv	__PR((struct timeval *tp, int digs, int lf));
84 	void	prn	__PR((int n));
85 static	void	_itos	__PR((unsigned int n, char *out, size_t	outlen));
86 	void	itos	__PR((int n));
87 	void	sitos	__PR((int n));
88 	int	stoi	__PR((unsigned char *icp));
89 	int	ltos	__PR((long n));
90 
91 static int ulltos	__PR((UIntmax_t n));
92 	void flushb	__PR((void));
93 	void unprs_buff	__PR((int));
94 	void prc_buff	__PR((unsigned char c));
95 	int  prs_buff	__PR((unsigned char *s));
96 static unsigned char *octal __PR((unsigned char c, unsigned char *ptr));
97 	void prs_cntl	__PR((unsigned char *s));
98 	void prull_buff	__PR((UIntmax_t lc));
99 	void prn_buff	__PR((int n));
100 	int setb	__PR((int fd));
101 
102 
103 /*
104  * printing and io conversion
105  */
106 void
prp()107 prp()
108 {
109 	if ((flags & prompt) == 0 && cmdadr) {
110 		prs_cntl(cmdadr);
111 		prs((unsigned char *)colon);
112 	}
113 }
114 
115 void
prs(as)116 prs(as)
117 	unsigned char	*as;
118 {
119 	if (as) {
120 		write(output, as, length(as) - 1);
121 	}
122 }
123 
124 #ifdef	PROTOTYPES
125 void
prc(unsigned char c)126 prc(unsigned char c)
127 #else
128 void
129 prc(c)
130 	unsigned char	c;
131 #endif
132 {
133 	if (c) {
134 		write(output, &c, 1);
135 	}
136 }
137 
138 #ifdef	__needed__	/* Was used by readwc() */
139 #ifdef	PROTOTYPES
140 void
prwc(wchar_t c)141 prwc(wchar_t c)
142 #else
143 void
144 prwc(c)
145 	wchar_t	c;
146 #endif
147 {
148 	char	mb[MB_LEN_MAX + 1];
149 	int	len;
150 
151 	if (c == 0) {
152 		return;
153 	}
154 	if ((len = wctomb(mb, c)) < 0) {
155 		mb[0] = (unsigned char)c;
156 		len = 1;
157 	}
158 	write(output, mb, len);
159 }
160 #endif
161 
162 #ifndef	HZ
163 #define	HZ	sysconf(_SC_CLK_TCK)
164 #endif
165 
166 void
clock2tv(t,tp)167 clock2tv(t, tp)
168 	clock_t		t;
169 	struct timeval	*tp;
170 {
171 	int _hz = HZ;	/* HZ may be a macro to a sysconf() call */
172 
173 	tp->tv_sec = t / _hz;
174 	tp->tv_usec = t % _hz;
175 	if (_hz <= 1000000)
176 		tp->tv_usec *= 1000000 / _hz;
177 	else
178 		tp->tv_usec /= _hz / 1000000;
179 }
180 
181 void
prt(t)182 prt(t)
183 	long	t;	/* t is time in clock ticks, not seconds */
184 {
185 	struct timeval	tv;
186 
187 	clock2tv(t, &tv);
188 	prtv(&tv, 3, TRUE);
189 }
190 
191 static int	divs[7] = { 1000000, 100000, 10000, 1000, 100, 10, 1 };
192 
193 void
prtv(tp,digs,lf)194 prtv(tp, digs, lf)
195 	struct timeval	*tp;
196 	int		digs;
197 	int		lf;	/* Long format */
198 {
199 	int s, hr, min, sec, frac;
200 
201 	if (digs < 0)
202 		digs = 3;
203 	if (digs > 6)
204 		digs = 6;
205 	frac = tp->tv_usec / divs[digs];
206 	s = tp->tv_sec;
207 	if (lf) {
208 		sec = s % 60;	/* Pure seconds		*/
209 		s /= 60;	/* s now holds minutes	*/
210 		min = s % 60;	/* Pure minutes		*/
211 		if (lf == 'l')
212 			min = s;
213 		hr = 0;
214 
215 		if ((lf != 'l') && (hr = s / 60) != 0) {
216 			prn_buff(hr);
217 			prc_buff(lf == ':' ? ':':'h');
218 		}
219 		if (lf == 'l' || hr > 0 || min > 0) {
220 			if (lf == ':' && min < 10 && hr > 0)
221 				prc_buff('0');
222 			prn_buff(min);
223 			prc_buff(lf == ':' ? ':':'m');
224 		}
225 	} else {
226 		sec = s;
227 	}
228 	if (lf == ':' && sec < 10 && tp->tv_sec >= 60)
229 		prc_buff('0');
230 	prn_buff(sec);
231 	if (digs > 0) {
232 #if defined(HAVE_LOCALECONV) && defined(USE_LOCALE)
233 		prc_buff(*(localeconv()->decimal_point));
234 #else
235 		prc_buff('.');
236 #endif
237 		itos(frac+1000000);
238 		prs_buff(numbuf+7-digs);
239 	}
240 	if (lf != FALSE && lf != ':')
241 		prc_buff('s');
242 }
243 
244 void
prn(n)245 prn(n)
246 	int	n;
247 {
248 	itos(n);
249 
250 	prs(numbuf);
251 }
252 
253 /*
254  * Convert unsigned int into "out" buffer.
255  */
256 static void
_itos(n,out,outlen)257 _itos(n, out, outlen)
258 	unsigned int	n;
259 	char	*out;
260 	size_t	outlen;
261 {
262 	unsigned char buf[NUMBUFLEN];
263 	unsigned char *abuf = &buf[NUMBUFLEN-1];
264 	unsigned int d;
265 
266 	*--abuf = (unsigned char)'\0';
267 
268 	do {
269 		 *--abuf = (unsigned char)('0' + n - 10 * (d = n / 10));
270 	} while ((n = d) != 0);
271 
272 	strncpy(out, (char *)abuf, outlen);
273 }
274 
275 /*
276  * Convert int into numbuf as if it was an unsigned.
277  */
278 void
itos(n)279 itos(n)
280 	int	n;
281 {
282 	_itos(n, (char *)numbuf, sizeof (numbuf));
283 }
284 
285 /*
286  * Convert signed int into numbuf.
287  */
288 void
sitos(n)289 sitos(n)
290 	int	n;
291 {
292 	char *np = (char *)numbuf;
293 
294 	if (n < 0) {
295 		*np++ = '-';
296 		_itos(-n, np, sizeof (numbuf) -1);
297 		return;
298 	}
299 	_itos(n, (char *)numbuf, sizeof (numbuf));
300 }
301 
302 int
stoi(icp)303 stoi(icp)
304 	unsigned char	*icp;
305 {
306 	unsigned char	*cp = icp;
307 	int	r = 0;
308 	unsigned char	c;
309 
310 	while ((c = *cp, digit(c)) && c && r >= 0) {
311 		r = r * 10 + c - '0';
312 		cp++;
313 	}
314 #ifdef	DO_STOI_PICKY
315 	if (r < 0 || cp == icp || *cp != '\0') {
316 #else
317 	if (r < 0 || cp == icp) {
318 #endif
319 		failed(icp, badnum);
320 		/* NOTREACHED */
321 	} else {
322 		return (r);
323 	}
324 
325 	return (-1);		/* Not reached, but keeps GCC happy */
326 }
327 
328 int
stosi(icp)329 stosi(icp)
330 	unsigned char	*icp;
331 {
332 	int	sign = 1;
333 
334 	if (*icp == '-') {
335 		sign = -1;
336 		icp++;
337 	}
338 	return (sign * stoi(icp));
339 }
340 
341 /*
342  * Convert signed long
343  */
344 int
sltos(n)345 sltos(n)
346 	long	n;
347 {
348 	if (n < 0) {
349 		int i;
350 
351 		i = ltos(-n);
352 		numbuf[--i] = '-';
353 		return (i);
354 	}
355 	return (ltos(n));
356 }
357 
358 #ifdef	DO_DOL_PAREN
359 /*
360  * Convert signed long long
361  */
362 int
slltos(n)363 slltos(n)
364 	Intmax_t	n;
365 {
366 	if (n < 0) {
367 		int i;
368 
369 		i = ulltos(-n);
370 		numbuf[--i] = '-';
371 		return (i);
372 	}
373 	return (ulltos(n));
374 }
375 #endif
376 
377 int
ltos(n)378 ltos(n)
379 	long	n;
380 {
381 	int i;
382 
383 	numbuf[NUMBUFLEN-1] = '\0';
384 	for (i = NUMBUFLEN-2; i >= 0; i--) {
385 		numbuf[i] = n % 10 + '0';
386 		if ((n /= 10) == 0) {
387 			break;
388 		}
389 	}
390 	return (i);
391 }
392 
393 static int
ulltos(n)394 ulltos(n)
395 	UIntmax_t	n;
396 {
397 	int i;
398 
399 	/* The max unsigned long long is 20 characters (+1 for '\0') */
400 	numbuf[NUMBUFLEN-1] = '\0';
401 	for (i = NUMBUFLEN-2; i >= 0; i--) {
402 		numbuf[i] = n % 10 + '0';
403 		if ((n /= 10) == 0) {
404 			break;
405 		}
406 	}
407 	return (i);
408 }
409 
410 void
flushb()411 flushb()
412 {
413 	if (bindex) {
414 		bufp[bindex] = '\0';
415 		write(buffd, bufp, length(bufp) - 1);
416 		bindex = 0;
417 	}
418 }
419 
420 void
unprs_buff(amt)421 unprs_buff(amt)
422 	int	amt;
423 {
424 	if (!bindex)
425 		return;
426 	bindex -= amt;
427 	if (bindex < 0)
428 		bindex = 0;
429 }
430 
431 #ifdef	PROTOTYPES
432 void
prc_buff(unsigned char c)433 prc_buff(unsigned char c)
434 #else
435 void
436 prc_buff(c)
437 	unsigned char	c;
438 #endif
439 {
440 	if (c) {
441 		if (buffd == -1) {
442 			if (bufp+bindex+1 >= brkend) {
443 				bufp = growstak(bufp+bindex+1);
444 				bufp -= bindex + 1;
445 			}
446 		} else if (bindex + 1 >= BUFLEN) {
447 			flushb();
448 		}
449 
450 		bufp[bindex++] = c;
451 	} else {
452 		flushb();
453 		write(buffd, &c, 1);
454 	}
455 }
456 
457 int
prs_buff(s)458 prs_buff(s)
459 	unsigned char	*s;
460 {
461 	int len = length(s) - 1;
462 
463 	if (buffd == -1) {
464 		if (bufp+bindex+len >= brkend) {
465 			bufp = growstak(bufp+bindex+len);
466 			bufp -= bindex + len;
467 		}
468 	} else if (bindex + len >= BUFLEN) {
469 		flushb();
470 	}
471 
472 	if (buffd != -1 && len >= BUFLEN) {
473 		write(buffd, s, len);
474 		return (0);
475 	} else {
476 		movstr(s, &bufp[bindex]);
477 		bindex += len;
478 		return (len);
479 	}
480 }
481 
482 #ifdef	PROTOTYPES
483 static unsigned char *
octal(unsigned char c,unsigned char * ptr)484 octal(unsigned char c, unsigned char *ptr)
485 #else
486 static unsigned char *
487 octal(c, ptr)
488 	unsigned char	c;
489 	unsigned char	*ptr;
490 #endif
491 {
492 	*ptr++ = '\\';
493 	*ptr++ = ((unsigned int)c >> 6) + '0';
494 	*ptr++ = (((unsigned int)c >> 3) & 07) + '0';
495 	*ptr++ = (c & 07) + '0';
496 	return (ptr);
497 }
498 
499 void
prs_cntl(s)500 prs_cntl(s)
501 	unsigned char	*s;
502 {
503 	/*
504 	 * Add redzone of MB_LEN_MAX * 4 bytes for octal number + nul
505 	 */
506 	unsigned char cbuf[BUFLEN+(4*MB_LEN_MAX)+1];
507 	int n;
508 	wchar_t wc;
509 	unsigned char *olds = s;
510 	unsigned char *ptr = cbuf;
511 	wchar_t c;
512 	BOOL	err = FALSE;
513 
514 	(void) mbtowc(NULL, NULL, 0);
515 	if ((n = mbtowc(&wc, (const char *)s, MB_LEN_MAX)) < 0) {
516 		(void) mbtowc(NULL, NULL, 0);
517 		n = 1;
518 		wc = *s;
519 		err = TRUE;
520 	}
521 	if (wc == 0)
522 		n = 0;
523 	while (n != 0) {
524 		if (err) {
525 			ptr = octal(*s++, ptr);
526 			err = FALSE;
527 		} else {
528 			c = wc;
529 			s += n;
530 			if (!iswprint(c)) {
531 				if (c < '\040' && c > 0) {
532 					/*
533 					 * assumes ASCII char
534 					 * translate a control character
535 					 * into a printable sequence
536 					 */
537 					*ptr++ = '^';
538 					*ptr++ = (c + 0100);
539 				} else if (c == 0177) {
540 					/* '\0177' does not work */
541 					*ptr++ = '^';
542 					*ptr++ = '?';
543 				} else {
544 					/*
545 					 * unprintable 8-bit byte sequence
546 					 * assumes all legal multibyte
547 					 * sequences are
548 					 * printable
549 					 */
550 					while (n--) {
551 						ptr = octal(*olds++, ptr);
552 					}
553 				}
554 			} else {
555 				while (n--) {
556 					*ptr++ = *olds++;
557 				}
558 			}
559 		}
560 		if (ptr >= &cbuf[BUFLEN]) {
561 			*ptr = '\0';
562 			prs(cbuf);
563 			ptr = cbuf;
564 		}
565 		olds = s;
566 		if ((n = mbtowc(&wc, (const char *)s, MB_LEN_MAX)) < 0) {
567 			(void) mbtowc(NULL, NULL, 0);
568 			n = 1;
569 			wc = *s;
570 			err = TRUE;
571 		}
572 		if (wc == 0)
573 			n = 0;
574 	}
575 	*ptr = '\0';
576 	prs(cbuf);
577 }
578 
579 #ifdef	DO_POSIX_UNSET
580 /*
581  * Quoted string printing as needed for the env(1) builtin when printing
582  * the list of shell variables in order to be POSIX compliant.
583  */
584 void
qprs_buff(s)585 qprs_buff(s)
586 	unsigned char	*s;
587 {
588 	int		n;
589 	char		c;
590 	wchar_t		wc = 0;
591 	int		isq = *s == '\0';
592 	unsigned char	*os = s;
593 
594 	(void) mbtowc(NULL, NULL, 0);
595 	for (;;) {
596 		if ((n = mbtowc(&wc, (const char *)s, MB_LEN_MAX)) < 0) {
597 			(void) mbtowc(NULL, NULL, 0);
598 			n = 1;
599 			wc = *s;
600 		}
601 		if (wc == 0)
602 			break;
603 		if (wc == '\'')
604 			break;
605 		if (!isq) {
606 			isq = escmeta(wc);
607 			if (!isq)
608 				isq =   wc == '*' || wc == '?' ||
609 					wc == '[' || wc == '~';
610 		}
611 		s += n;
612 	}
613 	if (wc != '\'') {
614 		if (isq)
615 			prc_buff('\'');
616 		prs_buff(os);
617 		if (isq)
618 			prc_buff('\'');
619 		return;
620 	}
621 	/*
622 	 * String contains at least one single quote:
623 	 */
624 	s = os;
625 	prc_buff('\'');
626 	while (*s) {
627 		if ((n = mbtowc(&wc, (const char *)s, MB_LEN_MAX)) < 0) {
628 			(void) mbtowc(NULL, NULL, 0);
629 			n = 1;
630 			wc = *s;
631 		}
632 		if (wc == 0)
633 			break;
634 		if (wc == '\'') {
635 			c = *s;
636 			*s = '\0';
637 			prs_buff(os);
638 			prs_buff(UC "'\\''");
639 			*s = c;
640 			os = s += n;
641 		} else {
642 			s += n;
643 		}
644 	}
645 	if (*os)
646 		prs_buff(os);
647 	prc_buff('\'');
648 }
649 #endif
650 
651 void
prull_buff(lc)652 prull_buff(lc)
653 	UIntmax_t	lc;
654 {
655 	prs_buff(&numbuf[ulltos(lc)]);
656 }
657 
658 void
prl_buff(l)659 prl_buff(l)
660 	long	l;
661 {
662 	prs_buff(&numbuf[ltos(l)]);
663 }
664 
665 void
prn_buff(n)666 prn_buff(n)
667 	int	n;
668 {
669 	itos(n);
670 
671 	prs_buff(numbuf);
672 }
673 
674 static unsigned char *locbufp;
675 
676 int
setb(fd)677 setb(fd)
678 	int	fd;
679 {
680 	int ofd;
681 
682 	if ((ofd = buffd) == -1) {
683 		/*
684 		 * Previous buffer was a growing buffer,
685 		 * so make it semi-permanent and remember value.
686 		 */
687 		if (bufp+bindex+1 >= brkend) {
688 			bufp = growstak(bufp+bindex+1);
689 			bufp -= bindex + 1;
690 		}
691 		if (bufp[bindex-1]) {
692 			bufp[bindex++] = 0;
693 		}
694 		locbufp = endstak(bufp+bindex);
695 	} else {
696 		/*
697 		 * Previous buffer was static, so just flush it.
698 		 */
699 		locbufp = NULL;
700 		flushb();
701 	}
702 	if ((buffd = fd) == -1) {
703 		/*
704 		 * Get new growing buffer
705 		 */
706 		bufp = locstak();
707 	} else {
708 		bufp = buffer;
709 	}
710 	bindex = 0;
711 	return (ofd);
712 }
713 
714 /*
715  * Hack to get the address of the semi-permanent buffer after setb(-1).
716  */
717 unsigned char *
endb()718 endb()
719 {
720 	return (locbufp);
721 }
722