| /* 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 |