1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* hex-document.c - implementation of a hex document
3
4 Copyright (C) 1998 - 2004 Free Software Foundation
5
6 GHex is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
10
11 GHex 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 GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GHex; see the file COPYING.
18 If not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21 Author: Jaka Mocnik <jaka@gnu.org>
22 */
23
24 #include <config.h>
25 #include <glib-object.h>
26 #include <glib/gi18n.h>
27
28 #include <gtkhex.h>
29
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <sys/stat.h>
33 #include <string.h>
34
35 static void hex_document_class_init (HexDocumentClass *);
36 static void hex_document_init (HexDocument *doc);
37 static void hex_document_finalize (GObject *obj);
38 static void hex_document_real_changed (HexDocument *doc,
39 gpointer change_data,
40 gboolean undoable);
41 static void hex_document_real_redo (HexDocument *doc);
42 static void hex_document_real_undo (HexDocument *doc);
43 static void move_gap_to (HexDocument *doc,
44 guint offset,
45 gint min_size);
46 static void free_stack (GList *stack);
47 static gint undo_stack_push (HexDocument *doc,
48 HexChangeData *change_data);
49 static void undo_stack_descend (HexDocument *doc);
50 static void undo_stack_ascend (HexDocument *doc);
51 static void undo_stack_free (HexDocument *doc);
52 static gboolean get_document_attributes (HexDocument *doc);
53
54 #define DEFAULT_UNDO_DEPTH 1024
55
56 enum {
57 DOCUMENT_CHANGED,
58 UNDO,
59 REDO,
60 UNDO_STACK_FORGET,
61 LAST_SIGNAL
62 };
63
64 static gint hex_signals[LAST_SIGNAL];
65
66 static GObjectClass *parent_class = NULL;
67
68 static GList *doc_list = NULL;
69
70 static void
free_stack(GList * stack)71 free_stack(GList *stack)
72 {
73 HexChangeData *cd;
74
75 while(stack) {
76 cd = (HexChangeData *)stack->data;
77 if(cd->v_string)
78 g_free(cd->v_string);
79 stack = g_list_remove(stack, cd);
80 g_free(cd);
81 }
82 }
83
84 static gint
undo_stack_push(HexDocument * doc,HexChangeData * change_data)85 undo_stack_push(HexDocument *doc, HexChangeData *change_data)
86 {
87 HexChangeData *cd;
88 GList *stack_rest;
89
90 #ifdef ENABLE_DEBUG
91 g_message("undo_stack_push");
92 #endif
93
94 if(doc->undo_stack != doc->undo_top) {
95 stack_rest = doc->undo_stack;
96 doc->undo_stack = doc->undo_top;
97 if(doc->undo_top) {
98 doc->undo_top->prev->next = NULL;
99 doc->undo_top->prev = NULL;
100 }
101 free_stack(stack_rest);
102 }
103
104 if((cd = g_new(HexChangeData, 1)) != NULL) {
105 memcpy(cd, change_data, sizeof(HexChangeData));
106 if(change_data->v_string) {
107 cd->v_string = g_malloc(cd->rep_len);
108 memcpy(cd->v_string, change_data->v_string, cd->rep_len);
109 }
110
111 doc->undo_depth++;
112
113 #ifdef ENABLE_DEBUG
114 g_message("depth at: %d", doc->undo_depth);
115 #endif
116
117 if(doc->undo_depth > doc->undo_max) {
118 GList *last;
119
120 #ifdef ENABLE_DEBUG
121 g_message("forget last undo");
122 #endif
123
124 last = g_list_last(doc->undo_stack);
125 doc->undo_stack = g_list_remove_link(doc->undo_stack, last);
126 doc->undo_depth--;
127 free_stack(last);
128 }
129
130 doc->undo_stack = g_list_prepend(doc->undo_stack, cd);
131 doc->undo_top = doc->undo_stack;
132
133 return TRUE;
134 }
135
136 return FALSE;
137 }
138
139 static void
undo_stack_descend(HexDocument * doc)140 undo_stack_descend(HexDocument *doc)
141 {
142 #ifdef ENABLE_DEBUG
143 g_message("undo_stack_descend");
144 #endif
145
146 if(doc->undo_top == NULL)
147 return;
148
149 doc->undo_top = doc->undo_top->next;
150 doc->undo_depth--;
151
152 #ifdef ENABLE_DEBUG
153 g_message("undo depth at: %d", doc->undo_depth);
154 #endif
155 }
156
157 static void
undo_stack_ascend(HexDocument * doc)158 undo_stack_ascend(HexDocument *doc)
159 {
160 #ifdef ENABLE_DEBUG
161 g_message("undo_stack_ascend");
162 #endif
163
164 if(doc->undo_stack == NULL || doc->undo_top == doc->undo_stack)
165 return;
166
167 if(doc->undo_top == NULL)
168 doc->undo_top = g_list_last(doc->undo_stack);
169 else
170 doc->undo_top = doc->undo_top->prev;
171 doc->undo_depth++;
172 }
173
174 static void
undo_stack_free(HexDocument * doc)175 undo_stack_free(HexDocument *doc)
176 {
177 #ifdef ENABLE_DEBUG
178 g_message("undo_stack_free");
179 #endif
180
181 if(doc->undo_stack == NULL)
182 return;
183
184 free_stack(doc->undo_stack);
185 doc->undo_stack = NULL;
186 doc->undo_top = NULL;
187 doc->undo_depth = 0;
188
189 g_signal_emit(G_OBJECT(doc), hex_signals[UNDO_STACK_FORGET], 0);
190 }
191
192 static gboolean
get_document_attributes(HexDocument * doc)193 get_document_attributes(HexDocument *doc)
194 {
195 static struct stat stats;
196
197 if(doc->file_name == NULL)
198 return FALSE;
199
200 if(!stat(doc->file_name, &stats) &&
201 S_ISREG(stats.st_mode)) {
202 doc->file_size = stats.st_size;
203
204 return TRUE;
205 }
206
207 return FALSE;
208 }
209
210
211 static void
move_gap_to(HexDocument * doc,guint offset,gint min_size)212 move_gap_to(HexDocument *doc, guint offset, gint min_size)
213 {
214 guchar *tmp, *buf_ptr, *tmp_ptr;
215
216 if(doc->gap_size < min_size) {
217 tmp = g_malloc(sizeof(guchar)*doc->file_size);
218 buf_ptr = doc->buffer;
219 tmp_ptr = tmp;
220 while(buf_ptr < doc->gap_pos)
221 *tmp_ptr++ = *buf_ptr++;
222 buf_ptr += doc->gap_size;
223 while(buf_ptr < doc->buffer + doc->buffer_size)
224 *tmp_ptr++ = *buf_ptr++;
225
226 doc->gap_size = MAX(min_size, 32);
227 doc->buffer_size = doc->file_size + doc->gap_size;
228 doc->buffer = g_realloc(doc->buffer, sizeof(guchar)*doc->buffer_size);
229 doc->gap_pos = doc->buffer + offset;
230
231 buf_ptr = doc->buffer;
232 tmp_ptr = tmp;
233
234 while(buf_ptr < doc->gap_pos)
235 *buf_ptr++ = *tmp_ptr++;
236 buf_ptr += doc->gap_size;
237 while(buf_ptr < doc->buffer + doc->buffer_size)
238 *buf_ptr++ = *tmp_ptr++;
239
240 g_free(tmp);
241 }
242 else {
243 if(doc->buffer + offset < doc->gap_pos) {
244 buf_ptr = doc->gap_pos + doc->gap_size - 1;
245 while(doc->gap_pos > doc->buffer + offset)
246 *buf_ptr-- = *(--doc->gap_pos);
247 }
248 else if(doc->buffer + offset > doc->gap_pos) {
249 buf_ptr = doc->gap_pos + doc->gap_size;
250 while(doc->gap_pos < doc->buffer + offset)
251 *doc->gap_pos++ = *buf_ptr++;
252 }
253 }
254 }
255
256 GtkWidget *
hex_document_add_view(HexDocument * doc)257 hex_document_add_view(HexDocument *doc)
258 {
259 GtkWidget *new_view;
260
261 new_view = gtk_hex_new(doc);
262
263 #if GTK_CHECK_VERSION (2,18,0)
264 gtk_widget_set_has_window (GTK_WIDGET (new_view), TRUE);
265 #else
266 gtk_fixed_set_has_window (GTK_FIXED(new_view), TRUE);
267 #endif
268
269 g_object_ref(new_view);
270
271 doc->views = g_list_append(doc->views, new_view);
272
273 return new_view;
274 }
275
276 void
hex_document_remove_view(HexDocument * doc,GtkWidget * view)277 hex_document_remove_view(HexDocument *doc, GtkWidget *view)
278 {
279 if(g_list_index(doc->views, view) == -1)
280 return;
281
282 doc->views = g_list_remove(doc->views, view);
283
284 g_object_unref(view);
285 }
286
287 static void
hex_document_finalize(GObject * obj)288 hex_document_finalize(GObject *obj)
289 {
290 HexDocument *hex;
291
292 hex = HEX_DOCUMENT(obj);
293
294 if(hex->buffer)
295 g_free(hex->buffer);
296
297 if(hex->file_name)
298 g_free(hex->file_name);
299
300 if(hex->path_end)
301 g_free(hex->path_end);
302
303 undo_stack_free(hex);
304
305 while(hex->views)
306 hex_document_remove_view(hex, (GtkWidget *)hex->views->data);
307
308 doc_list = g_list_remove(doc_list, hex);
309
310 G_OBJECT_CLASS (parent_class)->finalize (obj);
311 }
312
313 static void
hex_document_real_changed(HexDocument * doc,gpointer change_data,gboolean push_undo)314 hex_document_real_changed(HexDocument *doc, gpointer change_data,
315 gboolean push_undo)
316 {
317 if(push_undo && doc->undo_max > 0)
318 undo_stack_push(doc, change_data);
319 }
320
321 static void
hex_document_class_init(HexDocumentClass * klass)322 hex_document_class_init (HexDocumentClass *klass)
323 {
324 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
325
326 parent_class = g_type_class_peek_parent(klass);
327
328 gobject_class->finalize = hex_document_finalize;
329
330 klass->document_changed = hex_document_real_changed;
331 klass->undo = hex_document_real_undo;
332 klass->redo = hex_document_real_redo;
333 klass->undo_stack_forget = NULL;
334
335 hex_signals[DOCUMENT_CHANGED] =
336 g_signal_new ("document_changed",
337 G_TYPE_FROM_CLASS(gobject_class),
338 G_SIGNAL_RUN_FIRST,
339 G_STRUCT_OFFSET (HexDocumentClass, document_changed),
340 NULL,
341 NULL,
342 NULL,
343 G_TYPE_NONE,
344 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
345 hex_signals[UNDO] =
346 g_signal_new ("undo",
347 G_TYPE_FROM_CLASS(gobject_class),
348 G_SIGNAL_RUN_FIRST,
349 G_STRUCT_OFFSET (HexDocumentClass, undo),
350 NULL,
351 NULL,
352 NULL,
353 G_TYPE_NONE, 0);
354 hex_signals[REDO] =
355 g_signal_new ("redo",
356 G_TYPE_FROM_CLASS(gobject_class),
357 G_SIGNAL_RUN_FIRST,
358 G_STRUCT_OFFSET (HexDocumentClass, redo),
359 NULL,
360 NULL,
361 NULL,
362 G_TYPE_NONE, 0);
363 hex_signals[UNDO_STACK_FORGET] =
364 g_signal_new ("undo_stack_forget",
365 G_TYPE_FROM_CLASS(gobject_class),
366 G_SIGNAL_RUN_FIRST,
367 G_STRUCT_OFFSET (HexDocumentClass, undo_stack_forget),
368 NULL,
369 NULL,
370 NULL,
371 G_TYPE_NONE, 0);
372 }
373
374 static void
hex_document_init(HexDocument * doc)375 hex_document_init (HexDocument *doc)
376 {
377 doc->buffer = NULL;
378 doc->buffer_size = 0;
379 doc->file_size = 0;
380 doc->gap_pos = NULL;
381 doc->gap_size = 0;
382 doc->changed = FALSE;
383 doc->undo_stack = NULL;
384 doc->undo_top = NULL;
385 doc->undo_depth = 0;
386 doc->undo_max = DEFAULT_UNDO_DEPTH;
387 }
388
389 GType
hex_document_get_type(void)390 hex_document_get_type (void)
391 {
392 static GType doc_type = 0;
393
394 if (!doc_type) {
395 static const GTypeInfo doc_info = {
396 sizeof (HexDocumentClass),
397 NULL, /* base_init */
398 NULL, /* base_finalize */
399 (GClassInitFunc) hex_document_class_init,
400 NULL, /* class_finalize */
401 NULL, /* class_data */
402 sizeof (HexDocument),
403 0,
404 (GInstanceInitFunc) hex_document_init
405 };
406
407 doc_type = g_type_register_static (G_TYPE_OBJECT,
408 "HexDocument",
409 &doc_info,
410 0);
411 }
412
413 return doc_type;
414 }
415
416
417 /*-------- public API starts here --------*/
418
419
420 HexDocument *
hex_document_new()421 hex_document_new()
422 {
423 HexDocument *doc;
424
425 doc = HEX_DOCUMENT (g_object_new (hex_document_get_type(), NULL));
426 g_return_val_if_fail (doc != NULL, NULL);
427
428 doc->file_name = NULL;
429
430 doc->gap_size = 100;
431 doc->file_size = 0;
432 doc->buffer_size = doc->file_size + doc->gap_size;
433 doc->gap_pos = doc->buffer = (guchar *)g_malloc(doc->buffer_size);
434
435 doc->path_end = g_strdup(_("New document"));
436
437 doc_list = g_list_append(doc_list, doc);
438 return doc;
439 }
440
441 HexDocument *
hex_document_new_from_file(const gchar * name)442 hex_document_new_from_file(const gchar *name)
443 {
444 HexDocument *doc;
445 gchar *path_end;
446
447 doc = HEX_DOCUMENT (g_object_new (hex_document_get_type(), NULL));
448 g_return_val_if_fail (doc != NULL, NULL);
449
450 doc->file_name = (gchar *)g_strdup(name);
451 if(get_document_attributes(doc)) {
452 doc->gap_size = 100;
453 doc->buffer_size = doc->file_size + doc->gap_size;
454 doc->buffer = (guchar *)g_malloc(doc->buffer_size);
455
456 /* find the start of the filename without path */
457 path_end = g_path_get_basename (doc->file_name);
458 doc->path_end = g_filename_to_utf8 (path_end, -1, NULL, NULL, NULL);
459 g_free (path_end);
460
461 if(hex_document_read(doc)) {
462 doc_list = g_list_append(doc_list, doc);
463 return doc;
464 }
465 }
466 g_object_unref(G_OBJECT(doc));
467
468 return NULL;
469 }
470
471 guchar
hex_document_get_byte(HexDocument * doc,guint offset)472 hex_document_get_byte(HexDocument *doc, guint offset)
473 {
474 if(offset < doc->file_size) {
475 if(doc->gap_pos <= doc->buffer + offset)
476 offset += doc->gap_size;
477 return doc->buffer[offset];
478 }
479 else
480 return 0;
481 }
482
483 guchar *
hex_document_get_data(HexDocument * doc,guint offset,guint len)484 hex_document_get_data(HexDocument *doc, guint offset, guint len)
485 {
486 guchar *ptr, *data, *dptr;
487 guint i;
488
489 ptr = doc->buffer + offset;
490 if(ptr >= doc->gap_pos)
491 ptr += doc->gap_size;
492 dptr = data = g_malloc(sizeof(guchar)*len);
493 i = 0;
494 while(i < len) {
495 if(ptr >= doc->gap_pos && ptr < doc->gap_pos + doc->gap_size)
496 ptr += doc->gap_size;
497 *dptr++ = *ptr++;
498 i++;
499 }
500
501 return data;
502 }
503
504 void
hex_document_set_nibble(HexDocument * doc,guchar val,guint offset,gboolean lower_nibble,gboolean insert,gboolean undoable)505 hex_document_set_nibble(HexDocument *doc, guchar val, guint offset,
506 gboolean lower_nibble, gboolean insert,
507 gboolean undoable)
508 {
509 static HexChangeData change_data;
510
511 if(offset <= doc->file_size) {
512 if(!insert && offset == doc->file_size)
513 return;
514
515 doc->changed = TRUE;
516 change_data.start = offset;
517 change_data.end = offset;
518 change_data.v_string = NULL;
519 change_data.type = HEX_CHANGE_BYTE;
520 change_data.lower_nibble = lower_nibble;
521 change_data.insert = insert;
522 if(!lower_nibble && insert) {
523 move_gap_to(doc, offset, 1);
524 doc->gap_size--;
525 doc->gap_pos++;
526 doc->file_size++;
527 change_data.rep_len = 0;
528 if(offset == doc->file_size)
529 doc->buffer[offset] = 0;
530 }
531 else {
532 if(doc->buffer + offset >= doc->gap_pos)
533 offset += doc->gap_size;
534 change_data.rep_len = 1;
535 }
536
537 change_data.v_byte = doc->buffer[offset];
538 doc->buffer[offset] = (doc->buffer[offset] & (lower_nibble?0xF0:0x0F)) | (lower_nibble?val:(val << 4));
539
540 hex_document_changed(doc, &change_data, undoable);
541 }
542 }
543
544 void
hex_document_set_byte(HexDocument * doc,guchar val,guint offset,gboolean insert,gboolean undoable)545 hex_document_set_byte(HexDocument *doc, guchar val, guint offset,
546 gboolean insert, gboolean undoable)
547 {
548 static HexChangeData change_data;
549
550 if(offset <= doc->file_size) {
551 if(!insert && offset == doc->file_size)
552 return;
553
554 doc->changed = TRUE;
555 change_data.start = offset;
556 change_data.end = offset;
557 change_data.rep_len = (insert?0:1);
558 change_data.v_string = NULL;
559 change_data.type = HEX_CHANGE_BYTE;
560 change_data.lower_nibble = FALSE;
561 change_data.insert = insert;
562 if(insert) {
563 move_gap_to(doc, offset, 1);
564 doc->gap_size--;
565 doc->gap_pos++;
566 doc->file_size++;
567 }
568 else if(doc->buffer + offset >= doc->gap_pos)
569 offset += doc->gap_size;
570
571 change_data.v_byte = doc->buffer[offset];
572 doc->buffer[offset] = val;
573
574 hex_document_changed(doc, &change_data, undoable);
575 }
576 }
577
578 void
hex_document_set_data(HexDocument * doc,guint offset,guint len,guint rep_len,guchar * data,gboolean undoable)579 hex_document_set_data(HexDocument *doc, guint offset, guint len,
580 guint rep_len, guchar *data, gboolean undoable)
581 {
582 guint i;
583 guchar *ptr;
584 static HexChangeData change_data;
585
586 if(offset <= doc->file_size) {
587 if(doc->file_size - offset < rep_len)
588 rep_len -= doc->file_size - offset;
589
590 doc->changed = TRUE;
591
592 change_data.v_string = g_realloc(change_data.v_string, rep_len);
593 change_data.start = offset;
594 change_data.end = change_data.start + len - 1;
595 change_data.rep_len = rep_len;
596 change_data.type = HEX_CHANGE_STRING;
597 change_data.lower_nibble = FALSE;
598
599 i = 0;
600 ptr = &doc->buffer[offset];
601 if(ptr >= doc->gap_pos)
602 ptr += doc->gap_size;
603 while(offset + i < doc->file_size && i < rep_len) {
604 if(ptr >= doc->gap_pos && ptr < doc->gap_pos + doc->gap_size)
605 ptr += doc->gap_size;
606 change_data.v_string[i] = *ptr++;
607 i++;
608 }
609
610 if(rep_len == len) {
611 if(doc->buffer + offset >= doc->gap_pos)
612 offset += doc->gap_size;
613 }
614 else {
615 if(rep_len > len) {
616 move_gap_to(doc, offset + rep_len, 1);
617 }
618 else if(rep_len < len) {
619 move_gap_to(doc, offset + rep_len, len - rep_len);
620 }
621 doc->gap_pos -= (gint)rep_len - (gint)len;
622 doc->gap_size += (gint)rep_len - (gint)len;
623 doc->file_size += (gint)len - (gint)rep_len;
624 }
625
626 ptr = &doc->buffer[offset];
627 i = 0;
628 while(offset + i < doc->buffer_size && i < len) {
629 *ptr++ = *data++;
630 i++;
631 }
632
633 hex_document_changed(doc, &change_data, undoable);
634 }
635 }
636
637 void
hex_document_delete_data(HexDocument * doc,guint offset,guint len,gboolean undoable)638 hex_document_delete_data(HexDocument *doc, guint offset, guint len, gboolean undoable)
639 {
640 hex_document_set_data(doc, offset, 0, len, NULL, undoable);
641 }
642
643 gint
hex_document_read(HexDocument * doc)644 hex_document_read(HexDocument *doc)
645 {
646 FILE *file;
647 static HexChangeData change_data;
648
649 if(doc->file_name == NULL)
650 return FALSE;
651
652 if(!get_document_attributes(doc))
653 return FALSE;
654
655 if((file = fopen(doc->file_name, "r")) == NULL)
656 return FALSE;
657
658 doc->gap_size = doc->buffer_size - doc->file_size;
659 if(fread(doc->buffer + doc->gap_size, 1, doc->file_size, file) != doc->file_size)
660 {
661 g_return_val_if_reached(FALSE);
662 }
663 doc->gap_pos = doc->buffer;
664 fclose(file);
665 undo_stack_free(doc);
666
667 change_data.start = 0;
668 change_data.end = doc->file_size - 1;
669 doc->changed = FALSE;
670 hex_document_changed(doc, &change_data, FALSE);
671
672 return TRUE;
673 }
674
675 gint
hex_document_write_to_file(HexDocument * doc,FILE * file)676 hex_document_write_to_file(HexDocument *doc, FILE *file)
677 {
678 gint ret = TRUE;
679 size_t exp_len;
680
681 if(doc->gap_pos > doc->buffer) {
682 exp_len = MIN(doc->file_size, doc->gap_pos - doc->buffer);
683 ret = fwrite(doc->buffer, 1, exp_len, file);
684 ret = (ret == exp_len)?TRUE:FALSE;
685 }
686 if(doc->gap_pos < doc->buffer + doc->file_size) {
687 exp_len = doc->file_size - (size_t)(doc->gap_pos - doc->buffer);
688 ret = fwrite(doc->gap_pos + doc->gap_size, 1, exp_len, file);
689 ret = (ret == exp_len)?TRUE:FALSE;
690 }
691
692 return ret;
693 }
694
695 gint
hex_document_write(HexDocument * doc)696 hex_document_write(HexDocument *doc)
697 {
698 FILE *file;
699 gint ret = FALSE;
700
701 if(doc->file_name == NULL)
702 return FALSE;
703
704 if((file = fopen(doc->file_name, "wb")) != NULL) {
705 ret = hex_document_write_to_file(doc, file);
706 fclose(file);
707 if(ret) {
708 doc->changed = FALSE;
709 }
710 }
711
712 return ret;
713 }
714
715 void
hex_document_changed(HexDocument * doc,gpointer change_data,gboolean push_undo)716 hex_document_changed(HexDocument *doc, gpointer change_data,
717 gboolean push_undo)
718 {
719 g_signal_emit(G_OBJECT(doc), hex_signals[DOCUMENT_CHANGED], 0,
720 change_data, push_undo);
721 }
722
723 gboolean
hex_document_has_changed(HexDocument * doc)724 hex_document_has_changed(HexDocument *doc)
725 {
726 return doc->changed;
727 }
728
729 void
hex_document_set_max_undo(HexDocument * doc,guint max_undo)730 hex_document_set_max_undo(HexDocument *doc, guint max_undo)
731 {
732 if(doc->undo_max != max_undo) {
733 if(doc->undo_max > max_undo)
734 undo_stack_free(doc);
735 doc->undo_max = max_undo;
736 }
737 }
738
739 static gboolean
ignore_cb(GtkWidget * w,GdkEventAny * e,gpointer user_data)740 ignore_cb(GtkWidget *w, GdkEventAny *e, gpointer user_data)
741 {
742 return TRUE;
743 }
744
745 gint
hex_document_export_html(HexDocument * doc,gchar * html_path,gchar * base_name,guint start,guint end,guint cpl,guint lpp,guint cpw)746 hex_document_export_html(HexDocument *doc, gchar *html_path, gchar *base_name,
747 guint start, guint end, guint cpl, guint lpp,
748 guint cpw)
749 {
750 GtkWidget *progress_dialog, *progress_bar;
751 FILE *file;
752 int page, line, pos, lines, pages, c;
753 gchar *page_name, b;
754 gint update_pages;
755 gchar *progress_str;
756
757 lines = (end - start)/cpl;
758 if((end - start)%cpl != 0)
759 lines++;
760 pages = lines/lpp;
761 if(lines%lpp != 0)
762 pages++;
763 update_pages = pages/1000 + 1;
764
765 /* top page */
766 page_name = g_strdup_printf("%s/%s.html", html_path, base_name);
767 file = fopen(page_name, "w");
768 g_free(page_name);
769 if(!file)
770 return FALSE;
771 fprintf(file, "<HTML>\n<HEAD>\n");
772 fprintf(file, "<META HTTP-EQUIV=\"Content-Type\" "
773 "CONTENT=\"text/html; charset=UTF-8\">\n");
774 fprintf(file, "<META NAME=\"hexdata\" CONTENT=\"GHex export to HTML\">\n");
775 fprintf(file, "</HEAD>\n<BODY>\n");
776
777 fprintf(file, "<CENTER>");
778 fprintf(file, "<TABLE BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">\n");
779 fprintf(file, "<TR>\n<TD COLSPAN=\"3\"><B>%s</B></TD>\n</TR>\n",
780 doc->file_name?doc->file_name:doc->path_end);
781 fprintf(file, "<TR>\n<TD COLSPAN=\"3\"> </TD>\n</TR>\n");
782 for(page = 0; page < pages; page++) {
783 fprintf(file, "<TR>\n<TD>\n<A HREF=\"%s%08d.html\"><PRE>", base_name, page);
784 fprintf(file, _("Page"));
785 fprintf(file, " %d</PRE></A>\n</TD>\n<TD> </TD>\n<TD VALIGN=\"CENTER\"><PRE>%08x -", page+1, page*cpl*lpp);
786 fprintf(file, " %08x</PRE></TD>\n</TR>\n", MIN((page+1)*cpl*lpp-1, doc->file_size-1));
787 }
788 fprintf(file, "</TABLE>\n</CENTER>\n");
789 fprintf(file, "<HR WIDTH=\"100%%\">");
790 fprintf(file, _("Hex dump generated by"));
791 fprintf(file, " <B>"LIBGTKHEX_RELEASE_STRING"</B>\n");
792 fprintf(file, "</BODY>\n</HTML>\n");
793 fclose(file);
794
795 progress_dialog = gtk_dialog_new();
796 gtk_window_set_resizable(GTK_WINDOW(progress_dialog), FALSE);
797 gtk_window_set_modal(GTK_WINDOW(progress_dialog), TRUE);
798 g_signal_connect(G_OBJECT(progress_dialog), "delete-event",
799 G_CALLBACK(ignore_cb), NULL);
800 gtk_window_set_title(GTK_WINDOW(progress_dialog),
801 _("Saving to HTML..."));
802 progress_bar = gtk_progress_bar_new();
803 gtk_widget_show(progress_bar);
804 gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(progress_dialog))),
805 progress_bar);
806 gtk_widget_show(progress_dialog);
807
808 pos = start;
809 g_object_ref(G_OBJECT(doc));
810 for(page = 0; page < pages; page++) {
811 if((page%update_pages) == 0) {
812 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar),
813 (gdouble)page/(gdouble)pages);
814 progress_str = g_strdup_printf("%d/%d", page, pages);
815 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress_bar),
816 progress_str);
817 g_free(progress_str);
818 while(gtk_events_pending())
819 gtk_main_iteration();
820 }
821 /* write page header */
822 page_name = g_strdup_printf("%s/%s%08d.html",
823 html_path, base_name, page);
824 file = fopen(page_name, "w");
825 g_free(page_name);
826 if(!file)
827 break;
828 /* write header */
829 fprintf(file, "<HTML>\n<HEAD>\n");
830 fprintf(file, "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=iso-8859-1\">\n");
831 fprintf(file, "<META NAME=\"hexdata\" CONTENT=\"GHex export to HTML\">\n");
832 fprintf(file, "</HEAD>\n<BODY>\n");
833 /* write top table |previous|filename: page/pages|next| */
834 fprintf(file, "<TABLE BORDER=\"0\" CELLSPACING=\"0\" WIDTH=\"100%%\">\n");
835 fprintf(file, "<TR>\n<TD WIDTH=\"33%%\">\n");
836 if(page > 0) {
837 fprintf(file, "<A HREF=\"%s%08d.html\">", base_name, page-1);
838 fprintf(file, _("Previous page"));
839 fprintf(file, "</A>");
840 }
841 else
842 fprintf(file, " ");
843 fprintf(file, "\n</TD>\n");
844 fprintf(file, "<TD WIDTH=\"33%%\" ALIGN=\"CENTER\">\n");
845 fprintf(file, "<A HREF=\"%s.html\">", base_name);
846 fprintf(file, "%s:", doc->path_end);
847 fprintf(file, "</A>");
848 fprintf(file, " %d/%d", page+1, pages);
849 fprintf(file, "\n</TD>\n");
850 fprintf(file, "<TD WIDTH=\"33%%\" ALIGN=\"RIGHT\">\n");
851 if(page < pages - 1) {
852 fprintf(file, "<A HREF=\"%s%08d.html\">", base_name, page+1);
853 fprintf(file, _("Next page"));
854 fprintf(file, "</A>");
855 }
856 else
857 fprintf(file, " ");
858 fprintf(file, "\n</TD>\n");
859 fprintf(file, "</TR>\n</TABLE>\n");
860
861 /* now the actual data */
862 fprintf(file, "<CENTER>\n");
863 fprintf(file, "<TABLE BORDER=\"1\" CELLSPACING=\"2\" CELLPADDING=\"2\">\n");
864 fprintf(file, "<TR>\n<TD>\n");
865 fprintf(file, "<TABLE BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">\n");
866 for(line = 0; line < lpp && pos + line*cpl < doc->file_size; line++) {
867 /* offset of line*/
868 fprintf(file, "<TR>\n<TD>\n");
869 fprintf(file, "<PRE>%08x</PRE>\n", pos + line*cpl);
870 fprintf(file, "</TD>\n</TR>\n");
871 }
872 fprintf(file, "</TABLE>\n");
873 fprintf(file, "</TD>\n<TD>\n");
874 fprintf(file, "<TABLE BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">\n");
875 c = 0;
876 for(line = 0; line < lpp; line++) {
877 /* hex data */
878 fprintf(file, "<TR>\n<TD>\n<PRE>");
879 while(pos + c < end) {
880 fprintf(file, "%02x", hex_document_get_byte(doc, pos + c));
881 c++;
882 if(c%cpl == 0)
883 break;
884 if(c%cpw == 0)
885 fprintf(file, " ");
886 }
887 fprintf(file, "</PRE>\n</TD>\n</TR>\n");
888 }
889 fprintf(file, "</TABLE>\n");
890 fprintf(file, "</TD>\n<TD>\n");
891 fprintf(file, "<TABLE BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">\n");
892 c = 0;
893 for(line = 0; line < lpp; line++) {
894 /* ascii data */
895 fprintf(file, "<TR>\n<TD>\n<PRE>");
896 while(pos + c < end) {
897 b = hex_document_get_byte(doc, pos + c);
898 if(b >= 0x20)
899 fprintf(file, "%c", b);
900 else
901 fprintf(file, ".");
902 c++;
903 if(c%cpl == 0)
904 break;
905 }
906 fprintf(file, "</PRE></TD>\n</TR>\n");
907 if(pos >= end)
908 line = lpp;
909 }
910 pos += c;
911 fprintf(file, "</TD>\n</TR>\n");
912 fprintf(file, "</TABLE>\n");
913 fprintf(file, "</TABLE>\n</CENTER>\n");
914 fprintf(file, "<HR WIDTH=\"100%%\">");
915 fprintf(file, _("Hex dump generated by"));
916 fprintf(file, " <B>" LIBGTKHEX_RELEASE_STRING "</B>\n");
917 fprintf(file, "</BODY>\n</HTML>\n");
918 fclose(file);
919 }
920 g_object_unref(G_OBJECT(doc));
921 gtk_widget_destroy(progress_dialog);
922
923 return TRUE;
924 }
925
926 gint
hex_document_compare_data(HexDocument * doc,guchar * s2,gint pos,gint len)927 hex_document_compare_data(HexDocument *doc, guchar *s2, gint pos, gint len)
928 {
929 guchar c1;
930 guint i;
931
932 for(i = 0; i < len; i++, s2++) {
933 c1 = hex_document_get_byte(doc, pos + i);
934 if(c1 != (*s2))
935 return (c1 - (*s2));
936 }
937
938 return 0;
939 }
940
941 gint
hex_document_find_forward(HexDocument * doc,guint start,guchar * what,gint len,guint * found)942 hex_document_find_forward(HexDocument *doc, guint start, guchar *what,
943 gint len, guint *found)
944 {
945 guint pos;
946
947 pos = start;
948 while(pos < doc->file_size) {
949 if(hex_document_compare_data(doc, what, pos, len) == 0) {
950 *found = pos;
951 return TRUE;
952 }
953 pos++;
954 }
955
956 return FALSE;
957 }
958
959 gint
hex_document_find_backward(HexDocument * doc,guint start,guchar * what,gint len,guint * found)960 hex_document_find_backward(HexDocument *doc, guint start, guchar *what,
961 gint len, guint *found)
962 {
963 guint pos;
964
965 pos = start;
966
967 if(pos == 0)
968 return FALSE;
969
970 do {
971 pos--;
972 if(hex_document_compare_data(doc, what, pos, len) == 0) {
973 *found = pos;
974 return TRUE;
975 }
976 } while(pos > 0);
977
978 return FALSE;
979 }
980
981 gboolean
hex_document_undo(HexDocument * doc)982 hex_document_undo(HexDocument *doc)
983 {
984 if(doc->undo_top == NULL)
985 return FALSE;
986
987 g_signal_emit(G_OBJECT(doc), hex_signals[UNDO], 0);
988
989 return TRUE;
990 }
991
992 static void
hex_document_real_undo(HexDocument * doc)993 hex_document_real_undo(HexDocument *doc)
994 {
995 HexChangeData *cd;
996 gint len;
997 guchar *rep_data;
998 gchar c_val;
999
1000 cd = (HexChangeData *)doc->undo_top->data;
1001
1002 switch(cd->type) {
1003 case HEX_CHANGE_BYTE:
1004 if(cd->start >= 0 && cd->end < doc->file_size) {
1005 c_val = hex_document_get_byte(doc, cd->start);
1006 if(cd->rep_len > 0)
1007 hex_document_set_byte(doc, cd->v_byte, cd->start, FALSE, FALSE);
1008 else if(cd->rep_len == 0)
1009 hex_document_delete_data(doc, cd->start, 1, FALSE);
1010 else
1011 hex_document_set_byte(doc, cd->v_byte, cd->start, TRUE, FALSE);
1012 cd->v_byte = c_val;
1013 }
1014 break;
1015 case HEX_CHANGE_STRING:
1016 len = cd->end - cd->start + 1;
1017 rep_data = hex_document_get_data(doc, cd->start, len);
1018 hex_document_set_data(doc, cd->start, cd->rep_len, len, cd->v_string, FALSE);
1019 g_free(cd->v_string);
1020 cd->end = cd->start + cd->rep_len - 1;
1021 cd->rep_len = len;
1022 cd->v_string = rep_data;
1023 break;
1024 }
1025
1026 hex_document_changed(doc, cd, FALSE);
1027
1028 undo_stack_descend(doc);
1029 }
1030
1031 gboolean
hex_document_is_writable(HexDocument * doc)1032 hex_document_is_writable(HexDocument *doc)
1033 {
1034 return (doc->file_name != NULL &&
1035 access(doc->file_name, W_OK) == 0);
1036 }
1037
1038 gboolean
hex_document_redo(HexDocument * doc)1039 hex_document_redo(HexDocument *doc)
1040 {
1041 if(doc->undo_stack == NULL || doc->undo_top == doc->undo_stack)
1042 return FALSE;
1043
1044 g_signal_emit(G_OBJECT(doc), hex_signals[REDO], 0);
1045
1046 return TRUE;
1047 }
1048
1049 static void
hex_document_real_redo(HexDocument * doc)1050 hex_document_real_redo(HexDocument *doc)
1051 {
1052 HexChangeData *cd;
1053 gint len;
1054 guchar *rep_data;
1055 gchar c_val;
1056
1057 undo_stack_ascend(doc);
1058
1059 cd = (HexChangeData *)doc->undo_top->data;
1060
1061 switch(cd->type) {
1062 case HEX_CHANGE_BYTE:
1063 if(cd->start >= 0 && cd->end <= doc->file_size) {
1064 c_val = hex_document_get_byte(doc, cd->start);
1065 if(cd->rep_len > 0)
1066 hex_document_set_byte(doc, cd->v_byte, cd->start, FALSE, FALSE);
1067 else if(cd->rep_len == 0)
1068 hex_document_set_byte(doc, cd->v_byte, cd->start, cd->insert, FALSE);
1069 #if 0
1070 hex_document_delete_data(doc, cd->start, 1, FALSE);
1071 #endif
1072 else
1073 hex_document_set_byte(doc, cd->v_byte, cd->start, TRUE, FALSE);
1074 cd->v_byte = c_val;
1075 }
1076 break;
1077 case HEX_CHANGE_STRING:
1078 len = cd->end - cd->start + 1;
1079 rep_data = hex_document_get_data(doc, cd->start, len);
1080 hex_document_set_data(doc, cd->start, cd->rep_len, len, cd->v_string, FALSE);
1081 g_free(cd->v_string);
1082 cd->end = cd->start + cd->rep_len - 1;
1083 cd->rep_len = len;
1084 cd->v_string = rep_data;
1085 break;
1086 }
1087
1088 hex_document_changed(doc, cd, FALSE);
1089 }
1090
1091 const GList *
hex_document_get_list()1092 hex_document_get_list()
1093 {
1094 return doc_list;
1095 }
1096