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\">&nbsp;</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>&nbsp;</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, "&nbsp;");
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, "&nbsp;");
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