/* string buffers */ /* Copyright (C) 1991-2003 The National Gallery 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #ifdef HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ #include #include #include #include #include #include #include /** * SECTION: buf * @short_description: a string you can append to * @stability: Stable * @see_also: #vips * @include: vips/vips.h * * A message buffer you can append stuff to safely and quickly. If the message * gets too long, you get "..." and truncation. Message buffers can be on the * stack or heap. * * For example: * * |[ * char txt[256]; * VipsBuf buf = VIPS_BUF_STATIC (txt); * int i; * * vips_buf_appends (&buf, "Numbers are: "); * for (i = 0; i < array_length; i++) { * if (i > 0) * vips_buf_appends (&buf, ", "); * vips_buf_appendg (&buf, array[i]); * } * printf ("%s", vips_buf_all (&buf)); * ]| */ /* Largest string we can append in one operation. */ #define MAX_STRSIZE (100000) /** * VIPS_BUF_STATIC: * @TEXT: the storage area to use * * Initialize a heap buffer. For example: * * |[ * char txt[256]; * VipsBuf buf = VIPS_BUF_STATIC (txt); * ]| */ /** * vips_buf_rewind: * @buf: the buffer * * Reset the buffer to the empty string. */ void vips_buf_rewind( VipsBuf *buf ) { buf->i = 0; buf->lasti = 0; buf->full = FALSE; if( buf->base ) buf->base[0] = '\0'; } /** * vips_buf_init: * @buf: the buffer * * Initialize a buffer. */ void vips_buf_init( VipsBuf *buf ) { buf->base = NULL; buf->mx = 0; buf->dynamic = FALSE; vips_buf_rewind( buf ); } /** * vips_buf_destroy: * @buf: the buffer * * Destroy a buffer. Only needed for heap buffers. Leaves the buffer in the * _init state. */ void vips_buf_destroy( VipsBuf *buf ) { if( buf->dynamic ) { VIPS_FREE( buf->base ); } vips_buf_init( buf ); } /** * vips_buf_set_static: * @buf: the buffer * @base: the start of the memory area to use for storage * @mx: the size of the storage area * * Attach the buffer to a static memory area. The buffer needs to have been * initialised. The memory area needs to be at least 4 bytes long. */ void vips_buf_set_static( VipsBuf *buf, char *base, int mx ) { g_assert( mx >= 4 ); vips_buf_destroy( buf ); buf->base = base; buf->mx = mx; buf->dynamic = FALSE; vips_buf_rewind( buf ); } /** * vips_buf_init_static: * @buf: the buffer * @base: the start of the memory area to use for storage * @mx: the size of the storage area * * Initialise and attach to a static memory area. VIPS_BUF_STATIC() is usually * more convenient. * * For example: * * |[ * char txt[256]; * VipsBuf buf; * * vips_buf_init_static (&buf, txt, 256); * ]| * * Static buffers don't need to be freed when they go out of scope, but their * size must be set at compile-time. */ void vips_buf_init_static( VipsBuf *buf, char *base, int mx ) { vips_buf_init( buf ); vips_buf_set_static( buf, base, mx ); } /** * vips_buf_set_dynamic: * @buf: the buffer * @mx: the size of the storage area * * Attach the buffer to a heap memory area. The buffer needs to have been * initialised. The memory area needs to be at least 4 bytes long. */ void vips_buf_set_dynamic( VipsBuf *buf, int mx ) { g_assert( mx >= 4 ); if( buf->mx == mx && buf->dynamic ) /* No change? */ vips_buf_rewind( buf ); else { vips_buf_destroy( buf ); if( !(buf->base = VIPS_ARRAY( NULL, mx, char )) ) /* No error return, so just block writes. */ buf->full = TRUE; else { buf->mx = mx; buf->dynamic = TRUE; vips_buf_rewind( buf ); } } } /** * vips_buf_init_dynamic: * @buf: the buffer * @mx: the size of the storage area * * Initialise and attach to a heap memory area. * The memory area needs to be at least 4 bytes long. * * |[ * VipsBuf buf; * * vips_buf_init_synamic (&buf, 256); * ]| * * Dynamic buffers must be freed with vips_buf_destroy(), but their size can * be set at runtime. */ void vips_buf_init_dynamic( VipsBuf *buf, int mx ) { vips_buf_init( buf ); vips_buf_set_dynamic( buf, mx ); } /** * vips_buf_appendns: * @buf: the buffer * @str: the string to append to the buffer * @sz: the size of the string to append * * Append at most @sz chars from @str to @buf. @sz < 0 means unlimited. This * is the low-level append operation: functions like vips_buf_appendf() build * on top of this. * * Returns: %FALSE on overflow, %TRUE otherwise. */ gboolean vips_buf_appendns( VipsBuf *buf, const char *str, int sz ) { int len; int n; int avail; int cpy; if( buf->full ) return( FALSE ); /* Amount we want to copy. */ len = strlen( str ); if( sz >= 0 ) n = VIPS_MIN( sz, len ); else n = len; /* Space available. */ avail = buf->mx - buf->i - 4; /* Amount we actually copy. */ cpy = VIPS_MIN( n, avail ); strncpy( buf->base + buf->i, str, cpy ); buf->i += cpy; if( buf->i >= buf->mx - 4 ) { buf->full = TRUE; strcpy( buf->base + buf->mx - 4, "..." ); buf->i = buf->mx - 1; return( FALSE ); } return( TRUE ); } /** * vips_buf_appends: * @buf: the buffer * @str: the string to append to the buffer * * Append the whole of @str to @buf. * * Returns: %FALSE on overflow, %TRUE otherwise. */ gboolean vips_buf_appends( VipsBuf *buf, const char *str ) { return( vips_buf_appendns( buf, str, -1 ) ); } /** * vips_buf_appendc: * @buf: the buffer * @ch: the character to append to the buffer * * Append a single character @ch to @buf. * * Returns: %FALSE on overflow, %TRUE otherwise. */ gboolean vips_buf_appendc( VipsBuf *buf, char ch ) { char tiny[2]; tiny[0] = ch; tiny[1] = '\0'; return( vips_buf_appendns( buf, tiny, 1 ) ); } /** * vips_buf_change: * @buf: the buffer * @o: the string to search for * @n: the string to substitute * * Swap the rightmost occurence of @o for @n. * * Returns: %FALSE on overflow, %TRUE otherwise. */ gboolean vips_buf_change( VipsBuf *buf, const char *old, const char *new ) { int olen = strlen( old ); int nlen = strlen( new ); int i; if( buf->full ) return( FALSE ); if( buf->i - olen + nlen > buf->mx - 4 ) { buf->full = TRUE; return( FALSE ); } /* Find pos of old. */ for( i = buf->i - olen; i > 0; i-- ) if( vips_isprefix( old, buf->base + i ) ) break; g_assert( i >= 0 ); /* Move tail of buffer to make right-size space for new. */ memmove( buf->base + i + nlen, buf->base + i + olen, buf->i - i - olen ); /* Copy new in. */ memcpy( buf->base + i, new, nlen ); buf->i = i + nlen + (buf->i - i - olen); return( TRUE ); } /** * vips_buf_removec: * @buf: the buffer * @ch: the character to remove * * Remove the last character, if it's @ch. * * Returns: %FALSE on failure, %TRUE otherwise. */ gboolean vips_buf_removec( VipsBuf *buf, char ch ) { if( buf->full ) return( FALSE ); if( buf->i <= 0 ) return( FALSE ); if( buf->base[buf->i - 1] == ch ) buf->i -= 1; return( TRUE ); } /** * vips_buf_appendf: * @buf: the buffer * @fmt: printf()-style format string * @Varargs: arguments to format string * * Format the string and append to @buf. * * Returns: %FALSE on overflow, %TRUE otherwise. */ gboolean vips_buf_appendf( VipsBuf *buf, const char *fmt, ... ) { char str[MAX_STRSIZE]; va_list ap; va_start( ap, fmt ); (void) vips_vsnprintf( str, MAX_STRSIZE, fmt, ap ); va_end( ap ); return( vips_buf_appends( buf, str ) ); } /** * vips_buf_vappendf: * @buf: the buffer * @fmt: printf()-style format string * @ap: arguments to format string * * Append to @buf, args as vprintf(). * * Returns: %FALSE on overflow, %TRUE otherwise. */ gboolean vips_buf_vappendf( VipsBuf *buf, const char *fmt, va_list ap ) { char str[MAX_STRSIZE]; (void) vips_vsnprintf( str, MAX_STRSIZE, fmt, ap ); return( vips_buf_appends( buf, str ) ); } /** * vips_buf_appendg: * @buf: the buffer * @g: value to format and append * * Append a double, non-localised. Useful for config files etc. * * Returns: %FALSE on overflow, %TRUE otherwise. */ gboolean vips_buf_appendg( VipsBuf *buf, double g ) { char text[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr( text, sizeof( text ), g ); return( vips_buf_appends( buf, text ) ); } /** * vips_buf_appendd: * @buf: the buffer * @d: value to format and append * * Append a number. If the number is -ve, add brackets. Needed for * building function arguments. * * Returns: %FALSE on overflow, %TRUE otherwise. */ gboolean vips_buf_appendd( VipsBuf *buf, int d ) { if( d < 0 ) return( vips_buf_appendf( buf, " (%d)", d ) ); else return( vips_buf_appendf( buf, " %d", d ) ); } /** * vips_buf_appendgv: * @buf: the buffer * @value: #GValue to format and append * * Format and append a #GValue with g_strdup_value_contents(). * * Returns: %FALSE on overflow, %TRUE otherwise. */ gboolean vips_buf_appendgv( VipsBuf *buf, GValue *value ) { char *str_value; gboolean result; str_value = g_strdup_value_contents( value ); result = vips_buf_appends( buf, str_value ); g_free( str_value ); return( result ); } /** * vips_buf_append_size: * @buf: the buffer * @n: the number of bytes * * Turn a number of bytes into a sensible string ... eg "12", "12KB", "12MB", * "12GB" etc. * * Returns: %FALSE on overflow, %TRUE otherwise. */ gboolean vips_buf_append_size( VipsBuf *buf, size_t n ) { const static char *names[] = { /* File length unit. */ N_( "bytes" ), /* Kilo byte unit. */ N_( "KB" ), /* Mega byte unit. */ N_( "MB" ), /* Giga byte unit. */ N_( "GB" ), /* Tera byte unit. */ N_( "TB" ) }; double sz = n; int i; for( i = 0; sz > 1024 && i < VIPS_NUMBER( names ); sz /= 1024, i++ ) ; if( i == 0 ) /* No decimal places for bytes. */ return( vips_buf_appendf( buf, "%g %s", sz, _( names[i] ) ) ); else return( vips_buf_appendf( buf, "%.2f %s", sz, _( names[i] ) ) ); } /** * vips_buf_all: * @buf: the buffer * * Return the contents of the buffer as a C string. * * Returns: the %NULL-terminated contents of the buffer. This is a pointer to * the memory managed by the buffer and must not be freed. */ const char * vips_buf_all( VipsBuf *buf ) { buf->base[buf->i] = '\0'; return( buf->base ); } /** * vips_buf_firstline: * @buf: the buffer * * Trim to just the first line (excluding "\n"). * * Returns: the %NULL-terminated contents of the buffer. This is a pointer to * the memory managed by the buffer and must not be freed. */ const char * vips_buf_firstline( VipsBuf *buf ) { char *p; if( (p = strchr( vips_buf_all( buf ), '\n' )) ) *p = '\0'; return( vips_buf_all( buf ) ); } /** * vips_buf_is_empty: * @buf: the buffer * * Returns: %TRUE if the buffer is empty. */ gboolean vips_buf_is_empty( VipsBuf *buf ) { return( buf->i == 0 ); } /** * vips_buf_is_full: * @buf: the buffer * * Returns: %TRUE if the buffer is full. */ gboolean vips_buf_is_full( VipsBuf *buf ) { return( buf->full ); } /** * vips_buf_len: * @buf: the buffer * * Returns: the number of characters currently in the buffer. */ int vips_buf_len( VipsBuf *buf ) { return( buf->i ); }