busybox/libbb/xfuncs.c
Sören Tempel 3d9c649158 ls: don't output any colors with TERM=dumb
The TERM variable is usually set to "dumb" to indicate that the terminal
does not support any ANSI escape sequences. Presently, ls does not honor
this variable and outputs colors anyhow which results in unreadable
output, unless the user explicitly disables colors using `ls
--color=never`. The rational behind this change is that ls should "just
work" by default, even on dumb terminals.

For this reason, this patch adds a check which additionally consults the
TERM variable before printing any colors. This is analogous to the
existing check for ensuring that standard output is a tty. As such,
colors can still be forced with `--color=force`, even if TERM is set to
dumb.

function                                             old     new   delta
is_TERM_dumb                                           -      40     +40
ls_main                                              579     598     +19
.rodata                                           103246  103251      +5
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 2/0 up/down: 64/0)               Total: 64 bytes

Signed-off-by: Sören Tempel <soeren+git@soeren-tempel.net>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2021-06-04 22:39:10 +02:00

426 lines
11 KiB
C

/* vi: set sw=4 ts=4: */
/*
* Utility routines.
*
* Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
* Copyright (C) 2006 Rob Landley
* Copyright (C) 2006 Denys Vlasenko
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
/* We need to have separate xfuncs.c and xfuncs_printf.c because
* with current linkers, even with section garbage collection,
* if *.o module references any of XXXprintf functions, you pull in
* entire printf machinery. Even if you do not use the function
* which uses XXXprintf.
*
* xfuncs.c contains functions (not necessarily xfuncs)
* which do not pull in printf, directly or indirectly.
* xfunc_printf.c contains those which do.
*
* TODO: move xmalloc() and xatonum() here.
*/
#include "libbb.h"
/* Turn on nonblocking I/O on a fd */
int FAST_FUNC ndelay_on(int fd)
{
int flags = fcntl(fd, F_GETFL);
if (flags & O_NONBLOCK)
return flags;
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
return flags;
}
int FAST_FUNC ndelay_off(int fd)
{
int flags = fcntl(fd, F_GETFL);
if (!(flags & O_NONBLOCK))
return flags;
fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
return flags;
}
void FAST_FUNC close_on_exec_on(int fd)
{
fcntl(fd, F_SETFD, FD_CLOEXEC);
}
char* FAST_FUNC strncpy_IFNAMSIZ(char *dst, const char *src)
{
#ifndef IFNAMSIZ
enum { IFNAMSIZ = 16 };
#endif
return strncpy(dst, src, IFNAMSIZ);
}
/* Convert unsigned integer to ascii, writing into supplied buffer.
* A truncated result contains the first few digits of the result ala strncpy.
* Returns a pointer past last generated digit, does _not_ store NUL.
*/
char* FAST_FUNC utoa_to_buf(unsigned n, char *buf, unsigned buflen)
{
unsigned i, out, res;
if (buflen) {
out = 0;
BUILD_BUG_ON(sizeof(n) != 4 && sizeof(n) != 8);
if (sizeof(n) == 4)
// 2^32-1 = 4294967295
i = 1000000000;
#if UINT_MAX > 0xffffffff /* prevents warning about "const too large" */
else
if (sizeof(n) == 8)
// 2^64-1 = 18446744073709551615
i = 10000000000000000000;
#endif
for (; i; i /= 10) {
res = n / i;
n = n % i;
if (res || out || i == 1) {
if (--buflen == 0)
break;
out++;
*buf++ = '0' + res;
}
}
}
return buf;
}
/* Convert signed integer to ascii, like utoa_to_buf() */
char* FAST_FUNC itoa_to_buf(int n, char *buf, unsigned buflen)
{
if (!buflen)
return buf;
if (n < 0) {
n = -n;
*buf++ = '-';
buflen--;
}
return utoa_to_buf((unsigned)n, buf, buflen);
}
// The following two functions use a static buffer, so calling either one a
// second time will overwrite previous results.
//
// The largest 32 bit integer is -2 billion plus NUL, or 1+10+1=12 bytes.
// It so happens that sizeof(int) * 3 is enough for 32+ bit ints.
// (sizeof(int) * 3 + 2 is correct for any width, even 8-bit)
static char local_buf[sizeof(int) * 3];
/* Convert unsigned integer to ascii using a static buffer (returned). */
char* FAST_FUNC utoa(unsigned n)
{
*(utoa_to_buf(n, local_buf, sizeof(local_buf) - 1)) = '\0';
return local_buf;
}
/* Convert signed integer to ascii using a static buffer (returned). */
char* FAST_FUNC itoa(int n)
{
*(itoa_to_buf(n, local_buf, sizeof(local_buf) - 1)) = '\0';
return local_buf;
}
/* Emit a string of hex representation of bytes */
char* FAST_FUNC bin2hex(char *p, const char *cp, int count)
{
while (count) {
unsigned char c = *cp++;
/* put lowercase hex digits */
*p++ = 0x20 | bb_hexdigits_upcase[c >> 4];
*p++ = 0x20 | bb_hexdigits_upcase[c & 0xf];
count--;
}
return p;
}
/* Convert "[x]x[:][x]x[:][x]x[:][x]x" hex string to binary, no more than COUNT bytes */
char* FAST_FUNC hex2bin(char *dst, const char *str, int count)
{
errno = EINVAL;
while (*str && count) {
uint8_t val;
uint8_t c = *str++;
if (isdigit(c))
val = c - '0';
else if ((c|0x20) >= 'a' && (c|0x20) <= 'f')
val = (c|0x20) - ('a' - 10);
else
return NULL;
val <<= 4;
c = *str;
if (isdigit(c))
val |= c - '0';
else if ((c|0x20) >= 'a' && (c|0x20) <= 'f')
val |= (c|0x20) - ('a' - 10);
else if (c == ':' || c == '\0')
val >>= 4;
else
return NULL;
*dst++ = val;
if (c != '\0')
str++;
if (*str == ':')
str++;
count--;
}
errno = (*str ? ERANGE : 0);
return dst;
}
/* Return how long the file at fd is, if there's any way to determine it. */
#ifdef UNUSED
off_t FAST_FUNC fdlength(int fd)
{
off_t bottom = 0, top = 0, pos;
long size;
// If the ioctl works for this, return it.
if (ioctl(fd, BLKGETSIZE, &size) >= 0) return size*512;
// FIXME: explain why lseek(SEEK_END) is not used here!
// If not, do a binary search for the last location we can read. (Some
// block devices don't do BLKGETSIZE right.)
do {
char temp;
pos = bottom + (top - bottom) / 2;
// If we can read from the current location, it's bigger.
if (lseek(fd, pos, SEEK_SET)>=0 && safe_read(fd, &temp, 1)==1) {
if (bottom == top) bottom = top = (top+1) * 2;
else bottom = pos;
// If we can't, it's smaller.
} else {
if (bottom == top) {
if (!top) return 0;
bottom = top/2;
}
else top = pos;
}
} while (bottom + 1 != top);
return pos + 1;
}
#endif
int FAST_FUNC bb_putchar_stderr(char ch)
{
return write(STDERR_FILENO, &ch, 1);
}
ssize_t FAST_FUNC full_write1_str(const char *str)
{
return full_write(STDOUT_FILENO, str, strlen(str));
}
ssize_t FAST_FUNC full_write2_str(const char *str)
{
return full_write(STDERR_FILENO, str, strlen(str));
}
static int wh_helper(int value, int def_val, const char *env_name, int *err)
{
/* Envvars override even if "value" from ioctl is valid (>0).
* Rationale: it's impossible to guess what user wants.
* For example: "man CMD | ...": should "man" format output
* to stdout's width? stdin's width? /dev/tty's width? 80 chars?
* We _cant_ know it. If "..." saves text for e.g. email,
* then it's probably 80 chars.
* If "..." is, say, "grep -v DISCARD | $PAGER", then user
* would prefer his tty's width to be used!
*
* Since we don't know, at least allow user to do this:
* "COLUMNS=80 man CMD | ..."
*/
char *s = getenv(env_name);
if (s) {
value = atoi(s);
/* If LINES/COLUMNS are set, pretend that there is
* no error getting w/h, this prevents some ugly
* cursor tricks by our callers */
*err = 0;
}
if (value <= 1 || value >= 30000)
value = def_val;
return value;
}
/* It is perfectly ok to pass in a NULL for either width or for
* height, in which case that value will not be set. */
int FAST_FUNC get_terminal_width_height(int fd, unsigned *width, unsigned *height)
{
struct winsize win;
int err;
int close_me = -1;
if (fd == -1) {
if (isatty(STDOUT_FILENO))
fd = STDOUT_FILENO;
else
if (isatty(STDERR_FILENO))
fd = STDERR_FILENO;
else
if (isatty(STDIN_FILENO))
fd = STDIN_FILENO;
else
close_me = fd = open("/dev/tty", O_RDONLY);
}
win.ws_row = 0;
win.ws_col = 0;
/* I've seen ioctl returning 0, but row/col is (still?) 0.
* We treat that as an error too. */
err = ioctl(fd, TIOCGWINSZ, &win) != 0 || win.ws_row == 0;
if (height)
*height = wh_helper(win.ws_row, 24, "LINES", &err);
if (width)
*width = wh_helper(win.ws_col, 80, "COLUMNS", &err);
if (close_me >= 0)
close(close_me);
return err;
}
int FAST_FUNC get_terminal_width(int fd)
{
unsigned width;
get_terminal_width_height(fd, &width, NULL);
return width;
}
int FAST_FUNC is_dumb_term(void)
{
char *term = getenv("TERM");
return term && strcmp(term, "dumb") == 0;
}
int FAST_FUNC tcsetattr_stdin_TCSANOW(const struct termios *tp)
{
return tcsetattr(STDIN_FILENO, TCSANOW, tp);
}
int FAST_FUNC get_termios_and_make_raw(int fd, struct termios *newterm, struct termios *oldterm, int flags)
{
//TODO: slattach, shell read might be adapted to use this too: grep for "tcsetattr", "[VTIME] = 0"
int r;
memset(oldterm, 0, sizeof(*oldterm)); /* paranoia */
r = tcgetattr(fd, oldterm);
*newterm = *oldterm;
/* Turn off buffered input (ICANON)
* Turn off echoing (ECHO)
* and separate echoing of newline (ECHONL, normally off anyway)
*/
newterm->c_lflag &= ~(ICANON | ECHO | ECHONL);
if (flags & TERMIOS_CLEAR_ISIG) {
/* dont recognize INT/QUIT/SUSP chars */
newterm->c_lflag &= ~ISIG;
}
/* reads will block only if < 1 char is available */
newterm->c_cc[VMIN] = 1;
/* no timeout (reads block forever) */
newterm->c_cc[VTIME] = 0;
/* IXON, IXOFF, and IXANY:
* IXOFF=1: sw flow control is enabled on input queue:
* tty transmits a STOP char when input queue is close to full
* and transmits a START char when input queue is nearly empty.
* IXON=1: sw flow control is enabled on output queue:
* tty will stop sending if STOP char is received,
* and resume sending if START is received, or if any char
* is received and IXANY=1.
*/
if (flags & TERMIOS_RAW_CRNL_INPUT) {
/* IXON=0: XON/XOFF chars are treated as normal chars (why we do this?) */
/* dont convert CR to NL on input */
newterm->c_iflag &= ~(IXON | ICRNL);
}
if (flags & TERMIOS_RAW_CRNL_OUTPUT) {
/* dont convert NL to CR+NL on output */
newterm->c_oflag &= ~(ONLCR);
/* Maybe clear more c_oflag bits? Usually, only OPOST and ONLCR are set.
* OPOST Enable output processing (reqd for OLCUC and *NL* bits to work)
* OLCUC Map lowercase characters to uppercase on output.
* OCRNL Map CR to NL on output.
* ONOCR Don't output CR at column 0.
* ONLRET Don't output CR.
*/
}
if (flags & TERMIOS_RAW_INPUT) {
#ifndef IMAXBEL
# define IMAXBEL 0
#endif
#ifndef IUCLC
# define IUCLC 0
#endif
#ifndef IXANY
# define IXANY 0
#endif
/* IXOFF=0: disable sending XON/XOFF if input buf is full
* IXON=0: input XON/XOFF chars are not special
* BRKINT=0: dont send SIGINT on break
* IMAXBEL=0: dont echo BEL on input line too long
* INLCR,ICRNL,IUCLC: dont convert anything on input
*/
newterm->c_iflag &= ~(IXOFF|IXON|IXANY|BRKINT|INLCR|ICRNL|IUCLC|IMAXBEL);
}
return r;
}
int FAST_FUNC set_termios_to_raw(int fd, struct termios *oldterm, int flags)
{
struct termios newterm;
get_termios_and_make_raw(fd, &newterm, oldterm, flags);
return tcsetattr(fd, TCSANOW, &newterm);
}
pid_t FAST_FUNC safe_waitpid(pid_t pid, int *wstat, int options)
{
pid_t r;
do
r = waitpid(pid, wstat, options);
while ((r == -1) && (errno == EINTR));
return r;
}
pid_t FAST_FUNC wait_any_nohang(int *wstat)
{
return safe_waitpid(-1, wstat, WNOHANG);
}
// Wait for the specified child PID to exit, returning child's error return.
int FAST_FUNC wait4pid(pid_t pid)
{
int status;
if (pid <= 0) {
/*errno = ECHILD; -- wrong. */
/* we expect errno to be already set from failed [v]fork/exec */
return -1;
}
if (safe_waitpid(pid, &status, 0) == -1)
return -1;
if (WIFEXITED(status))
return WEXITSTATUS(status);
if (WIFSIGNALED(status))
return WTERMSIG(status) + 0x180;
return 0;
}