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