summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--base.h81
-rw-r--r--data.c163
-rw-r--r--main.c458
-rw-r--r--memory.c104
-rw-r--r--ui.glade777
-rw-r--r--unicode.h1
6 files changed, 1023 insertions, 561 deletions
diff --git a/base.h b/base.h
new file mode 100644
index 0000000..a533ce0
--- /dev/null
+++ b/base.h
@@ -0,0 +1,81 @@
+// types and stuff needed everywhere
+
+#include <gtk/gtk.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <assert.h>
+#include <ctype.h>
+#include <wctype.h>
+
+typedef pid_t PID;
+typedef uint64_t Address;
+#define SCNxADDR SCNx64
+#define PRIdADDR PRId64
+#define PRIxADDR PRIx64
+
+typedef enum {
+ TYPE_U8,
+ TYPE_S8,
+ TYPE_U16,
+ TYPE_S16,
+ TYPE_U32,
+ TYPE_S32,
+ TYPE_U64,
+ TYPE_S64,
+ TYPE_ASCII,
+ TYPE_UTF16,
+ TYPE_UTF32,
+ TYPE_F32,
+ TYPE_F64
+} DataType;
+
+typedef enum {
+ SEARCH_ENTER_VALUE,
+ SEARCH_SAME_DIFFERENT
+} SearchType;
+
+// a memory map
+typedef struct {
+ Address lo, size;
+} Map;
+
+typedef struct {
+ GtkWindow *window;
+ GtkBuilder *builder;
+ bool stop_while_accessing_memory;
+ long editing_memory; // index of memory value being edited, or -1 if none is
+ PID pid;
+ Address total_memory; // total amount of memory used by process, in bytes
+ Map *maps;
+ Address memory_view_address;
+ uint32_t memory_view_entries; // # of entries to show
+ unsigned nmaps;
+ DataType data_type;
+ SearchType search_type;
+} State;
+
+static void display_dialog_box_nofmt(State *state, GtkMessageType type, char const *message) {
+ GtkWidget *box = gtk_message_dialog_new(state->window,
+ GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
+ type, GTK_BUTTONS_OK, "%s", message);
+ // make sure dialog box is closed when OK is clicked.
+ g_signal_connect_swapped(box, "response", G_CALLBACK(gtk_widget_destroy), box);
+ gtk_widget_show_all(box);
+}
+
+// this is a macro so we get -Wformat warnings
+#define display_dialog_box(state, type, fmt, ...) do { \
+ char _buf[1024]; \
+ snprintf(_buf, sizeof _buf, fmt, __VA_ARGS__); \
+ display_dialog_box_nofmt(state, type, _buf); \
+} while (0)
+#define display_error(state, fmt, ...) display_dialog_box(state, GTK_MESSAGE_ERROR, fmt, __VA_ARGS__)
+#define display_info(state, fmt, ...) display_dialog_box(state, GTK_MESSAGE_INFO, fmt, __VA_ARGS__)
+#define display_info_nofmt(state, message) display_dialog_box_nofmt(state, GTK_MESSAGE_INFO, message)
+
+
diff --git a/data.c b/data.c
new file mode 100644
index 0000000..6afb100
--- /dev/null
+++ b/data.c
@@ -0,0 +1,163 @@
+// stuff for dealing with various data types.
+
+static DataType data_type_from_name(char const *name) {
+ switch (name[0]) {
+ case 'u':
+ switch (atoi(&name[1])) {
+ case 8: return TYPE_U8;
+ case 16: return TYPE_U16;
+ case 32: return TYPE_U32;
+ case 64: return TYPE_U64;
+ }
+ if (strncmp(name, "utf", 3) == 0) {
+ switch (atoi(&name[3])) {
+ case 16: return TYPE_UTF16;
+ case 32: return TYPE_UTF32;
+ }
+ }
+ break;
+ case 's':
+ switch (atoi(&name[1])) {
+ case 8: return TYPE_S8;
+ case 16: return TYPE_S16;
+ case 32: return TYPE_S32;
+ case 64: return TYPE_S64;
+ }
+ break;
+ case 'f':
+ switch (atoi(&name[1])) {
+ case 32: return TYPE_F32;
+ case 64: return TYPE_F64;
+ }
+ break;
+ case 'a':
+ if (strcmp(name, "ascii") == 0)
+ return TYPE_ASCII;
+ break;
+ }
+ assert(0);
+ return TYPE_U8;
+}
+
+static size_t data_type_size(DataType type) {
+ switch (type) {
+ case TYPE_U8:
+ case TYPE_S8:
+ case TYPE_ASCII:
+ return 1;
+ case TYPE_U16:
+ case TYPE_S16:
+ case TYPE_UTF16:
+ return 2;
+ case TYPE_U32:
+ case TYPE_S32:
+ case TYPE_F32:
+ case TYPE_UTF32:
+ return 4;
+ case TYPE_U64:
+ case TYPE_S64:
+ case TYPE_F64:
+ return 8;
+ }
+ return (size_t)-1;
+}
+
+// set str to "a" for 'a', "\\n" for '\n', "\\xff" for (wchar_t)255, etc.
+static void char_to_str(uint32_t c, char *str, size_t str_size) {
+ if (c <= WINT_MAX && iswgraph((wint_t)c)) {
+ snprintf(str, str_size, "%lc", (wint_t)c);
+ } else {
+ switch (c) {
+ case ' ': snprintf(str, str_size, "(space)"); break;
+ case '\n': snprintf(str, str_size, "\\n"); break;
+ case '\t': snprintf(str, str_size, "\\t"); break;
+ case '\r': snprintf(str, str_size, "\\r"); break;
+ case '\v': snprintf(str, str_size, "\\v"); break;
+ case '\0': snprintf(str, str_size, "\\0"); break;
+ default:
+ if (c < 256)
+ snprintf(str, str_size, "\\x%02x", (unsigned)c);
+ else
+ snprintf(str, str_size, "\\x%05lx", (unsigned long)c);
+ }
+ }
+}
+
+static bool char_from_str(char const *str, uint32_t *c) {
+ if (str[0] == '\0') return false;
+ if (str[0] == '\\') {
+ switch (str[1]) {
+ case 'n': *c = '\n'; return str[2] == '\0';
+ case 't': *c = '\t'; return str[2] == '\0';
+ case 'r': *c = '\r'; return str[2] == '\0';
+ case 'v': *c = '\v'; return str[2] == '\0';
+ case '0': *c = '\0'; return str[2] == '\0';
+ case 'x': {
+ unsigned long v = 0;
+ int w = 0;
+ if (sscanf(&str[2], "%lx%n", &v, &w) != 1 ||
+ (size_t)w != strlen(&str[2]) || v > UINT32_MAX)
+ return false;
+ *c = (uint32_t)v;
+ return true;
+ }
+ }
+ }
+ return unicode_utf8_to_utf32(c, str, strlen(str)) == strlen(str);
+}
+
+static void data_to_str(void const *value, DataType type, char *str, size_t str_size) {
+ switch (type) {
+ case TYPE_U8: snprintf(str, str_size, "%" PRIu8, *(uint8_t *)value); break;
+ case TYPE_U16: snprintf(str, str_size, "%" PRIu16, *(uint16_t *)value); break;
+ case TYPE_U32: snprintf(str, str_size, "%" PRIu32, *(uint32_t *)value); break;
+ case TYPE_U64: snprintf(str, str_size, "%" PRIu64, *(uint64_t *)value); break;
+ case TYPE_S8: snprintf(str, str_size, "%" PRId8, *(int8_t *)value); break;
+ case TYPE_S16: snprintf(str, str_size, "%" PRId16, *(int16_t *)value); break;
+ case TYPE_S32: snprintf(str, str_size, "%" PRId32, *(int32_t *)value); break;
+ case TYPE_S64: snprintf(str, str_size, "%" PRId64, *(int64_t *)value); break;
+ case TYPE_F32: snprintf(str, str_size, "%g", *(float *)value); break;
+ case TYPE_F64: snprintf(str, str_size, "%g", *(double *)value); break;
+ case TYPE_UTF16: char_to_str(*(uint16_t *)value, str, str_size); break;
+ case TYPE_UTF32: char_to_str(*(uint32_t *)value, str, str_size); break;
+ case TYPE_ASCII:
+ char_to_str((uint8_t)*(char *)value, str, str_size);
+ break;
+
+ }
+}
+
+// returns true on success, false if str is not a well-formatted value
+static bool data_from_str(char const *str, DataType type, void *value) {
+ int len = (int)strlen(str);
+ int w = 0;
+ uint32_t c = 0;
+ switch (type) {
+ case TYPE_U8: return sscanf(str, "%" SCNu8 "%n", (uint8_t *)value, &w) == 1 && w == len;
+ case TYPE_S8: return sscanf(str, "%" SCNd8 "%n", ( int8_t *)value, &w) == 1 && w == len;
+ case TYPE_U16: return sscanf(str, "%" SCNu16 "%n", (uint16_t *)value, &w) == 1 && w == len;
+ case TYPE_S16: return sscanf(str, "%" SCNd16 "%n", ( int16_t *)value, &w) == 1 && w == len;
+ case TYPE_U32: return sscanf(str, "%" SCNu32 "%n", (uint32_t *)value, &w) == 1 && w == len;
+ case TYPE_S32: return sscanf(str, "%" SCNd32 "%n", ( int32_t *)value, &w) == 1 && w == len;
+ case TYPE_U64: return sscanf(str, "%" SCNu64 "%n", (uint64_t *)value, &w) == 1 && w == len;
+ case TYPE_S64: return sscanf(str, "%" SCNd64 "%n", ( int64_t *)value, &w) == 1 && w == len;
+ case TYPE_F32: return sscanf(str, "%f%n", (float *)value, &w) == 1 && w == len;
+ case TYPE_F64: return sscanf(str, "%lf%n", (double *)value, &w) == 1 && w == len;
+ case TYPE_ASCII:
+ if (!char_from_str(str, &c)) return false;
+ if (c > 127) return false;
+ *(uint8_t *)value = (uint8_t)c;
+ return true;
+ case TYPE_UTF16:
+ if (!char_from_str(str, &c)) return false;
+ if (c > 65535) return false;
+ *(uint16_t *)value = (uint16_t)c;
+ return true;
+ case TYPE_UTF32:
+ if (!char_from_str(str, &c)) return false;
+ *(uint32_t *)value = c;
+ return true;
+ }
+ assert(0);
+ return false;
+}
diff --git a/main.c b/main.c
index 6052a26..bc304e1 100644
--- a/main.c
+++ b/main.c
@@ -1,341 +1,16 @@
-#include <gtk/gtk.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <inttypes.h>
-#include <unistd.h>
-#include <assert.h>
-#include <ctype.h>
-#include <wctype.h>
-
-typedef pid_t PID;
-typedef uint64_t Address;
-#define SCNxADDR SCNx64
-#define PRIdADDR PRId64
-#define PRIxADDR PRIx64
-
+#include "base.h"
#include "unicode.h"
-
-// a memory map
-typedef struct {
- Address lo, size;
-} Map;
-
-typedef enum {
- TYPE_U8,
- TYPE_S8,
- TYPE_U16,
- TYPE_S16,
- TYPE_U32,
- TYPE_S32,
- TYPE_U64,
- TYPE_S64,
- TYPE_ASCII,
- TYPE_UTF16,
- TYPE_UTF32,
- TYPE_F32,
- TYPE_F64
-} DataType;
-
-static DataType data_type_from_name(char const *name) {
- switch (name[0]) {
- case 'u':
- switch (atoi(&name[1])) {
- case 8: return TYPE_U8;
- case 16: return TYPE_U16;
- case 32: return TYPE_U32;
- case 64: return TYPE_U64;
- }
- if (strncmp(name, "utf", 3) == 0) {
- switch (atoi(&name[3])) {
- case 16: return TYPE_UTF16;
- case 32: return TYPE_UTF32;
- }
- }
- break;
- case 's':
- switch (atoi(&name[1])) {
- case 8: return TYPE_S8;
- case 16: return TYPE_S16;
- case 32: return TYPE_S32;
- case 64: return TYPE_S64;
- }
- break;
- case 'f':
- switch (atoi(&name[1])) {
- case 32: return TYPE_F32;
- case 64: return TYPE_F64;
- }
- break;
- case 'a':
- if (strcmp(name, "ascii") == 0)
- return TYPE_ASCII;
- break;
- }
- assert(0);
- return TYPE_U8;
-}
-
-static size_t data_type_size(DataType type) {
- switch (type) {
- case TYPE_U8:
- case TYPE_S8:
- case TYPE_ASCII:
- return 1;
- case TYPE_U16:
- case TYPE_S16:
- case TYPE_UTF16:
- return 2;
- case TYPE_U32:
- case TYPE_S32:
- case TYPE_F32:
- case TYPE_UTF32:
- return 4;
- case TYPE_U64:
- case TYPE_S64:
- case TYPE_F64:
- return 8;
- }
- return (size_t)-1;
-}
-
-// set str to "a" for 'a', "\\n" for '\n', "\\xff" for (wchar_t)255, etc.
-static void char_to_str(uint32_t c, char *str, size_t str_size) {
- if (c <= WINT_MAX && iswgraph((wint_t)c)) {
- snprintf(str, str_size, "%lc", (wint_t)c);
- } else {
- switch (c) {
- case ' ': snprintf(str, str_size, "(space)"); break;
- case '\n': snprintf(str, str_size, "\\n"); break;
- case '\t': snprintf(str, str_size, "\\t"); break;
- case '\r': snprintf(str, str_size, "\\r"); break;
- case '\v': snprintf(str, str_size, "\\v"); break;
- case '\0': snprintf(str, str_size, "\\0"); break;
- default:
- if (c < 256)
- snprintf(str, str_size, "\\x%02x", (unsigned)c);
- else
- snprintf(str, str_size, "\\x%05lx", (unsigned long)c);
- }
- }
-}
-
-static bool char_from_str(char const *str, uint32_t *c) {
- if (str[0] == '\0') return false;
- if (str[0] == '\\') {
- switch (str[1]) {
- case 'n': *c = '\n'; return str[2] == '\0';
- case 't': *c = '\t'; return str[2] == '\0';
- case 'r': *c = '\r'; return str[2] == '\0';
- case 'v': *c = '\v'; return str[2] == '\0';
- case '0': *c = '\0'; return str[2] == '\0';
- case 'x': {
- unsigned long v = 0;
- int w = 0;
- if (sscanf(&str[2], "%lx%n", &v, &w) != 1 ||
- (size_t)w != strlen(&str[2]) || v > UINT32_MAX)
- return false;
- *c = (uint32_t)v;
- return true;
- }
- }
- }
- return unicode_utf8_to_utf32(c, str, strlen(str)) == strlen(str);
-}
-
-static void data_to_str(void const *value, DataType type, char *str, size_t str_size) {
- switch (type) {
- case TYPE_U8: snprintf(str, str_size, "%" PRIu8, *(uint8_t *)value); break;
- case TYPE_U16: snprintf(str, str_size, "%" PRIu16, *(uint16_t *)value); break;
- case TYPE_U32: snprintf(str, str_size, "%" PRIu32, *(uint32_t *)value); break;
- case TYPE_U64: snprintf(str, str_size, "%" PRIu64, *(uint64_t *)value); break;
- case TYPE_S8: snprintf(str, str_size, "%" PRId8, *(int8_t *)value); break;
- case TYPE_S16: snprintf(str, str_size, "%" PRId16, *(int16_t *)value); break;
- case TYPE_S32: snprintf(str, str_size, "%" PRId32, *(int32_t *)value); break;
- case TYPE_S64: snprintf(str, str_size, "%" PRId64, *(int64_t *)value); break;
- case TYPE_F32: snprintf(str, str_size, "%g", *(float *)value); break;
- case TYPE_F64: snprintf(str, str_size, "%g", *(double *)value); break;
- case TYPE_UTF16: char_to_str(*(uint16_t *)value, str, str_size); break;
- case TYPE_UTF32: char_to_str(*(uint32_t *)value, str, str_size); break;
- case TYPE_ASCII:
- char_to_str((uint8_t)*(char *)value, str, str_size);
- break;
-
- }
-}
-
-// returns true on success, false if str is not a well-formatted value
-static bool data_from_str(char const *str, DataType type, void *value) {
- int len = (int)strlen(str);
- int w = 0;
- uint32_t c = 0;
- switch (type) {
- case TYPE_U8: return sscanf(str, "%" SCNu8 "%n", (uint8_t *)value, &w) == 1 && w == len;
- case TYPE_S8: return sscanf(str, "%" SCNd8 "%n", ( int8_t *)value, &w) == 1 && w == len;
- case TYPE_U16: return sscanf(str, "%" SCNu16 "%n", (uint16_t *)value, &w) == 1 && w == len;
- case TYPE_S16: return sscanf(str, "%" SCNd16 "%n", ( int16_t *)value, &w) == 1 && w == len;
- case TYPE_U32: return sscanf(str, "%" SCNu32 "%n", (uint32_t *)value, &w) == 1 && w == len;
- case TYPE_S32: return sscanf(str, "%" SCNd32 "%n", ( int32_t *)value, &w) == 1 && w == len;
- case TYPE_U64: return sscanf(str, "%" SCNu64 "%n", (uint64_t *)value, &w) == 1 && w == len;
- case TYPE_S64: return sscanf(str, "%" SCNd64 "%n", ( int64_t *)value, &w) == 1 && w == len;
- case TYPE_F32: return sscanf(str, "%f%n", (float *)value, &w) == 1 && w == len;
- case TYPE_F64: return sscanf(str, "%lf%n", (double *)value, &w) == 1 && w == len;
- case TYPE_ASCII:
- if (!char_from_str(str, &c)) return false;
- if (c > 127) return false;
- *(uint8_t *)value = (uint8_t)c;
- return true;
- case TYPE_UTF16:
- if (!char_from_str(str, &c)) return false;
- if (c > 65535) return false;
- *(uint16_t *)value = (uint16_t)c;
- return true;
- case TYPE_UTF32:
- if (!char_from_str(str, &c)) return false;
- *(uint32_t *)value = c;
- return true;
+#include "data.c"
+#include "memory.c"
+
+static SearchType search_type_from_str(char const *str) {
+ if (strcmp(str, "enter-value") == 0) {
+ return SEARCH_ENTER_VALUE;
+ } else if (strcmp(str, "same-different") == 0) {
+ return SEARCH_SAME_DIFFERENT;
}
assert(0);
- return false;
-}
-
-typedef struct {
- GtkWindow *window;
- GtkBuilder *builder;
- bool stop_while_accessing_memory;
- long editing_memory; // index of memory value being edited, or -1 if none is
- PID pid;
- Map *maps;
- Address memory_view_address;
- uint32_t memory_view_entries; // # of entries to show
- unsigned nmaps;
- DataType data_type;
-} State;
-
-static void display_dialog_box_nofmt(State *state, GtkMessageType type, char const *message) {
- GtkWidget *box = gtk_message_dialog_new(state->window,
- GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
- type, GTK_BUTTONS_OK, "%s", message);
- // make sure dialog box is closed when OK is clicked.
- g_signal_connect_swapped(box, "response", G_CALLBACK(gtk_widget_destroy), box);
- gtk_widget_show_all(box);
-}
-
-// this is a macro so we get -Wformat warnings
-#define display_dialog_box(state, type, fmt, ...) do { \
- char _buf[1024]; \
- snprintf(_buf, sizeof _buf, fmt, __VA_ARGS__); \
- display_dialog_box_nofmt(state, type, _buf); \
-} while (0)
-#define display_error(state, fmt, ...) display_dialog_box(state, GTK_MESSAGE_ERROR, fmt, __VA_ARGS__)
-#define display_info(state, fmt, ...) display_dialog_box(state, GTK_MESSAGE_INFO, fmt, __VA_ARGS__)
-#define display_info_nofmt(state, message) display_dialog_box_nofmt(state, GTK_MESSAGE_INFO, message)
-
-
-
-static void close_process(State *state, char const *reason) {
- GtkBuilder *builder = state->builder;
- free(state->maps); state->maps = NULL;
- state->nmaps = 0;
- state->pid = 0;
- {
- GtkListStore *store = GTK_LIST_STORE(gtk_builder_get_object(builder, "memory"));
- gtk_list_store_clear(store);
- }
- if (reason)
- display_info(state, "Can't access process anymore: %s", reason);
- else
- display_info_nofmt(state, "Can't access process anymore.");
-}
-
-// don't use this function; use one of the ones below
-static int memory_open(State *state, int flags) {
- if (state->pid) {
- if (state->stop_while_accessing_memory) {
- if (kill(state->pid, SIGSTOP) == -1) {
- close_process(state, strerror(errno));
- return 0;
- }
- }
- char name[64];
- sprintf(name, "/proc/%lld/mem", (long long)state->pid);
- int fd = open(name, flags);
- if (fd == -1) {
- close_process(state, strerror(errno));
- return 0;
- }
- return fd;
- }
- return 0;
-}
-
-static void memory_close(State *state, int fd) {
- if (state->stop_while_accessing_memory) {
- kill(state->pid, SIGCONT);
- }
- if (fd) close(fd);
-}
-
-
-// get a file descriptor for reading memory from the process
-// returns 0 on failure
-static int memory_reader_open(State *state) {
- return memory_open(state, O_RDONLY);
-}
-
-static void memory_reader_close(State *state, int reader) {
- memory_close(state, reader);
-}
-
-// like memory_reader_open, but for writing to memory
-static int memory_writer_open(State *state) {
- return memory_open(state, O_WRONLY);
-}
-
-static void memory_writer_close(State *state, int writer) {
- memory_close(state, writer);
-}
-
-static uint8_t memory_read_byte(int reader, Address addr) {
- lseek(reader, (off_t)addr, SEEK_SET);
- uint8_t byte = 0;
- read(reader, &byte, 1);
- return byte;
-}
-
-// returns number of bytes successfully read
-static Address memory_read_bytes(int reader, Address addr, uint8_t *memory, Address nbytes) {
- lseek(reader, (off_t)addr, SEEK_SET);
- Address idx = 0;
- while (idx < nbytes) {
- ssize_t n = read(reader, &memory[idx], (size_t)(nbytes - idx));
- if (n <= 0) break;
- idx += (Address)n;
- }
- return idx;
-}
-
-// returns # of bytes written (so either 0 or 1)
-static Address memory_write_byte(int writer, Address addr, uint8_t byte) {
- lseek(writer, (off_t)addr, SEEK_SET);
- if (write(writer, &byte, 1) == 1)
- return 1;
- else
- return 0;
-}
-
-// returns # of bytes written
-static Address memory_write_bytes(int writer, Address addr, uint8_t const *bytes, Address nbytes) {
- lseek(writer, (off_t)addr, SEEK_SET);
- Address idx = 0;
- while (idx < nbytes) {
- ssize_t n = write(writer, &bytes[idx], (size_t)(nbytes - idx));
- if (n < 0) break;
- idx += (Address)n;
- }
- return idx;
+ return 0xff;
}
// pass config_potentially_changed = false if there definitely hasn't been an update to the target address
@@ -383,7 +58,34 @@ static void update_memory_view(State *state, bool config_potentially_changed) {
}
}
-G_MODULE_EXPORT void update_configuration(GtkWidget *widget, gpointer user_data) {
+static void bytes_to_text(uint64_t nbytes, char *out, size_t out_size) {
+ if (nbytes == 0)
+ snprintf(out, out_size, "0");
+ else if (nbytes < (1ul<<10))
+ snprintf(out, out_size, "%uB", (unsigned)nbytes);
+ else if (nbytes < (1ul<<20))
+ snprintf(out, out_size, "%.1fKB", (double)nbytes / (double)(1ul<<10));
+ else if (nbytes < (1ul<<30))
+ snprintf(out, out_size, "%.1fMB", (double)nbytes / (double)(1ul<<20));
+ else
+ snprintf(out, out_size, "%.1fGB", (double)nbytes / (double)(1ul<<30));
+}
+
+// returns the name of the selected GtkRadioButton in the group whose leader's name is group_name
+// (or NULL if none is, which shouldn't usually happen)
+static char const *radio_group_get_selected(State *state, char const *group_name) {
+ GtkRadioButton *leader = GTK_RADIO_BUTTON(gtk_builder_get_object(state->builder, group_name));
+ for (GSList *l = gtk_radio_button_get_group(leader);
+ l; l = l->next) {
+ GtkToggleButton *button = l->data;
+ if (gtk_toggle_button_get_active(button)) {
+ return gtk_widget_get_name(GTK_WIDGET(button));
+ }
+ }
+ return NULL;
+}
+
+G_MODULE_EXPORT void update_configuration(GtkWidget *_widget, gpointer user_data) {
State *state = user_data;
GtkBuilder *builder = state->builder;
state->stop_while_accessing_memory = gtk_toggle_button_get_active(
@@ -404,19 +106,48 @@ G_MODULE_EXPORT void update_configuration(GtkWidget *widget, gpointer user_data)
state->memory_view_address = address;
update_memview = true;
}
- GtkRadioButton *data_type_u8 = GTK_RADIO_BUTTON(
- gtk_builder_get_object(builder, "type-u8"));
- for (GSList *l = gtk_radio_button_get_group(data_type_u8);
- l; l = l->next) {
- GtkToggleButton *button = l->data;
- if (gtk_toggle_button_get_active(button)) {
- char const *type_name = gtk_widget_get_name(GTK_WIDGET(button));
- DataType type = data_type_from_name(type_name);
- if (state->data_type != type) {
- state->data_type = type;
- update_memview = true;
- }
+ char const *data_type_str = radio_group_get_selected(state, "type-u8");
+ DataType data_type = data_type_from_name(data_type_str);
+ if (state->data_type != data_type) {
+ state->data_type = data_type;
+ update_memview = true;
+ }
+
+ // update memory/disk estimates for search
+ GtkLabel *memory_label = GTK_LABEL(gtk_builder_get_object(builder, "required-memory"));
+ GtkLabel *disk_label = GTK_LABEL(gtk_builder_get_object(builder, "required-disk"));
+ char const *search_type_str = radio_group_get_selected(state, "enter-value");
+ SearchType search_type = search_type_from_str(search_type_str);
+ state->search_type = search_type;
+ if (state->pid) {
+ size_t item_size = data_type_size(data_type);
+ Address total_memory = state->total_memory;
+ Address total_items = total_memory / item_size;
+ Address memory_usage = total_items / 8; // 1 bit per item
+ {
+ char text[32];
+ bytes_to_text(memory_usage, text, sizeof text);
+ gtk_label_set_text(memory_label, text);
+ }
+ Address disk_usage = 0;
+ switch (search_type) {
+ case SEARCH_ENTER_VALUE:
+ // no disk space needed
+ break;
+ case SEARCH_SAME_DIFFERENT:
+ // we need to store all of memory on disk for this kind of search
+ disk_usage = total_memory;
+ break;
}
+
+ {
+ char text[32];
+ bytes_to_text(disk_usage, text, sizeof text);
+ gtk_label_set_text(disk_label, text);
+ }
+ } else {
+ gtk_label_set_text(memory_label, "N/A");
+ gtk_label_set_text(disk_label, "N/A");
}
if (update_memview) {
@@ -439,6 +170,7 @@ static void update_maps(State *state) {
rewind(maps_file);
Map *maps = state->maps = calloc(capacity, sizeof *maps);
unsigned nmaps = 0;
+ state->total_memory = 0;
if (maps) {
while (fgets(line, sizeof line, maps_file)) {
Address addr_lo, addr_hi;
@@ -448,6 +180,7 @@ static void update_maps(State *state) {
Map *map = &maps[nmaps++];
map->lo = addr_lo;
map->size = addr_hi - addr_lo;
+ state->total_memory += map->size;
}
}
}
@@ -461,7 +194,7 @@ static void update_maps(State *state) {
}
// the user entered a PID.
-G_MODULE_EXPORT void select_pid(GtkButton *button, gpointer user_data) {
+G_MODULE_EXPORT void select_pid(GtkButton *_button, gpointer user_data) {
State *state = user_data;
GtkBuilder *builder = state->builder;
GtkEntry *pid = GTK_ENTRY(gtk_builder_get_object(builder, "pid"));
@@ -491,6 +224,7 @@ G_MODULE_EXPORT void select_pid(GtkButton *button, gpointer user_data) {
state->pid = (PID)pid_number;
close(dir);
update_maps(state);
+ update_configuration(NULL, state); // we need to do this to update the search resource usage estimates
if (state->nmaps) {
// display whatever's in the first mapping
Map *first_map = &state->maps[0];
@@ -502,7 +236,7 @@ G_MODULE_EXPORT void select_pid(GtkButton *button, gpointer user_data) {
}
// a value in memory was edited
-G_MODULE_EXPORT void memory_edited(GtkCellRendererText *renderer, char *path, char *new_text, gpointer user_data) {
+G_MODULE_EXPORT void memory_edited(GtkCellRendererText *_renderer, char *path, char *new_text, gpointer user_data) {
State *state = user_data;
GtkBuilder *builder = state->builder;
DataType data_type = state->data_type;
@@ -514,12 +248,14 @@ G_MODULE_EXPORT void memory_edited(GtkCellRendererText *renderer, char *path, ch
if (data_from_str(new_text, data_type, &value)) {
int writer = memory_writer_open(state);
if (writer) {
+ // write the value
bool success = memory_write_bytes(writer, addr, (uint8_t const *)&value, item_size) == item_size;
memory_writer_close(state, writer);
if (success) {
GtkListStore *store = GTK_LIST_STORE(gtk_builder_get_object(builder, "memory"));
GtkTreeIter iter;
char value_str[32];
+ // convert back to a string (so new_text = "0.10" becomes value_str = "0.1", etc.)
data_to_str(&value, data_type, value_str, sizeof value_str);
gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(store), &iter, path);
gtk_list_store_set(store, &iter, 2, value_str, -1);
@@ -529,21 +265,37 @@ G_MODULE_EXPORT void memory_edited(GtkCellRendererText *renderer, char *path, ch
}
}
-G_MODULE_EXPORT void refresh_memory(GtkWidget *widget, gpointer user_data) {
+G_MODULE_EXPORT void refresh_memory(GtkWidget *_widget, gpointer user_data) {
State *state = user_data;
update_memory_view(state, true);
}
-G_MODULE_EXPORT void memory_editing_started(GtkCellRenderer *renderer, GtkCellEditable *editable, char *path, gpointer user_data) {
+G_MODULE_EXPORT void memory_editing_started(GtkCellRenderer *_renderer, GtkCellEditable *editable, char *path, gpointer user_data) {
State *state = user_data;
state->editing_memory = atol(path);
}
-G_MODULE_EXPORT void memory_editing_canceled(GtkCellRenderer *renderer, gpointer user_data) {
+G_MODULE_EXPORT void memory_editing_canceled(GtkCellRenderer *_renderer, gpointer user_data) {
State *state = user_data;
state->editing_memory = -1;
}
+G_MODULE_EXPORT void search_start(GtkWidget *_widget, gpointer user_data) {
+ State *state = user_data;
+ GtkBuilder *builder = state->builder;
+ SearchType search_type = state->search_type;
+ gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder, "pre-search")));
+ gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder, "search-common")));
+ switch (search_type) {
+ case SEARCH_ENTER_VALUE:
+ gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder, "search-enter-value")));
+ break;
+ case SEARCH_SAME_DIFFERENT:
+ gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder, "search-same-different")));
+ break;
+ }
+}
+
// this function is run once per frame
static gboolean frame_callback(gpointer user_data) {
State *state = user_data;
diff --git a/memory.c b/memory.c
new file mode 100644
index 0000000..37b9983
--- /dev/null
+++ b/memory.c
@@ -0,0 +1,104 @@
+// low-level stuff for reading/writing the memory of another process
+
+static void close_process(State *state, char const *reason) {
+ GtkBuilder *builder = state->builder;
+ free(state->maps); state->maps = NULL;
+ state->nmaps = 0;
+ state->pid = 0;
+ {
+ GtkListStore *store = GTK_LIST_STORE(gtk_builder_get_object(builder, "memory"));
+ gtk_list_store_clear(store);
+ }
+ if (reason)
+ display_info(state, "Can't access process anymore: %s", reason);
+ else
+ display_info_nofmt(state, "Can't access process anymore.");
+}
+
+// don't use this function; use one of the ones below
+static int memory_open(State *state, int flags) {
+ if (state->pid) {
+ if (state->stop_while_accessing_memory) {
+ if (kill(state->pid, SIGSTOP) == -1) {
+ close_process(state, strerror(errno));
+ return 0;
+ }
+ }
+ char name[64];
+ sprintf(name, "/proc/%lld/mem", (long long)state->pid);
+ int fd = open(name, flags);
+ if (fd == -1) {
+ close_process(state, strerror(errno));
+ return 0;
+ }
+ return fd;
+ }
+ return 0;
+}
+
+static void memory_close(State *state, int fd) {
+ if (state->stop_while_accessing_memory) {
+ kill(state->pid, SIGCONT);
+ }
+ if (fd) close(fd);
+}
+
+
+// get a file descriptor for reading memory from the process
+// returns 0 on failure
+static int memory_reader_open(State *state) {
+ return memory_open(state, O_RDONLY);
+}
+
+static void memory_reader_close(State *state, int reader) {
+ memory_close(state, reader);
+}
+
+// like memory_reader_open, but for writing to memory
+static int memory_writer_open(State *state) {
+ return memory_open(state, O_WRONLY);
+}
+
+static void memory_writer_close(State *state, int writer) {
+ memory_close(state, writer);
+}
+
+static uint8_t memory_read_byte(int reader, Address addr) {
+ lseek(reader, (off_t)addr, SEEK_SET);
+ uint8_t byte = 0;
+ read(reader, &byte, 1);
+ return byte;
+}
+
+// returns number of bytes successfully read
+static Address memory_read_bytes(int reader, Address addr, uint8_t *memory, Address nbytes) {
+ lseek(reader, (off_t)addr, SEEK_SET);
+ Address idx = 0;
+ while (idx < nbytes) {
+ ssize_t n = read(reader, &memory[idx], (size_t)(nbytes - idx));
+ if (n <= 0) break;
+ idx += (Address)n;
+ }
+ return idx;
+}
+
+// returns # of bytes written (so either 0 or 1)
+static Address memory_write_byte(int writer, Address addr, uint8_t byte) {
+ lseek(writer, (off_t)addr, SEEK_SET);
+ if (write(writer, &byte, 1) == 1)
+ return 1;
+ else
+ return 0;
+}
+
+// returns # of bytes written
+static Address memory_write_bytes(int writer, Address addr, uint8_t const *bytes, Address nbytes) {
+ lseek(writer, (off_t)addr, SEEK_SET);
+ Address idx = 0;
+ while (idx < nbytes) {
+ ssize_t n = write(writer, &bytes[idx], (size_t)(nbytes - idx));
+ if (n < 0) break;
+ idx += (Address)n;
+ }
+ return idx;
+}
diff --git a/ui.glade b/ui.glade
index 94d37ae..515755f 100644
--- a/ui.glade
+++ b/ui.glade
@@ -273,7 +273,7 @@
</packing>
</child>
<child>
- <object class="GtkLabel">
+ <object class="GtkLabel" id="data-type-label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">How to interpret the process' memory.</property>
@@ -287,226 +287,587 @@
</packing>
</child>
<child>
- <object class="GtkRadioButton" id="type-u8">
- <property name="label" translatable="yes">8-bit unsigned integer</property>
- <property name="name">u8</property>
+ <object class="GtkBox" id="data-type-box">
<property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">False</property>
- <property name="active">True</property>
- <property name="draw-indicator">True</property>
- <signal name="toggled" handler="update_configuration" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">3</property>
- </packing>
- </child>
- <child>
- <object class="GtkRadioButton" id="type-s8">
- <property name="label" translatable="yes">8-bit signed integer</property>
- <property name="name">s8</property>
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">False</property>
- <property name="active">True</property>
- <property name="draw-indicator">True</property>
- <property name="group">type-u8</property>
- <signal name="toggled" handler="update_configuration" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">4</property>
- </packing>
- </child>
- <child>
- <object class="GtkRadioButton" id="type-u16">
- <property name="label" translatable="yes">16-bit unsigned integer</property>
- <property name="name">u16</property>
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">False</property>
- <property name="active">True</property>
- <property name="draw-indicator">True</property>
- <property name="group">type-u8</property>
- <signal name="toggled" handler="update_configuration" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">5</property>
- </packing>
- </child>
- <child>
- <object class="GtkRadioButton" id="type-s16">
- <property name="label" translatable="yes">16-bit signed integer</property>
- <property name="name">s16</property>
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">False</property>
- <property name="draw-indicator">True</property>
- <property name="group">type-u8</property>
- <signal name="toggled" handler="update_configuration" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">6</property>
- </packing>
- </child>
- <child>
- <object class="GtkRadioButton" id="type-u32">
- <property name="label" translatable="yes">32-bit unsigned integer</property>
- <property name="name">u32</property>
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">False</property>
- <property name="draw-indicator">True</property>
- <property name="group">type-u8</property>
- <signal name="toggled" handler="update_configuration" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">7</property>
- </packing>
- </child>
- <child>
- <object class="GtkRadioButton" id="type-s32">
- <property name="label" translatable="yes">32-bit signed integer</property>
- <property name="name">s32</property>
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">False</property>
- <property name="draw-indicator">True</property>
- <property name="group">type-u8</property>
- <signal name="toggled" handler="update_configuration" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">8</property>
- </packing>
- </child>
- <child>
- <object class="GtkRadioButton" id="type-u64">
- <property name="label" translatable="yes">64-bit unsigned integer</property>
- <property name="name">u64</property>
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">False</property>
- <property name="draw-indicator">True</property>
- <property name="group">type-u8</property>
- <signal name="toggled" handler="update_configuration" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">9</property>
- </packing>
- </child>
- <child>
- <object class="GtkRadioButton" id="type-s64">
- <property name="label" translatable="yes">64-bit signed integer</property>
- <property name="name">s64</property>
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">False</property>
- <property name="draw-indicator">True</property>
- <property name="group">type-u8</property>
- <signal name="toggled" handler="update_configuration" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">10</property>
- </packing>
- </child>
- <child>
- <object class="GtkRadioButton" id="type-ascii">
- <property name="label" translatable="yes">ASCII text</property>
- <property name="name">ascii</property>
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">False</property>
- <property name="draw-indicator">True</property>
- <property name="group">type-u8</property>
- <signal name="toggled" handler="update_configuration" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">11</property>
- </packing>
- </child>
- <child>
- <object class="GtkRadioButton" id="type-utf16">
- <property name="label" translatable="yes">UTF-16 text</property>
- <property name="name">utf16</property>
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">False</property>
- <property name="active">True</property>
- <property name="draw-indicator">True</property>
- <property name="group">type-u8</property>
- <signal name="toggled" handler="update_configuration" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">12</property>
- </packing>
- </child>
- <child>
- <object class="GtkRadioButton" id="type-utf32">
- <property name="label" translatable="yes">UTF-32 text</property>
- <property name="name">utf32</property>
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">False</property>
- <property name="draw-indicator">True</property>
- <property name="group">type-u8</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">13</property>
- </packing>
- </child>
- <child>
- <object class="GtkRadioButton" id="type-f32">
- <property name="label" translatable="yes">32-bit floating-point</property>
- <property name="name">f32</property>
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">False</property>
- <property name="draw-indicator">True</property>
- <property name="group">type-u8</property>
- <signal name="toggled" handler="update_configuration" swapped="no"/>
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkRadioButton" id="type-u8">
+ <property name="label" translatable="yes">8-bit unsigned integer</property>
+ <property name="name">u8</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="active">True</property>
+ <property name="draw-indicator">True</property>
+ <signal name="toggled" handler="update_configuration" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="type-s8">
+ <property name="label" translatable="yes">8-bit signed integer</property>
+ <property name="name">s8</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="active">True</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">type-u8</property>
+ <signal name="toggled" handler="update_configuration" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="type-u16">
+ <property name="label" translatable="yes">16-bit unsigned integer</property>
+ <property name="name">u16</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="active">True</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">type-u8</property>
+ <signal name="toggled" handler="update_configuration" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="type-s16">
+ <property name="label" translatable="yes">16-bit signed integer</property>
+ <property name="name">s16</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">type-u8</property>
+ <signal name="toggled" handler="update_configuration" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="type-u32">
+ <property name="label" translatable="yes">32-bit unsigned integer</property>
+ <property name="name">u32</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">type-u8</property>
+ <signal name="toggled" handler="update_configuration" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="type-s32">
+ <property name="label" translatable="yes">32-bit signed integer</property>
+ <property name="name">s32</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">type-u8</property>
+ <signal name="toggled" handler="update_configuration" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="type-u64">
+ <property name="label" translatable="yes">64-bit unsigned integer</property>
+ <property name="name">u64</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">type-u8</property>
+ <signal name="toggled" handler="update_configuration" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">6</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="type-s64">
+ <property name="label" translatable="yes">64-bit signed integer</property>
+ <property name="name">s64</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">type-u8</property>
+ <signal name="toggled" handler="update_configuration" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">7</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="type-f32">
+ <property name="label" translatable="yes">32-bit floating-point</property>
+ <property name="name">f32</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">type-u8</property>
+ <signal name="toggled" handler="update_configuration" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="type-f64">
+ <property name="label" translatable="yes">64-bit floating-point</property>
+ <property name="name">f64</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">type-u8</property>
+ <signal name="toggled" handler="update_configuration" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">9</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="type-ascii">
+ <property name="label" translatable="yes">ASCII text</property>
+ <property name="name">ascii</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">type-u8</property>
+ <signal name="toggled" handler="update_configuration" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">10</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="type-utf16">
+ <property name="label" translatable="yes">UTF-16 text</property>
+ <property name="name">utf16</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="active">True</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">type-u8</property>
+ <signal name="toggled" handler="update_configuration" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">11</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="type-utf32">
+ <property name="label" translatable="yes">UTF-32 text</property>
+ <property name="name">utf32</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">type-u8</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">12</property>
+ </packing>
+ </child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">14</property>
+ <property name="position">16</property>
</packing>
</child>
<child>
- <object class="GtkRadioButton" id="type-f64">
- <property name="label" translatable="yes">64-bit floating-point</property>
- <property name="name">f64</property>
+ <object class="GtkBox" id="search-box">
<property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">False</property>
- <property name="draw-indicator">True</property>
- <property name="group">type-u8</property>
- <signal name="toggled" handler="update_configuration" swapped="no"/>
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="search-label">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">Search for a value</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="pre-search">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="search-type-label">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">Search type:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="enter-value">
+ <property name="label" translatable="yes">Enter a value</property>
+ <property name="name">enter-value</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="tooltip-text" translatable="yes">At every step in the search, enter the current value of what you're looking for (or an approximate value for floating-point numbers).</property>
+ <property name="active">True</property>
+ <property name="draw-indicator">True</property>
+ <signal name="toggled" handler="update_configuration" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="same-different">
+ <property name="label" translatable="yes">Same/different</property>
+ <property name="name">same-different</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="tooltip-text" translatable="yes">At every step in the search, enter whether or not the value has changed since the last step. This will use quite a bit of disk space, to record all the process' memory.</property>
+ <property name="active">True</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">enter-value</property>
+ <signal name="toggled" handler="update_configuration" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="required-memory-box">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkLabel" id="required-memory-label">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">Memory required (approx): </property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="required-memory">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">N/A</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="required-disk-box">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkLabel" id="required-disk-label">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">Disk space required (approx): </property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="required-disk">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">N/A</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="begin-search">
+ <property name="label" translatable="yes">Begin search</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="tooltip-text" translatable="yes">Start searching for a value in memory. Make sure the value is stored somewhere in memory before you start.</property>
+ <signal name="clicked" handler="search_start" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="search-enter-value">
+ <property name="can-focus">False</property>
+ <property name="no-show-all">True</property>
+ <child>
+ <object class="GtkLabel" id="current-value-label">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">Current value:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="current-value">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="search-same-different">
+ <property name="can-focus">False</property>
+ <property name="no-show-all">True</property>
+ <child>
+ <object class="GtkRadioButton" id="same">
+ <property name="label" translatable="yes">Same</property>
+ <property name="name">same</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="margin-end">5</property>
+ <property name="active">True</property>
+ <property name="draw-indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="different">
+ <property name="label" translatable="yes">Different</property>
+ <property name="name">different</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="active">True</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">same</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="search-common">
+ <property name="can-focus">False</property>
+ <property name="no-show-all">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="steps-completed-box">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkLabel" id="steps-completed">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="steps-completed-label">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes"> steps completed</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="candidates-left-box">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="tooltip-text" translatable="yes">Number of possible places where this value could be</property>
+ <child>
+ <object class="GtkLabel" id="candidates-left">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="candidates-left-label">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes"> candidates left</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="same-different-update">
+ <property name="label" translatable="yes">Update</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="tooltip-text" translatable="yes">Click this to indicate that the value in the box above is the current value of the thing you're looking for.</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">15</property>
+ <property name="position">18</property>
</packing>
</child>
</object>
diff --git a/unicode.h b/unicode.h
index c327006..926f426 100644
--- a/unicode.h
+++ b/unicode.h
@@ -1,3 +1,4 @@
+// Functions for handling unicode text.
#ifndef UNICODE_H_
#define UNICODE_H_
#define UNICODE_BOX_CHARACTER 0x2610