| /* util.c -- various utility functions |
| |
| Copyright 1993-2026 Free Software Foundation, Inc. |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <https://www.gnu.org/licenses/>. */ |
| |
| #include "info.h" |
| #include "util.h" |
| |
| /* wrapper for asprintf */ |
| static int |
| xvasprintf (char **ptr, const char *template, va_list ap) |
| { |
| int ret; |
| ret = vasprintf (ptr, template, ap); |
| if (ret < 0) |
| abort (); /* out of memory */ |
| return ret; |
| } |
| |
| int |
| xasprintf (char **ptr, const char *template, ...) |
| { |
| int ret; |
| va_list v; |
| va_start (v, template); |
| ret = xvasprintf (ptr, template, v); |
| va_end (v); |
| return ret; |
| } |
| |
| /* Return a pointer to the part of PATHNAME that simply defines the file. */ |
| char * |
| filename_non_directory (char *pathname) |
| { |
| register char *filename = pathname + strlen (pathname); |
| |
| if (HAVE_DRIVE (pathname)) |
| pathname += 2; |
| |
| while (filename > pathname && !IS_SLASH (filename[-1])) |
| filename--; |
| |
| return filename; |
| } |
| |
| |
| /* If ITER points to an ANSI escape sequence (Select Graphic Rendition), |
| process it, set PLEN to its length in bytes, and return 1. |
| Otherwise, return 0. |
| */ |
| int |
| ansi_sgr_escape (mbi_iterator_t iter, int *plen) |
| { |
| if (raw_escapes_p && *mbi_cur_ptr (iter) == '\033' && mbi_avail (iter)) |
| { |
| mbi_advance (iter); |
| if (*mbi_cur_ptr (iter) == '[' && mbi_avail (iter)) |
| { |
| ITER_SETBYTES (iter, 1); |
| mbi_advance (iter); |
| if (isdigit ((unsigned char) *mbi_cur_ptr (iter)) |
| && mbi_avail (iter)) |
| { |
| ITER_SETBYTES (iter, 1); |
| mbi_advance (iter); |
| if (*mbi_cur_ptr (iter) == 'm') |
| { |
| *plen = 4; |
| return 1; |
| } |
| else if (isdigit ((unsigned char) *mbi_cur_ptr (iter)) |
| && mbi_avail (iter)) |
| { |
| ITER_SETBYTES (iter, 1); |
| mbi_advance (iter); |
| if (*mbi_cur_ptr (iter) == 'm') |
| { |
| *plen = 5; |
| return 1; |
| } |
| } |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| |
| /* Flexible Text Buffer */ |
| |
| void |
| text_buffer_init (struct text_buffer *buf) |
| { |
| memset (buf, 0, sizeof *buf); |
| } |
| |
| void |
| text_buffer_free (struct text_buffer *buf) |
| { |
| free (buf->base); |
| } |
| |
| size_t |
| text_buffer_vprintf (struct text_buffer *buf, const char *format, va_list ap) |
| { |
| ssize_t n; |
| va_list ap_copy; |
| |
| if (!buf->base) |
| { |
| if (buf->size == 0) |
| buf->size = MIN_TEXT_BUF_ALLOC; /* Initial allocation */ |
| |
| buf->base = xmalloc (buf->size); |
| } |
| |
| for (;;) |
| { |
| va_copy (ap_copy, ap); |
| n = vsnprintf (buf->base + buf->off, buf->size - buf->off, |
| format, ap_copy); |
| va_end (ap_copy); |
| if (n < 0 || buf->off + n >= buf->size || |
| !memchr (buf->base + buf->off, '\0', buf->size - buf->off + 1)) |
| { |
| size_t newlen = buf->size * 2; |
| if (newlen < buf->size) |
| xalloc_die (); |
| buf->size = newlen; |
| buf->base = xrealloc (buf->base, buf->size); |
| } |
| else |
| { |
| buf->off += n; |
| break; |
| } |
| } |
| return n; |
| } |
| |
| /* Make sure there are LEN free bytes at end of BUF. */ |
| void |
| text_buffer_alloc (struct text_buffer *buf, size_t len) |
| { |
| if (buf->off + len > buf->size) |
| { |
| buf->size = buf->off + len; |
| if (buf->size < MIN_TEXT_BUF_ALLOC) |
| buf->size = MIN_TEXT_BUF_ALLOC; |
| buf->base = xrealloc (buf->base, buf->size); |
| } |
| } |
| |
| /* Return number of bytes that can be written to text buffer without |
| reallocating the text buffer. */ |
| size_t |
| text_buffer_space_left (struct text_buffer *buf) |
| { |
| /* buf->size is the offset of the first byte after the allocated space. |
| buf->off is the offset of the first byte to be written to. */ |
| return buf->size - buf->off; |
| } |
| |
| #if HAVE_ICONV |
| |
| /* Run iconv using text buffer as output buffer. */ |
| size_t |
| text_buffer_iconv (struct text_buffer *buf, iconv_t iconv_state, |
| ICONV_CONST char **inbuf, size_t *inbytesleft) |
| { |
| size_t out_bytes_left; |
| char *outptr; |
| size_t iconv_ret; |
| size_t extra_alloc = 1; |
| |
| while (1) |
| { |
| outptr = text_buffer_base (buf) + text_buffer_off (buf); |
| out_bytes_left = text_buffer_space_left (buf); |
| |
| iconv_ret = iconv (iconv_state, inbuf, inbytesleft, |
| &outptr, &out_bytes_left); |
| text_buffer_off (buf) = outptr - text_buffer_base (buf); |
| |
| if (iconv_ret != (size_t) -1) |
| break; /* success */ |
| |
| /* If we ran out of space, allocate more and try again. */ |
| if (errno == E2BIG) |
| text_buffer_alloc (buf, (extra_alloc *= 4)); |
| else |
| break; /* let calling code deal with it */ |
| } |
| return iconv_ret; |
| } |
| |
| #endif /* HAVE_ICONV */ |
| |
| size_t |
| text_buffer_add_string (struct text_buffer *buf, const char *str, size_t len) |
| { |
| text_buffer_alloc (buf, len); |
| memcpy (buf->base + buf->off, str, len); |
| buf->off += len; |
| return len; |
| } |
| |
| size_t |
| text_buffer_fill (struct text_buffer *buf, int c, size_t len) |
| { |
| char *p; |
| size_t i; |
| |
| text_buffer_alloc (buf, len); |
| |
| for (i = 0, p = buf->base + buf->off; i < len; i++) |
| *p++ = c; |
| buf->off += len; |
| |
| return len; |
| } |
| |
| void |
| text_buffer_add_char (struct text_buffer *buf, int c) |
| { |
| char ch = c; |
| text_buffer_add_string (buf, &ch, 1); |
| } |
| |
| size_t |
| text_buffer_printf (struct text_buffer *buf, const char *format, ...) |
| { |
| va_list ap; |
| size_t n; |
| |
| va_start (ap, format); |
| n = text_buffer_vprintf (buf, format, ap); |
| va_end (ap); |
| return n; |
| } |
| |
| #if defined(__MSDOS__) || defined(__MINGW32__) |
| /* Cannot use FILENAME_CMP here, since that does not consider forward- |
| and back-slash characters equal. */ |
| int |
| fncmp (const char *fn1, const char *fn2) |
| { |
| const char *s1 = fn1, *s2 = fn2; |
| |
| while (tolower (*s1) == tolower (*s2) |
| || (IS_SLASH (*s1) && IS_SLASH (*s2))) |
| { |
| if (*s1 == 0) |
| return 0; |
| s1++; |
| s2++; |
| } |
| |
| return tolower (*s1) - tolower (*s2); |
| } |
| |
| #endif |