1 /*
2 * memory.c
3 *
4 * Copyright 2013 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #include <ctype.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <gdk/gdkkeysyms.h>
28
29 #include "common.h"
30
31 enum
32 {
33 MEMORY_ADDR,
34 MEMORY_BYTES,
35 MEMORY_ASCII
36 };
37
38 static ScpTreeStore *store;
39 static GtkTreeSelection *selection;
40
on_memory_bytes_edited(G_GNUC_UNUSED GtkCellRendererText * renderer,gchar * path_str,gchar * new_text,G_GNUC_UNUSED gpointer gdata)41 static void on_memory_bytes_edited(G_GNUC_UNUSED GtkCellRendererText *renderer, gchar *path_str,
42 gchar *new_text, G_GNUC_UNUSED gpointer gdata)
43 {
44 if (*new_text && (debug_state() & DS_VARIABLE))
45 {
46 GtkTreeIter iter;
47 const char *addr, *bytes;
48 guint i;
49
50 scp_tree_store_get_iter_from_string(store, &iter, path_str);
51 scp_tree_store_get(store, &iter, MEMORY_ADDR, &addr, MEMORY_BYTES, &bytes, -1);
52
53 for (i = 0; bytes[i]; i++)
54 if (!(isxdigit(bytes[i]) ? isxdigit(new_text[i]) : new_text[i] == ' '))
55 break;
56
57 if (bytes[i] || new_text[i])
58 dc_error("memory: invalid format");
59 else
60 {
61 utils_strchrepl(new_text, ' ', '\0');
62 debug_send_format(T, "07-data-write-memory-bytes 0x%s%s", addr, new_text);
63 }
64 }
65 else
66 plugin_blink();
67 }
68
on_memory_entry_key_press(G_GNUC_UNUSED GtkWidget * widget,GdkEventKey * event,GtkEditable * editable)69 static gboolean on_memory_entry_key_press(G_GNUC_UNUSED GtkWidget *widget, GdkEventKey *event,
70 GtkEditable *editable)
71 {
72 const char *text = gtk_entry_get_text(GTK_ENTRY(editable));
73 gint pos = gtk_editable_get_position(editable);
74
75 if (event->keyval <= 0x7F && ((isxdigit(event->keyval) && isxdigit(text[pos])) ||
76 (event->keyval == ' ' && text[pos] == ' ')) && event->state <= GDK_SHIFT_MASK)
77 {
78 char c = event->keyval;
79
80 gtk_editable_set_editable(editable, TRUE);
81 gtk_editable_delete_text(editable, pos, pos + 1);
82 gtk_editable_insert_text(editable, &c, 1, &pos);
83 gtk_editable_set_position(editable, pos);
84 gtk_editable_set_editable(editable, FALSE);
85 return TRUE;
86 }
87
88 return event->keyval == GDK_Insert || event->keyval == GDK_KP_Insert ||
89 event->keyval == GDK_space || event->keyval == GDK_KP_Space;
90 }
91
92 static const char *memory_font;
93
on_memory_bytes_editing_started(G_GNUC_UNUSED GtkCellRenderer * cell,GtkCellEditable * cell_editable,G_GNUC_UNUSED const gchar * path,G_GNUC_UNUSED gpointer gdata)94 static void on_memory_bytes_editing_started(G_GNUC_UNUSED GtkCellRenderer *cell,
95 GtkCellEditable *cell_editable, G_GNUC_UNUSED const gchar *path,
96 G_GNUC_UNUSED gpointer gdata)
97 {
98 iff (GTK_IS_ENTRY(cell_editable), "memory_bytes: not an entry")
99 {
100 GtkEditable *editable = GTK_EDITABLE(cell_editable);
101
102 ui_widget_modify_font_from_string(GTK_WIDGET(editable), memory_font);
103 gtk_entry_set_overwrite_mode(GTK_ENTRY(editable), TRUE);
104 gtk_editable_set_editable(editable, FALSE);
105 gtk_editable_set_position(editable, 0);
106 g_signal_connect(editable, "key-press-event", G_CALLBACK(on_memory_entry_key_press),
107 editable);
108 }
109 }
110
111 static const TreeCell memory_cells[] =
112 {
113 { "memory_bytes", G_CALLBACK(on_memory_bytes_edited) },
114 { NULL, NULL }
115 };
116
117 static guint pointer_size;
118 static char *addr_format;
119 #define MAX_BYTES_PER_LINE 128
120 #define MAX_POINTER_SIZE 8
121
122 static gint back_bytes_per_line;
123 static gint bytes_per_line;
124 static gint bytes_per_group = 1;
125
memory_configure(void)126 static void memory_configure(void)
127 {
128 gint groups_per_line;
129
130 back_bytes_per_line = pref_memory_bytes_per_line;
131 bytes_per_line = pref_memory_bytes_per_line;
132 if ((unsigned) (bytes_per_line - 8) > MAX_BYTES_PER_LINE - 8)
133 bytes_per_line = 16;
134
135 groups_per_line = bytes_per_line / bytes_per_group;
136 bytes_per_line = groups_per_line * bytes_per_group;
137 }
138
139 static guint64 memory_start;
140 static guint memory_count = 0;
141 #define MAX_BYTES (124 * MAX_BYTES_PER_LINE)
142
write_block(guint64 start,const char * contents,guint count,const char * maddr)143 static void write_block(guint64 start, const char *contents, guint count, const char *maddr)
144 {
145 if (!memory_count)
146 memory_start = start;
147 else if (memory_count < MAX_BYTES)
148 memory_count = start - memory_start;
149
150 while (memory_count < MAX_BYTES)
151 {
152 GtkTreeIter iter;
153 char *addr = g_strdup_printf(addr_format, start);
154 GString *bytes = g_string_sized_new(bytes_per_line * 3);
155 GString *ascii = g_string_new(" ");
156 gint n = 0;
157
158 while (n < bytes_per_line)
159 {
160 char locale;
161 gchar *utf8;
162
163 g_string_append_len(bytes, contents, 2);
164 contents += 2;
165 memory_count++;
166 locale = strtol(bytes->str + bytes->len - 2, NULL, 16);
167 utf8 = locale >= 0x20 ? g_locale_to_utf8(&locale, 1, NULL, NULL, NULL) : NULL;
168
169 if (utf8)
170 {
171 g_string_append(ascii, utf8);
172 g_free(utf8);
173 }
174 else
175 g_string_append_c(ascii, '.'); /* 0xfffd? */
176
177 if (++n % bytes_per_group == 0)
178 g_string_append_c(bytes, ' ');
179
180 if (!--count)
181 break;
182 }
183
184 while (n < bytes_per_line)
185 {
186 g_string_append(bytes, " ");
187
188 if (++n % bytes_per_group == 0)
189 g_string_append_c(bytes, ' ');
190 }
191
192 scp_tree_store_append_with_values(store, &iter, NULL, MEMORY_ADDR, addr,
193 MEMORY_BYTES, bytes->str, MEMORY_ASCII, ascii->str, -1);
194 if (!g_strcmp0(addr, maddr))
195 gtk_tree_selection_select_iter(selection, &iter);
196
197 g_free(addr);
198 g_string_free(bytes, TRUE);
199 g_string_free(ascii, TRUE);
200
201 if (!count)
202 break;
203
204 start += bytes_per_line;
205 }
206
207 if (count)
208 dc_error("memory: too much data");
209 }
210
memory_node_read(const ParseNode * node,const char * maddr)211 static void memory_node_read(const ParseNode *node, const char *maddr)
212 {
213 iff (node->type == PT_ARRAY, "memory: contains value")
214 {
215 GArray *nodes = (GArray *) node->value;
216 const char *begin = parse_find_value(nodes, "begin");
217 const char *offset = parse_find_value(nodes, "offset");
218 const char *contents = parse_find_value(nodes, "contents");
219
220 iff (begin && contents, "memory: no begin or contents")
221 {
222 guint64 start = g_ascii_strtoull(begin, NULL, 0);
223 guint64 count = strlen(contents) / 2;
224
225 if (offset)
226 start += g_ascii_strtoull(offset, NULL, 0);
227
228 iff (count, "memory: contents too short")
229 write_block(start, contents, count, maddr);
230 }
231 }
232 }
233
on_memory_read_bytes(GArray * nodes)234 void on_memory_read_bytes(GArray *nodes)
235 {
236 if (pointer_size <= MAX_POINTER_SIZE)
237 {
238 GtkTreeIter iter;
239 char *maddr = NULL;
240
241 if (gtk_tree_selection_get_selected(selection, NULL, &iter))
242 gtk_tree_model_get((GtkTreeModel *) store, &iter, MEMORY_ADDR, &maddr, -1);
243
244 store_clear(store);
245 memory_count = 0;
246
247 if (pref_memory_bytes_per_line != back_bytes_per_line)
248 {
249 memory_configure();
250 gtk_tree_view_column_queue_resize(get_column("memory_bytes_column"));
251 gtk_tree_view_column_queue_resize(get_column("memory_ascii_column"));
252 }
253
254 parse_foreach(parse_lead_array(nodes), (GFunc) memory_node_read, maddr);
255 g_free(maddr);
256 }
257 }
258
memory_clear(void)259 void memory_clear(void)
260 {
261 store_clear(store);
262 }
263
memory_update(void)264 gboolean memory_update(void)
265 {
266 if (memory_count)
267 {
268 debug_send_format(T, "04-data-read-memory-bytes 0x%" G_GINT64_MODIFIER "x %u",
269 memory_start, memory_count);
270 }
271 return TRUE;
272 }
273
on_memory_refresh(G_GNUC_UNUSED const MenuItem * menu_item)274 static void on_memory_refresh(G_GNUC_UNUSED const MenuItem *menu_item)
275 {
276 debug_send_format(T, "-data-read-memory-bytes 0x%" G_GINT64_MODIFIER "x %u",
277 memory_start, memory_count);
278 }
279
on_memory_read(G_GNUC_UNUSED const MenuItem * menu_item)280 static void on_memory_read(G_GNUC_UNUSED const MenuItem *menu_item)
281 {
282 GString *command = g_string_new("-data-read-memory-bytes ");
283 gchar *expr = utils_get_default_selection();
284
285 if (expr)
286 {
287 g_string_append(command, expr);
288 g_free(expr);
289 }
290 else if (memory_count)
291 {
292 g_string_append_printf(command, "0x%" G_GINT64_MODIFIER "x %u", memory_start,
293 memory_count);
294 }
295
296 view_command_line(command->str, _("Read Memory"), " ", TRUE);
297 g_string_free(command, TRUE);
298 }
299
on_memory_copy(G_GNUC_UNUSED const MenuItem * menu_item)300 static void on_memory_copy(G_GNUC_UNUSED const MenuItem *menu_item)
301 {
302 GtkTreeIter iter;
303 const char *addr, *bytes;
304 const gchar *ascii;
305 gchar *string;
306
307 if (gtk_tree_selection_get_selected(selection, NULL, &iter))
308 {
309 scp_tree_store_get(store, &iter, MEMORY_ADDR, &addr, MEMORY_BYTES, &bytes,
310 MEMORY_ASCII, &ascii, -1);
311 string = g_strdup_printf("%s%s%s", addr, bytes, ascii);
312 gtk_clipboard_set_text(gtk_widget_get_clipboard(menu_item->widget,
313 GDK_SELECTION_CLIPBOARD), string, -1);
314 g_free(string);
315 }
316 }
317
on_memory_clear(G_GNUC_UNUSED const MenuItem * menu_item)318 static void on_memory_clear(G_GNUC_UNUSED const MenuItem *menu_item)
319 {
320 store_clear(store);
321 memory_count = 0;
322 }
323
on_memory_group_display(const MenuItem * menu_item)324 static void on_memory_group_display(const MenuItem *menu_item)
325 {
326 guint i;
327
328 for (i = 0; (1 << i) < bytes_per_group; i++);
329 menu_item_set_active(menu_item + i + 1, TRUE);
330 }
331
on_memory_group_update(const MenuItem * menu_item)332 static void on_memory_group_update(const MenuItem *menu_item)
333 {
334 bytes_per_group = 1 << GPOINTER_TO_INT(menu_item->gdata);
335 back_bytes_per_line = 0;
336
337 if (memory_count)
338 on_memory_refresh(menu_item);
339 }
340
341 #define DS_FRESHABLE (DS_VRIABLE | DS_EXTRA_2)
342 #define DS_COPYABLE (DS_BASICS | DS_EXTRA_1)
343 #define DS_CLEARABLE (DS_ACTIVE | DS_EXTRA_2)
344
345 #define GROUP_ITEM(count, POWER) \
346 { ("memory_group_"count), on_memory_group_update, DS_VARIABLE, NULL, \
347 GINT_TO_POINTER(POWER) }
348
349 static MenuItem memory_menu_items[] =
350 {
351 { "memory_refresh", on_memory_refresh, DS_VARIABLE, NULL, NULL },
352 { "memory_read", on_memory_read, DS_VARIABLE, NULL, NULL },
353 { "memory_copy", on_memory_copy, DS_COPYABLE, NULL, NULL },
354 { "memory_clear", on_memory_clear, DS_CLEARABLE, NULL, NULL },
355 { "memory_group", on_memory_group_display, DS_VARIABLE, NULL, NULL },
356 GROUP_ITEM("1", 0),
357 GROUP_ITEM("2", 1),
358 GROUP_ITEM("4", 2),
359 GROUP_ITEM("8", 3),
360 { NULL, NULL, 0, NULL, NULL }
361 };
362
memory_menu_extra_state(void)363 static guint memory_menu_extra_state(void)
364 {
365 return (gtk_tree_selection_get_selected(selection, NULL, NULL) << DS_INDEX_1) |
366 (memory_count != 0) << DS_INDEX_2;
367 }
368
369 static MenuInfo memory_menu_info = { memory_menu_items, memory_menu_extra_state, 0 };
370
on_memory_key_press(G_GNUC_UNUSED GtkWidget * widget,GdkEventKey * event,gpointer gdata)371 static gboolean on_memory_key_press(G_GNUC_UNUSED GtkWidget *widget, GdkEventKey *event,
372 gpointer gdata)
373 {
374 if (event->keyval == GDK_Insert || event->keyval == GDK_KP_Insert)
375 {
376 menu_item_execute(&memory_menu_info, (const MenuItem *) gdata, FALSE);
377 return TRUE;
378 }
379
380 return FALSE;
381 }
382
memory_init(void)383 void memory_init(void)
384 {
385 GtkWidget *tree = GTK_WIDGET(view_connect("memory_view", &store, &selection,
386 memory_cells, "memory_window", NULL));
387
388 memory_font = *pref_memory_font ? pref_memory_font : pref_vte_font;
389 ui_widget_modify_font_from_string(tree, memory_font);
390 g_signal_connect(get_object("memory_bytes"), "editing-started",
391 G_CALLBACK(on_memory_bytes_editing_started), NULL);
392 g_signal_connect(tree, "key-press-event", G_CALLBACK(on_memory_key_press),
393 (gpointer) menu_item_find(memory_menu_items, "memory_read"));
394
395 pointer_size = MAX(sizeof(void *), sizeof(&memory_init));
396 addr_format = g_strdup_printf("%%0%u" G_GINT64_MODIFIER "x ", pointer_size * 2);
397 memory_configure();
398
399 if (pointer_size > MAX_POINTER_SIZE)
400 {
401 msgwin_status_add(_("Scope: pointer size > %d, Data disabled."), MAX_POINTER_SIZE);
402 gtk_widget_hide(tree);
403 }
404 else
405 menu_connect("memory_menu", &memory_menu_info, tree);
406 }
407
memory_finalize(void)408 void memory_finalize(void)
409 {
410 g_free(addr_format);
411 }
412