blob: 9fd9ffd62dbf556f39b0887aa553ee9f55e94b03 [file] [log] [blame]
/* 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