/* 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 #include #include #include #include #include #include #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); }