/* Jungle Monkey
* Copyright (C) 1999-2001 The Regents of the University of Michigan
*
* 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
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "elf.h"
static ElfNode* elf_new_vformat (const gchar* name, const gchar* format, va_list args);
/* ************************************************************ */
/* Building */
ElfNode*
elf_new (const gchar* name)
{
ElfNode* node;
node = g_new0 (ElfNode, 1);
if (name)
node->name = g_strdup (name);
return node;
}
/**
elf_new_format (gchar* name, gchar* fmt, ...)
format:
s = string
d = integer
b = boolean
B = boolean, but only set if TRUE
ex:
elf_new_format ("info", "sdb", "name", "myfile",
"length", 23120,
"read-only", FALSE);
elf_new_format ("info", "");
elf_new_format ("info", NULL);
*/
ElfNode*
elf_new_format (const gchar* name, const gchar* format, ...)
{
va_list args;
ElfNode* node;
va_start (args, format);
node = elf_new_vformat (name, format, args);
va_end (args);
return node;
}
static ElfNode*
elf_new_vformat (const gchar* name, const gchar* format, va_list args)
{
ElfNode* node;
g_return_val_if_fail (format, NULL);
node = elf_new (name);
if (!format)
return node;
for (; *format; ++format)
{
gchar* attribute;
attribute = va_arg (args, gchar*);
switch (*format)
{
case 's':
{
gchar* s;
s = va_arg (args, gchar*);
elf_set_attribute (node, attribute, s);
break;
}
case 'd':
{
gint d;
d = va_arg (args, gint);
elf_set_attribute_int (node, attribute, d);
break;
}
case 'b':
{
gboolean b;
b = (gboolean) va_arg (args, gint);
elf_set_attribute_bool (node, attribute, b);
break;
}
case 'B':
{
gboolean b;
b = (gboolean) va_arg (args, gint);
if (b)
elf_set_attribute_bool (node, attribute, b);
break;
}
}
}
return node;
}
void
elf_delete (ElfNode* node)
{
if (node != NULL)
{
GList* i;
/* Check if we already have the attribute */
for (i = node->attributes; i != NULL; i = i->next)
{
ElfAttribute* attribute = (ElfAttribute*) i->data;
g_assert (attribute != NULL);
g_free(attribute->name);
g_free(attribute->value);
g_free(attribute);
}
g_list_free(node->attributes);
g_free(node->name);
g_free(node);
}
}
void
elf_delete_list (GList* list)
{
GList* i;
for (i = list; i != NULL; i = i->next)
{
ElfNode* node = (ElfNode*) i->data;
g_assert (node != NULL);
elf_delete(node);
}
g_list_free(list);
}
ElfNode*
elf_clone (const ElfNode* node)
{
ElfNode* n;
GList* i;
g_return_val_if_fail (node, NULL);
n = elf_new (node->name);
for (i = node->attributes; i != NULL; i = i->next)
{
ElfAttribute* attr = (ElfAttribute*) i->data;
ElfAttribute* a;
a = g_new0 (ElfAttribute, 1);
a->name = g_strdup (attr->name);
a->length = attr->length;
if (attr->value)
{
a->value = g_malloc (attr->length + 1);
memcpy (a->value, attr->value, attr->length);
a->value[attr->length] = '\0';
}
n->attributes = g_list_prepend (n->attributes, a);
}
n->attributes = g_list_reverse (n->attributes);
return n;
}
void
elf_set_name (ElfNode* node, const char* name)
{
g_return_if_fail (node);
g_return_if_fail (name);
g_free (node->name);
node->name = g_strdup (name);
}
gchar*
elf_get_name (ElfNode* node)
{
g_return_val_if_fail (node, NULL);
return node->name;
}
void
elf_set_attribute (ElfNode* node, const gchar* name, const gchar* value)
{
if (value)
elf_set_attribute_data (node, name, value, strlen (value));
else
elf_set_attribute_data (node, name, NULL, 0);
}
void
elf_set_attribute_int (ElfNode* node, const gchar* name, gint value)
{
gchar buffer[64];
sprintf (buffer, "%d", value);
elf_set_attribute (node, name, buffer);
}
void
elf_set_attribute_bool (ElfNode* node, const gchar* name, gboolean value)
{
if (value)
elf_set_attribute (node, name, "1");
else
elf_set_attribute (node, name, "0");
}
void
elf_set_attribute_data (ElfNode* node, const gchar* name, const gchar* data, gint length)
{
ElfAttribute* attribute;
GList* i;
g_return_if_fail (node != NULL);
g_return_if_fail (name != NULL);
/* Check if we already have the attribute */
for (i = node->attributes; i != NULL; i = i->next)
{
attribute = (ElfAttribute*) i->data;
g_assert (attribute != NULL);
if (!strcmp(attribute->name, name))
{
g_free (attribute->value);
if (data)
{
attribute->value = g_malloc (length + 1);
memcpy (attribute->value, data, length);
attribute->value[length] = '\0';
}
else
attribute->value = NULL;
attribute->length = length;
return;
}
}
/* Add the attribute */
attribute = g_new0(ElfAttribute, 1);
attribute->name = g_strdup (name);
if (data)
attribute->value = g_memdup (data, length);
attribute->length = length;
node->attributes = g_list_prepend(node->attributes, attribute);
}
gchar*
elf_get_attribute (const ElfNode* node, const gchar* name)
{
gchar* value = NULL;
gint length = 0;
elf_get_attribute_data (node, name, &value, &length);
return value;
}
gint
elf_get_attribute_int (const ElfNode* node, const gchar* name)
{
gchar* attr;
attr = elf_get_attribute (node, name);
if (!attr)
return 0;
return atoi(attr);
}
gboolean
elf_get_attribute_bool (const ElfNode* node, const gchar* name)
{
gchar* attr;
attr = elf_get_attribute (node, name);
if (!attr)
return FALSE;
if (*attr == '1' || *attr == 't' || *attr == 'T')
return TRUE;
return FALSE;
}
void
elf_get_attribute_data (const ElfNode* node, const gchar* name, gchar** value, gint* length)
{
GList* i;
g_return_if_fail (node != NULL);
g_return_if_fail (name != NULL);
g_return_if_fail (value != NULL);
g_return_if_fail (length != NULL);
*value = NULL;
*length = 0;
for (i = node->attributes; i != NULL; i = i->next)
{
ElfAttribute* attribute = (ElfAttribute*) i->data;
g_assert (attribute != NULL);
if (!strcmp(attribute->name, name))
{
*value = attribute->value;
*length = attribute->length;
}
}
}
gboolean
elf_has_attribute (const ElfNode* node, const gchar* name)
{
GList* i;
g_return_val_if_fail (node != NULL, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
for (i = node->attributes; i != NULL; i = i->next)
{
ElfAttribute* attribute = (ElfAttribute*) i->data;
g_assert (attribute != NULL);
if (!strcmp(attribute->name, name))
return TRUE;
}
return FALSE;
}
void
elf_remove_attribute (ElfNode* node, const gchar* name)
{
GList* i;
g_return_if_fail (node != NULL);
g_return_if_fail (name != NULL);
/* TODO: Fix, this is inefficient */
for (i = node->attributes; i != NULL; i = i->next)
{
ElfAttribute* attribute = (ElfAttribute*) i->data;
g_assert (attribute != NULL);
if (!strcmp(attribute->name, name))
{
node->attributes = g_list_remove(node->attributes, attribute);
return;
}
}
}
/* ************************************************************ */
/* Reading */
#define SKIP_WHITESPACE { while (i < length && (isspace((int) buffer[i]) && buffer[i] != '\n')) ++i; }
#define CHECK_DONE { if (i >= length) goto done; }
static ElfAttribute* elf_read_attribute (const gchar* buffer, gint length, gint* bytes_read);
ElfNode*
elf_read (const gchar* buffer, gint length)
{
gint bytes_read;
return elf_read_node (buffer, length, &bytes_read);
}
GList*
elf_read_list (const gchar* buf, gint len)
{
GList* nodes = NULL;
gint off = 0;
while (off < len)
{
gint br;
ElfNode* node;
node = elf_read_node (&buf[off], len - off, &br);
if (node == NULL)
break;
off += br;
nodes = g_list_prepend (nodes, node);
}
nodes = g_list_reverse (nodes);
return nodes;
}
GList*
elf_read_list_path (const gchar* path)
{
FILE* file = NULL;
gchar* buf = NULL;
gint len;
struct stat s;
GList* list = NULL;
file = fopen (path, "r");
if (!file) return NULL;
if (fstat(fileno(file), &s))
goto done;
len = s.st_size;
if (len <= 0)
goto done;
buf = g_malloc (len);
if (fread (buf, len, 1, file) != 1)
goto done;
list = elf_read_list (buf, len);
done:
if (file) fclose (file);
if (buf) g_free (buf);
return list;
}
ElfNode*
elf_read_node (const gchar* buffer, gint length, gint* bytes_read)
{
ElfNode* node = NULL;
gint i = 0;
const gchar* p;
*bytes_read = 0;
/* Skip whitespace */
SKIP_WHITESPACE;
CHECK_DONE;
/* Read in name */
p = &buffer[i];
while (i < length && !isspace((int) buffer[i])) ++i;
if (p == &buffer[i]) goto done;
node = g_new0 (ElfNode, 1);
node->name = g_strndup (p, &buffer[i] - p);
CHECK_DONE;
/* Skip whitespace */
SKIP_WHITESPACE;
CHECK_DONE;
/* Read the attributes */
while (i < length)
{
gint br;
ElfAttribute* attribute;
attribute =
elf_read_attribute (&buffer[i], length - i, &br);
i += br;
if (attribute == NULL)
break;
node->attributes = g_list_prepend (node->attributes, attribute);
}
node->attributes = g_list_reverse(node->attributes);
/* Skip whitespace and newline */
SKIP_WHITESPACE;
if (i < length && buffer[i] == '\n') ++i;
done:
*bytes_read = i;
return node;
}
ElfAttribute*
elf_read_attribute (const gchar* buffer, gint length, gint* bytes_read)
{
ElfAttribute* attribute = NULL;
gint i = 0;
const gchar* p;
gint j;
*bytes_read = 0;
/* Skip whitespace */
SKIP_WHITESPACE;
CHECK_DONE;
if (buffer[i] == '\n')
{
++i;
goto done;
}
/* Read in the name */
p = &buffer[i];
while (i < length && !isspace((int) buffer[i]) && buffer[i] != '=') ++i;
if (p == &buffer[i]) goto done;
attribute = g_new0 (ElfAttribute, 1);
attribute->name = g_strndup (p, &buffer[i] - p);
CHECK_DONE;
/* Skip whitespace */
SKIP_WHITESPACE;
CHECK_DONE;
/* Skip = */
if (buffer[i] != '=') goto done;
++i;
CHECK_DONE;
/* Skip whitespace */
SKIP_WHITESPACE;
CHECK_DONE;
/* Skip '"' */
if (buffer[i] != '\"') goto done;
++i;
CHECK_DONE;
/* Read in the value */
p = &buffer[i];
while (i < length)
{
/* Ignore \" */
if (buffer[i] == '\\' && (i + 1 < length) && buffer[i + 1] == '\"')
i += 2;
else if (buffer[i] == '\"')
break;
++i;
}
CHECK_DONE;
if (buffer[i] != '\"') goto done;
/* Save the attribute */
attribute->value = g_new0 (gchar, 1 + &buffer[i] - p);
attribute->length = &buffer[i] - p;
j = 0;
while (p < &buffer[i])
{
/* Break if this is the last quote */
if (*p == '\"')
{
break;
}
/* Convert \" to " */
else if (*p == '\\' && (&p[1] < &buffer[i]) && p[1] == '\"')
{
attribute->value[j] = '\"';
p += 2;
}
/* Convert \\ to \ */
else if (*p == '\\' && (&p[1] < &buffer[i]) && p[1] == '\\')
{
attribute->value[j] = '\\';
p += 2;
}
/* Otherwise, it's just a normal letter */
else
{
attribute->value[j] = *p;
p++;
}
++j;
}
attribute->value[j] = '\0';
/* Move past the \ */
++i;
done:
*bytes_read = i;
return attribute;
}
/* ************************************************************ */
/* Writing */
static void glue(gchar** dstp, gint* lenp, gchar* src1, gint length1, gchar* src2, gint length2);
static void elf_write_attribute(ElfAttribute* attribute, gchar** bufferp, gint* lengthp);
static void
glue(gchar** dstp, gint* lenp, gchar* src1, gint length1, gchar* src2, gint length2)
{
gchar* dst;
gint len;
len = length1 + length2;
dst = g_new0(gchar, len);
memcpy(dst, src1, length1);
memcpy(&dst[length1], src2, length2);
g_free(src1);
g_free(src2);
*dstp = dst;
*lenp = len;
}
void
elf_write_list (GList* nodes, gchar** bufferp, gint* lengthp)
{
GList* i;
gchar* buffer = NULL;
gint length = 0;
g_return_if_fail (bufferp != NULL);
g_return_if_fail (lengthp != NULL);
for (i = nodes; i != NULL; i = i->next)
{
ElfNode* node;
gchar* new_buffer;
gint new_length;
node = (ElfNode*) i->data;
g_assert (node != NULL);
elf_write(node, &new_buffer, &new_length);
glue(&buffer, &length, buffer, length, new_buffer, new_length);
}
*bufferp = buffer;
*lengthp = length;
}
void
elf_write (ElfNode* node, gchar** bufferp, gint* lengthp)
{
GList* i;
gchar* buffer;
gint length;
gchar* newline;
g_return_if_fail (node);
g_return_if_fail (node->name);
g_return_if_fail (bufferp);
g_return_if_fail (lengthp);
buffer = g_strdup_printf ("%s ", node->name);
length = strlen (buffer);
for (i = node->attributes; i != NULL; i = i->next)
{
ElfAttribute* attribute;
gchar* new_buffer;
gint new_length;
attribute = (ElfAttribute*) i->data;
g_assert (attribute != NULL);
elf_write_attribute(attribute, &new_buffer, &new_length);
glue (&buffer, &length, buffer, length, new_buffer, new_length);
}
newline = g_strdup ("\n");
glue (bufferp, lengthp, buffer, length, newline, 1);
}
void
elf_write_attribute (ElfAttribute* attribute, gchar** bufferp, gint* lengthp)
{
gchar* buffer;
g_return_if_fail (attribute);
g_return_if_fail (attribute->name);
g_return_if_fail (bufferp);
g_return_if_fail (lengthp);
if (attribute->value)
{
gchar* escaped_value;
gint i, j;
gint name_len;
gint buffer_len;
escaped_value = g_new0 (gchar, 2 * attribute->length + 1);
for (i = 0, j = 0; i < attribute->length; ++i)
{
if (attribute->value[i] == '\"')
{
escaped_value[j++] = '\\';
escaped_value[j++] = '\"';
}
else if (attribute->value[i] == '\\')
{
escaped_value[j++] = '\\';
escaped_value[j++] = '\\';
}
else
{
escaped_value[j++] = attribute->value[i];
}
}
name_len = strlen (attribute->name);
buffer_len = name_len + 2 + j + 2;
buffer = g_new (gchar, buffer_len);
memcpy (buffer, attribute->name, name_len);
buffer[name_len] = '=';
buffer[name_len + 1] = '\"';
memcpy (&buffer[name_len + 2], escaped_value, j);
buffer[name_len + 2 + j] = '\"';
buffer[name_len + 2 + j + 1] = ' ';
g_free (escaped_value);
*bufferp = buffer;
*lengthp = buffer_len;
}
else
{
buffer = g_strdup_printf ("%s ", attribute->name);
*bufferp = buffer;
*lengthp = strlen (buffer);
}
}
void
elf_write_format (gchar** bufferp, gint* lengthp,
const gchar* name, const gchar* format, ...)
{
va_list args;
ElfNode* node;
va_start (args, format);
node = elf_new_vformat (name, format, args);
va_end (args);
if (!node)
{
*bufferp = NULL;
*lengthp = 0;
return;
}
elf_write (node, bufferp, lengthp);
elf_delete (node);
}
void
elf_write_list_path (GList* list, const gchar* path)
{
FILE* file;
gchar* buf;
gint len;
g_return_if_fail (path);
file = fopen (path, "w");
if (!file) return;
elf_write_list (list, &buf, &len);
if (buf)
{
fwrite (buf, len, 1, file);
g_free (buf);
}
fclose (file);
}
syntax highlighted by Code2HTML, v. 0.9.1