/* GNet - Networking library
* Copyright (C) 2000, 2001 David Helder
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "pack.h"
#include <string.h>
static gsize strlenn(char* str, gsize n);
static void flipmemcpy(char* dst, char* src, gsize n);
#define MEMCPY(D,S,N) \
do { \
if ((N) == 1) \
*((char*) (D)) = *((char*) (S)); \
else if ((N) == 2) \
{ \
((char*) (D))[0] = ((char*) (S))[0]; \
((char*) (D))[1] = ((char*) (S))[1]; \
} \
else if ((N) == 4) \
{ \
((char*) (D))[0] = ((char*) (S))[0]; \
((char*) (D))[1] = ((char*) (S))[1]; \
((char*) (D))[2] = ((char*) (S))[2]; \
((char*) (D))[3] = ((char*) (S))[3]; \
} \
else \
{ \
memcpy((D), (S), (N)); \
} \
} while(0)
#define FLIPMEMCPY(D,S,N) \
do { \
if ((N) == 1) \
*((char*) (D)) = *((char*) (S)); \
else if ((N) == 2) \
{ \
((char*) (D))[0] = ((char*) (S))[1]; \
((char*) (D))[1] = ((char*) (S))[0]; \
} \
else if ((N) == 4) \
{ \
((char*) (D))[0] = ((char*) (S))[3]; \
((char*) (D))[1] = ((char*) (S))[2]; \
((char*) (D))[2] = ((char*) (S))[1]; \
((char*) (D))[3] = ((char*) (S))[0]; \
} \
else \
{ \
flipmemcpy((D), (S), (N)); \
} \
} while(0)
/*
Get the length of string @str. @str is at most @n characters long.
@str need not be NUL-terminated (which is why we pass @n).
*/
static gsize
strlenn(char* str, gsize n)
{
gsize len = 0;
while (*str++ && len < n) ++len;
return len;
}
static void
flipmemcpy(char* dst, char* src, gsize n)
{
int nn = n;
for (; n; --n)
*dst++ = src[nn-n];
}
# if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
# define LEMEMCPY(D,S,N) MEMCPY(D,S,N)
# define BEMEMCPY(D,S,N) FLIPMEMCPY(D,S,N)
# else
# define BEMEMCPY(D,S,N) MEMCPY(D,S,N)
# define LEMEMCPY(D,S,N) FLIPMEMCPY(D,S,N)
# endif
/* **************************************** */
/* SIZE of type */
/* TYPE is actual data type, VTYPE is type according to vargs, TYPESTD
is the standard type (in endian or standard mode) */
#define SIZE(TYPENATIVE, VTYPE, TYPESTD) \
do { \
if (mult == 0) mult = 1; \
n += mult * (!sizemode ? sizeof(TYPENATIVE) : sizeof(TYPESTD)); \
for (; mult != 0; mult--) { TYPENATIVE t = (TYPENATIVE) va_arg (args, VTYPE); t=t;} \
/* mult = 0; */ \
} while(0)
/* PACK does MEMCPY regardless of endian */
/* TYPE is actual data type, VTYPE is type according to vargs */
#define PACK(TYPE, VTYPE) \
do { \
for (mult=(mult?mult:1); mult; --mult) \
{ \
TYPE t; \
g_return_val_if_fail (n + sizeof(TYPE) <= length, -1); \
t = (TYPE) va_arg (args, VTYPE); \
MEMCPY(buffer, (char*) &t, sizeof(TYPE)); \
buffer += sizeof(TYPE); \
n += sizeof(TYPE); \
} \
mult = 0; \
} while(0)
/* PACK2 does memcpy based on endian */
#define PACK2(TYPENATIVE, VTYPE, TYPESTD) \
do { \
for (mult=(mult?mult:1); mult; --mult) \
{ \
if (sizemode == 0) \
{ \
TYPENATIVE t; \
g_return_val_if_fail (n + sizeof(TYPENATIVE) <= length, -1);\
t = (TYPENATIVE) va_arg (args, VTYPE); \
MEMCPY(buffer, (char*) &t, sizeof(TYPENATIVE)); \
buffer += sizeof(TYPENATIVE); \
n += sizeof(TYPENATIVE); \
} \
else if (sizemode == 1) \
{ \
TYPESTD t; \
g_return_val_if_fail (n + sizeof(TYPESTD) <= length, -1); \
t = (TYPESTD) va_arg (args, VTYPE); \
LEMEMCPY(buffer, (char*) &t, sizeof(TYPESTD)); \
buffer += sizeof(TYPESTD); \
n += sizeof(TYPESTD); \
} \
else if (sizemode == 2) \
{ \
TYPESTD t; \
g_return_val_if_fail (n + sizeof(TYPESTD) <= length, -1); \
t = (TYPESTD) va_arg (args, VTYPE); \
BEMEMCPY(buffer, (char*) &t, sizeof(TYPESTD)); \
buffer += sizeof(TYPESTD); \
n += sizeof(TYPESTD); \
} \
} \
mult = 0; \
} while(0)
/* **************************************** */
/**
* gnet_pack
* @format: pack data format
* @buffer: buffer to pack to
* @length: length of @buffer
* @Varargs: variables to pack from
*
* Writes @Varargs to @buffer. @format is a string that describes
* the @Varargs and how they are to be packed. This string is a list
* of characters, each describing the type of an argument in
* @Varargs.
*
* An example:
*
* <informalexample>
* <programlisting>
* char buf[5];
* int myint = 42;
* int mybyte = 23;
* &space;
* gnet_pack("!ib", buf, sizeof(buf), myint, mybyte);
* &comstart; Now buf is { 42, 0, 0, 0, 23 }; &comend;
* </programlisting>
* </informalexample>
*
* As a shortcut, most types can be prefixed by an integer to specify
* how many times the type is repeated. For example, "4i2b" is
* equivalent to "iiiibb". This repeat argument is refered below to
* as REPEAT.
*
* Native byte order and sizes are used by default. If the first
* character of @format is < then little endian order and standard
* sizes are used. If the first character is > or !, then big endian
* (or network) order and standard size are used. Standard sizes are
* 1 byte for chars, 2 bytes for shorts, and 4 bytes for ints and
* longs.
*
* The types:
*
* x is a NULL character. It can be used for padding. It does not
* correspond to an argument in @Varargs.
*
* b/B is a signed/unsigned char.
*
* h/H is a signed/unsigned short.
*
* i/I is a signed/unsigned int.
*
* l/L is a signed/unsigned long.
*
* f/D is a float/double (always native order/size).
*
* v is a void pointer (always native size).
*
* s is a zero-terminated string.
*
* S is a zero-padded string of maximum length REPEAT. We write
* up-to a NULL character or REPEAT characters, whichever comes
* first. We then write NULL characters up to a total of REPEAT
* characters. Special case: if REPEAT is not specified, we write
* the string as a non-NULL-terminated string (note that it can't be
* unpacked easily then).
*
* r is a byte array of NEXT bytes. NEXT is the next argument passed
* to gnet_pack() and is a gint.
*
* R is a byte array of REPEAT bytes. REPEAT must be specified.
*
* p is a Pascal string. The string passed is a NULL-termiated
* string of less than 256 character. The string writen is a
* non-NULL-terminated string with a byte before the string storing
* the string length. REPEAT is repeat.
*
* Mnemonics: (B)yte, s(H)ort, (I)nteger, (F)loat, (D)ouble, (V)oid
* pointer, (S)tring, (R)aw
*
* Pack was inspired by Python's and Perl's pack. It is more like
* Python's than Perl's. Note that in GNet, a repeat of 0 does not
* align the data (as in Python).
*
* Returns: number of bytes packed; -1 on error.
*
**/
gint
gnet_pack (const gchar* format, gchar* buffer, const gint length, ...)
{
va_list args;
gint rv;
va_start (args, length);
rv = gnet_vpack (format, buffer, length, args);
va_end (args);
return rv;
}
/**
* gnet_pack_strdup
* @format: pack data format
* @bufferp: pointer to a buffer (buffer is caller owned)
* @Varargs: variables to pack from
*
* Writes @Varargs into a buffer pointed to by @bufferp. The buffer
* is allocated by the function and is caller owned. See gnet_pack()
* for more information.
*
* Returns: bytes packed; -1 on error.
*
**/
gint
gnet_pack_strdup (const gchar* format, gchar** bufferp, ...)
{
va_list args;
gint size;
gint rv;
g_return_val_if_fail (format, -1);
g_return_val_if_fail (bufferp, -1);
/* Get size */
va_start (args, bufferp);
size = gnet_vcalcsize (format, args);
va_end (args);
g_return_val_if_fail (size >= 0, -1);
if (size == 0)
{
*bufferp = NULL;
return 0;
}
*bufferp = g_new (gchar, size);
/* Pack */
va_start (args, bufferp);
rv = gnet_vpack (format, *bufferp, size, args);
va_end (args);
return rv;
}
/* **************************************** */
/**
* gnet_calcsize
* @format: pack data format
* @Varargs: variables
*
* Calculates the size of the buffer needed to pack @Varargs by the
* given format. See gnet_pack() for more information.
*
* Returns: number of bytes required to pack; -1 on error.
*
**/
gint
gnet_calcsize (const gchar* format, ...)
{
va_list args;
gint size;
va_start (args, format);
size = gnet_vcalcsize (format, args);
va_end (args);
return size;
}
/**
* gnet_vcalcsize
* @format: pack data format
* @args: var args
*
* Var arg interface to gnet_calcsize(). See gnet_calcsize() for
* additional information.
*
* Returns: number of bytes required to pack; -1 on error.
*
**/
gint
gnet_vcalcsize (const gchar* format, va_list args)
{
gint n = 0;
gchar* p = (gchar*) format;
guint mult = 0;
gint sizemode = 0; /* 1 = little, 2 = big */
if (!format)
return 0;
switch (*p)
{
case '@': ++p; break;
case '<': sizemode = 1; ++p; break;
case '>':
case '!': sizemode = 2; ++p; break;
}
for (; *p; ++p)
{
switch (*p)
{
case 'x': { n += mult?mult:1; mult = 0; break; }
case 'b': { SIZE(gint8, int, gint8); break; }
case 'B': { SIZE(guint8, unsigned int, guint8); break; }
case 'h': { SIZE(short, int, gint16); break; }
case 'H': { SIZE(unsigned short, unsigned int, guint16); break; }
case 'i': { SIZE(int, int, gint32); break; }
case 'I': { SIZE(unsigned int, unsigned int, guint32); break; }
case 'l': { SIZE(long, long, gint32); break; }
case 'L': { SIZE(unsigned long, unsigned long, guint32); break; }
case 'f': { SIZE(float, double, float); break; }
case 'd': { SIZE(double, double, double); break; }
case 'v': { SIZE(void*, void*, void*); break; }
case 's':
{
for (mult=(mult?mult:1); mult; --mult)
{
char* s;
s = va_arg (args, char*);
g_return_val_if_fail (s, -1);
n += strlen(s) + 1;
}
mult = 0;
break;
}
case 'S':
{
if (mult != 0)
n += mult;
else
{
char* s;
s = va_arg (args, char*);
n += strlen(s);
}
mult = 0;
break;
}
case 'r':
{
for (mult=(mult?mult:1); mult; --mult)
{
char* s;
int ln;
s = va_arg (args, char*);
g_return_val_if_fail (s, -1);
ln = va_arg (args, int);
n += ln;
}
mult = 0;
break;
}
case 'R':
{
char* s;
s = va_arg (args, char*);
g_return_val_if_fail (s, -1);
g_return_val_if_fail (mult, -1);
n += mult;
mult = 0;
break;
}
case 'p':
{
for (mult=(mult?mult:1); mult; --mult)
{
char* s;
int slen;
s = va_arg (args, char*);
g_return_val_if_fail (s, -1);
slen = strlen(s);
n += slen + 1;
}
mult = 0;
break;
}
case '0':case '1':case '2':case '3':case '4':
case '5':case '6':case '7':case '8':case '9':
{
mult *= 10;
mult += (*p - '0');
break;
}
case ' ':case '\t':case '\n': break;
default:
g_return_val_if_fail (FALSE, -1);
}
}
return n;
}
/**
* gnet_vpack
* @format: pack data format
* @buffer: buffer to pack to
* @length: length of @buffer
* @args: var args
*
* Var arg interface to gnet_pack(). See gnet_pack() for more
* information.
*
* Returns: bytes packed; -1 on error.
*
**/
gint
gnet_vpack (const gchar* format, gchar* buffer, const gint length, va_list args)
{
gint n = 0;
gchar* p = (gchar*) format;
guint mult = 0;
gint sizemode = 0; /* 1 = little, 2 = big */
g_return_val_if_fail (format, -1);
g_return_val_if_fail (buffer, -1);
g_return_val_if_fail (length, -1);
switch (*p)
{
case '@': ++p; break;
case '<': sizemode = 1; ++p; break;
case '>':
case '!': sizemode = 2; ++p; break;
}
for (; *p; ++p)
{
switch (*p)
{
case 'x':
{
for (mult=(mult?mult:1); mult; --mult)
{
g_return_val_if_fail (n + 1 <= length, -1);
*buffer++ = 0; ++n;
}
mult = 0;
break;
}
case 'b': { PACK(gint8, int); break; }
case 'B': { PACK(guint8, unsigned int); break; }
case 'h': { PACK2(short, int, gint16); break; }
case 'H': { PACK2(unsigned short, unsigned int, guint16); break; }
case 'i': { PACK2(int, int, gint32); break; }
case 'I': { PACK2(unsigned int, unsigned int, guint32); break; }
case 'l': { PACK2(long, long, gint32); break; }
case 'L': { PACK2(unsigned long, unsigned long, guint32); break; }
case 'f': { PACK(float, double); break; }
case 'd': { PACK(double, double); break; }
case 'v': { PACK2(void*, void*, void*); break; }
case 's':
{
for (mult=(mult?mult:1); mult; --mult)
{
gchar* s;
gsize slen;
s = va_arg (args, gchar*);
g_return_val_if_fail (s, -1);
slen = strlen(s);
g_return_val_if_fail (n + slen + 1 <= length, -1);
memcpy (buffer, s, slen + 1); /* include the 0 */
buffer += slen + 1;
n += slen + 1;
}
mult = 0;
break;
}
case 'S':
{
gchar* s;
s = va_arg (args, gchar*);
g_return_val_if_fail (s, -1);
if (!mult)
{
gsize slen;
slen = strlen(s);
g_return_val_if_fail (n + slen <= length, -1);
memcpy (buffer, s, slen); /* don't include the 0 */
buffer += slen;
n += slen;
}
else
{
guint i;
g_return_val_if_fail (n + mult <= length, -1);
for (i = 0; i < mult && s[i]; ++i)
*buffer++ = s[i];
for (; i < mult; ++i)
*buffer++ = 0;
n += mult;
}
mult = 0;
break;
}
case 'r':
{
for (mult=(mult?mult:1); mult; --mult)
{
gchar* s;
guint ln;
s = va_arg (args, gchar*);
ln = va_arg (args, guint);
g_return_val_if_fail (s, -1);
g_return_val_if_fail (n + ln <= length, -1);
memcpy(buffer, s, ln);
buffer += ln;
n += ln;
}
mult = 0;
break;
}
case 'R':
{
gchar* s;
s = va_arg (args, gchar*);
g_return_val_if_fail (s, -1);
g_return_val_if_fail (mult, -1);
g_return_val_if_fail (n + mult <= length, -1);
memcpy (buffer, s, mult);
buffer += mult;
n += mult;
mult = 0;
break;
}
case 'p':
{
for (mult=(mult?mult:1); mult; --mult)
{
gchar* s;
gsize slen;
s = va_arg (args, char*);
g_return_val_if_fail (s, -1);
slen = strlen(s);
g_return_val_if_fail (n < 256, -1);
g_return_val_if_fail (n + slen + 1 <= length, -1);
*buffer++ = slen;
memcpy (buffer, s, slen);
buffer += slen;
n += slen + 1;
}
mult = 0;
break;
}
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
mult *= 10;
mult += (*p - '0');
break;
}
case ' ': case '\t': case '\n': break;
default: g_return_val_if_fail (FALSE, -1);
}
}
return n;
}
/* **************************************** */
#define UNPACK(TYPE) \
do { \
for (mult=(mult?mult:1); mult; --mult) \
{ \
TYPE* t; \
g_return_val_if_fail (n + sizeof(TYPE) <= length, FALSE); \
t = va_arg (args, TYPE*); \
MEMCPY((char*) t, buffer, sizeof(TYPE)); \
buffer += sizeof(TYPE); \
n += sizeof(TYPE); \
} \
mult = 0; \
} while(0)
#define UNPACK2(TYPENATIVE, TYPESTD) \
do { \
for (mult=(mult?mult:1); mult; --mult) \
{ \
if (sizemode == 0) \
{ \
TYPENATIVE* t; \
g_return_val_if_fail (n + sizeof(TYPENATIVE) <= length, -1); \
t = va_arg (args, TYPENATIVE*); \
MEMCPY((char*) t, buffer, sizeof(TYPENATIVE)); \
buffer += sizeof(TYPENATIVE); \
n += sizeof(TYPENATIVE); \
} \
else if (sizemode == 1) \
{ \
TYPESTD* t; \
g_return_val_if_fail (n + sizeof(TYPESTD) <= length, -1); \
t = va_arg (args, TYPESTD*); \
LEMEMCPY((char*) t, buffer, sizeof(TYPESTD)); \
buffer += sizeof(TYPESTD); \
n += sizeof(TYPESTD); \
} \
else if (sizemode == 2) \
{ \
TYPESTD* t; \
g_return_val_if_fail (n + sizeof(TYPESTD) <= length, -1); \
t = va_arg (args, TYPESTD*); \
BEMEMCPY((char*) t, buffer, sizeof(TYPESTD)); \
buffer += sizeof(TYPESTD); \
n += sizeof(TYPESTD); \
} \
} \
mult = 0; \
} while(0)
/* **************************************** */
/**
* gnet_unpack
* @format: unpack data format
* @buffer: buffer to unpack from
* @length: length of @buffer
* @Varargs: addresses of variables to unpack to
*
* Reads the data in @buffer into @Varargs. @format is a string that
* describes the @Varargs and how they are to be packed. This string
* is a list of characters, each describing the type of an argument
* in @Varargs.
*
* An example:
*
* <informalexample>
* <programlisting>
* char buf[5] = { 42, 0, 0, 0, 23 };
* int myint;
* int mybyte;
* &space;
* gnet_unpack("!ib", buf, sizeof(buf), &myint, &mybyte);
* &comstart; Now myint is 42 and mybyte is 23 &comend;
* </programlisting>
* </informalexample>
*
* In unpack, the arguments must be pointers to the appropriate type.
* Strings and byte arrays are allocated dynamicly (by g_new()). The
* caller is responsible for g_free()-ing it.
*
* As a shortcut, most types can be prefixed by an integer to specify
* how many times the type is repeated. For example, "4i2b" is
* equivalent to "iiiibb". This repeat argument is refered below to
* as REPEAT.
*
* The types:
*
* x is a pad byte. The byte is skipped and not stored. We do not
* check its value.
*
* b/B is a signed/unsigned char.
*
* h/H is a signed/unsigned short.
*
* i/I is a signed/unsigned int.
*
* l/L is a signed/unsigned long.
*
* f/D is a float/double (always native order/size).
*
* v is a void pointer (always native size).
*
* s is a zero-terminated string.
*
* S is a zero-padded string of length REPEAT. We read REPEAT
* characters or until a NULL character. Any remaining characters
* are filled in with 0's. REPEAT must be specified.
*
* r is a byte array of NEXT bytes. NEXT is the next argument and is
* a gint. REPEAT is repeat.
*
* R is a byte array of REPEAT bytes. REPEAT must be specified.
*
* p is a Pascal string. The first byte read is the length of the
* string and the string follows. The unpacked string will be a
* normal, NULL-terminated string. REPEAT is repeat.
*
* Returns: number of bytes unpacked; -1 on error. The bytes are
* unpacked to the variables pointed to by the @Varargs.
*
**/
gint
gnet_unpack (const gchar* format, gchar* buffer, gint length, ...)
{
va_list args;
gint rv;
va_start (args, length);
rv = gnet_vunpack (format, buffer, length, args);
va_end (args);
return rv;
}
/**
* gnet_vunpack
* @format: unpack data format
* @buffer: buffer to unpack from
* @length: length of @buffer
* @args: var args
*
* Var arg interface to gnet_unpack(). See gnet_unpack() for more
* information.
*
* Returns: number of bytes packed; -1 on error.
*
**/
gint
gnet_vunpack (const gchar* format, gchar* buffer, gint length, va_list args)
{
gint n = 0;
gchar* p = (gchar*) format;
guint mult = 0;
gint sizemode = 0; /* 1 = little, 2 = big */
g_return_val_if_fail (format, -1);
g_return_val_if_fail (buffer, -1);
switch (*p)
{
case '@': ++p; break;
case '<': sizemode = 1; ++p; break;
case '>':
case '!': sizemode = 2; ++p; break;
}
for (; *p; ++p)
{
switch (*p)
{
case 'x':
{
mult = mult? mult:1;
g_return_val_if_fail (n + mult <= length, FALSE);
buffer += mult;
n += mult;
mult = 0;
break;
}
case 'b': { UNPACK(gint8); break; }
case 'B': { UNPACK(guint8); break; }
case 'h': { UNPACK2(short, gint16); break; }
case 'H': { UNPACK2(unsigned short, guint16); break; }
case 'i': { UNPACK2(int, gint32); break; }
case 'I': { UNPACK2(unsigned int, guint32); break; }
case 'l': { UNPACK2(long, gint32); break; }
case 'L': { UNPACK2(unsigned long, guint32); break; }
case 'f': { UNPACK(float); break; }
case 'd': { UNPACK(double); break; }
case 'v': { UNPACK2(void*, void*); break; }
case 's':
{
for (mult=(mult?mult:1); mult; --mult)
{
gchar** sp;
gsize slen;
sp = va_arg (args, gchar**);
g_return_val_if_fail (sp, -1);
slen = strlenn(buffer, length - n);
g_return_val_if_fail (n + slen <= length, FALSE);
*sp = g_new(gchar, slen + 1);
memcpy (*sp, buffer, slen);
(*sp)[slen] = 0;
buffer += slen + 1;
n += slen + 1;
}
mult = 0;
break;
}
case 'S':
{
gchar** sp;
gsize slen;
g_return_val_if_fail (mult, -1);
sp = va_arg (args, gchar**);
g_return_val_if_fail (sp, -1);
slen = MIN(mult, strlenn(buffer, length - n));
g_return_val_if_fail ((n + slen) <= length, -1);
*sp = g_new(gchar, mult + 1);
memcpy (*sp, buffer, slen);
while (slen < (mult + 1)) (*sp)[slen++] = '\0';
buffer += mult;
n += mult;
mult = 0; break;
}
case 'r': /* r is the same as s, in this case. */
{
for (mult=(mult?mult:1); mult; --mult)
{
gchar** sp;
guint ln;
sp = va_arg (args, gchar**);
ln = va_arg (args, guint);
g_return_val_if_fail (sp, -1);
g_return_val_if_fail (n + ln <= length, FALSE);
*sp = g_new(char, ln);
memcpy(*sp, buffer, ln);
buffer += ln;
n += ln;
}
mult = 0; break;
}
case 'R':
{
gchar** sp;
sp = va_arg (args, gchar**);
g_return_val_if_fail (sp, -1);
g_return_val_if_fail (mult, -1);
g_return_val_if_fail (n + mult <= length, -1);
*sp = g_new(char, mult);
memcpy(*sp, buffer, mult);
buffer += mult;
n += mult;
mult = 0;
break;
}
case 'p':
{
for (mult=(mult?mult:1); mult; --mult)
{
gchar** sp;
guint slen;
sp = va_arg (args, gchar**);
g_return_val_if_fail (sp, -1);
g_return_val_if_fail (n + 1 <= length, FALSE);
slen = *buffer++;
++n;
g_return_val_if_fail (n + slen <= length, FALSE);
*sp = g_new(gchar, slen + 1);
memcpy (*sp, buffer, slen);
(*sp)[slen] = 0;
buffer += slen;
n += slen;
}
mult = 0;
break;
}
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
mult *= 10;
mult += (*p - '0');
break;
}
case ' ': case '\t': case '\n': break;
default: g_return_val_if_fail (FALSE, -1);
}
}
return n;
}
syntax highlighted by Code2HTML, v. 0.9.1