Split file src/keybind.[ch] to lib/keybind.[ch] and src/keybind-defaults.[ch].
[midnight-commander.git] / src / viewer / hex.c
blob202f38db58d275820b9eae7fcd4f252ae52e2a85
1 /*
2 Internal file viewer for the Midnight Commander
3 Function for hex view
5 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
6 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
8 Written by: 1994, 1995, 1998 Miguel de Icaza
9 1994, 1995 Janne Kukonlehto
10 1995 Jakub Jelinek
11 1996 Joseph M. Hinkle
12 1997 Norbert Warmuth
13 1998 Pavel Machek
14 2004 Roland Illig <roland.illig@gmx.de>
15 2005 Roland Illig <roland.illig@gmx.de>
16 2009 Slava Zanko <slavazanko@google.com>
17 2009 Andrew Borodin <aborodin@vmail.ru>
18 2009 Ilia Maslakov <il.smind@gmail.com>
20 This file is part of the Midnight Commander.
22 The Midnight Commander is free software; you can redistribute it
23 and/or modify it under the terms of the GNU General Public License as
24 published by the Free Software Foundation; either version 2 of the
25 License, or (at your option) any later version.
27 The Midnight Commander is distributed in the hope that it will be
28 useful, but WITHOUT ANY WARRANTY; without even the implied warranty
29 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30 General Public License for more details.
32 You should have received a copy of the GNU General Public License
33 along with this program; if not, write to the Free Software
34 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
35 MA 02110-1301, USA.
38 #include <config.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <stdint.h> /* uintmax_t */
44 #include "lib/global.h"
45 #include "lib/tty/tty.h"
46 #include "lib/skin.h"
47 #include "lib/vfs/mc-vfs/vfs.h"
48 #include "lib/util.h"
49 #include "lib/widget.h"
50 #include "lib/charsets.h"
52 #include "src/main.h"
54 #include "internal.h"
56 /*** global variables ****************************************************************************/
58 /*** file scope macro definitions ****************************************************************/
60 /*** file scope type declarations ****************************************************************/
62 typedef enum
64 MARK_NORMAL,
65 MARK_SELECTED,
66 MARK_CURSOR,
67 MARK_CHANGED
68 } mark_t;
70 /*** file scope variables ************************************************************************/
72 static const char hex_char[] = "0123456789ABCDEF";
74 /*** file scope functions ************************************************************************/
75 /* --------------------------------------------------------------------------------------------- */
77 static int
78 utf8_to_int (char *str, int *char_width, gboolean * result)
80 int res = -1;
81 gunichar ch;
82 gchar *next_ch = NULL;
83 int width = 0;
85 *result = TRUE;
87 if (str == NULL)
89 *result = FALSE;
90 width = 0;
91 return 0;
94 res = g_utf8_get_char_validated (str, -1);
96 if (res < 0)
98 ch = *str;
99 width = 0;
101 else
103 ch = res;
104 /* Calculate UTF-8 char width */
105 next_ch = g_utf8_next_char (str);
106 if (next_ch)
108 width = next_ch - str;
110 else
112 ch = 0;
113 width = 0;
116 *char_width = width;
117 return ch;
120 /* --------------------------------------------------------------------------------------------- */
121 /*** public functions ****************************************************************************/
122 /* --------------------------------------------------------------------------------------------- */
124 void
125 mcview_display_hex (mcview_t * view)
127 const screen_dimen top = view->data_area.top;
128 const screen_dimen left = view->data_area.left;
129 const screen_dimen height = view->data_area.height;
130 const screen_dimen width = view->data_area.width;
131 const int ngroups = view->bytes_per_line / 4;
132 const screen_dimen text_start = 8 + 13 * ngroups + ((width < 80) ? 0 : (ngroups - 1 + 1));
133 /* 8 characters are used for the file offset, and every hex group
134 * takes 13 characters. On ``big'' screens, the groups are separated
135 * by an extra vertical line, and there is an extra space before the
136 * text column.
139 screen_dimen row, col;
140 off_t from;
141 int c;
142 mark_t boldflag = MARK_NORMAL;
143 struct hexedit_change_node *curr = view->change_list;
144 int ch = 0;
146 char hex_buff[10]; /* A temporary buffer for sprintf and mvwaddstr */
147 int bytes; /* Number of bytes already printed on the line */
149 mcview_display_clean (view);
151 /* Find the first displayable changed byte */
152 from = view->dpy_start;
153 while (curr && (curr->offset < from))
155 curr = curr->next;
158 for (row = 0; mcview_get_byte (view, from, NULL) == TRUE && row < height; row++)
160 size_t i;
161 col = 0;
163 /* Print the hex offset */
164 g_snprintf (hex_buff, sizeof (hex_buff), "%08jX ", (uintmax_t) from);
165 widget_move (view, top + row, left);
166 tty_setcolor (VIEW_BOLD_COLOR);
167 for (i = 0; col < width && hex_buff[i] != '\0'; i++)
169 tty_print_char (hex_buff[i]);
170 /* tty_print_char(hex_buff[i]); */
171 col += 1;
173 tty_setcolor (NORMAL_COLOR);
175 for (bytes = 0; bytes < view->bytes_per_line; bytes++, from++)
178 #ifdef HAVE_CHARSET
179 if (view->utf8)
181 char corr_buf[6 + 1];
182 int cnt, cw = 1;
183 gboolean read_res = TRUE;
184 ch = mcview_get_utf (view, from, &cw, &read_res);
185 if (!read_res)
186 break;
187 /* char width is greater 0 bytes */
188 if (cw != 0)
190 struct hexedit_change_node *corr = curr;
191 int res = g_unichar_to_utf8 (ch, (char *) corr_buf);
192 for (cnt = 0; cnt < cw; cnt++)
194 if (curr != NULL && from + cnt == curr->offset)
196 /* replace only changed bytes in array of multibyte char */
197 corr_buf[cnt] = curr->value;
198 curr = curr->next;
201 corr_buf[res] = '\0';
202 /* Determine the state of the current multibyte char */
203 ch = utf8_to_int ((char *) corr_buf, &cw, &read_res);
204 curr = corr;
207 #endif
208 if (!mcview_get_byte (view, from, &c))
209 break;
211 /* Save the cursor position for mcview_place_cursor() */
212 if (from == view->hex_cursor && !view->hexview_in_text)
214 view->cursor_row = row;
215 view->cursor_col = col;
218 /* Determine the state of the current byte */
219 boldflag =
220 (from == view->hex_cursor) ? MARK_CURSOR
221 : (curr != NULL && from == curr->offset) ? MARK_CHANGED
222 : (view->search_start <= from &&
223 from < view->search_end) ? MARK_SELECTED : MARK_NORMAL;
225 /* Determine the value of the current byte */
226 if (curr != NULL && from == curr->offset)
228 c = curr->value;
229 curr = curr->next;
232 /* Select the color for the hex number */
233 tty_setcolor (boldflag == MARK_NORMAL ? NORMAL_COLOR :
234 boldflag == MARK_SELECTED ? VIEW_BOLD_COLOR :
235 boldflag == MARK_CHANGED ? VIEW_UNDERLINED_COLOR :
236 /* boldflag == MARK_CURSOR */
237 view->hexview_in_text ? VIEW_SELECTED_COLOR : VIEW_UNDERLINED_COLOR);
239 /* Print the hex number */
240 widget_move (view, top + row, left + col);
241 if (col < width)
243 tty_print_char (hex_char[c / 16]);
244 col += 1;
246 if (col < width)
248 tty_print_char (hex_char[c % 16]);
249 col += 1;
252 /* Print the separator */
253 tty_setcolor (NORMAL_COLOR);
254 if (bytes != view->bytes_per_line - 1)
256 if (col < width)
258 tty_print_char (' ');
259 col += 1;
262 /* After every four bytes, print a group separator */
263 if (bytes % 4 == 3)
265 if (view->data_area.width >= 80 && col < width)
267 tty_print_one_vline (TRUE);
268 col += 1;
270 if (col < width)
272 tty_print_char (' ');
273 col += 1;
278 /* Select the color for the character; this differs from the
279 * hex color when boldflag == MARK_CURSOR */
280 tty_setcolor (boldflag == MARK_NORMAL ? NORMAL_COLOR :
281 boldflag == MARK_SELECTED ? VIEW_BOLD_COLOR :
282 boldflag == MARK_CHANGED ? VIEW_UNDERLINED_COLOR :
283 /* boldflag == MARK_CURSOR */
284 view->hexview_in_text ? VIEW_SELECTED_COLOR : MARKED_SELECTED_COLOR);
287 #ifdef HAVE_CHARSET
288 if (utf8_display)
290 if (!view->utf8)
292 c = convert_from_8bit_to_utf_c ((unsigned char) c, view->converter);
294 if (!g_unichar_isprint (c))
295 c = '.';
297 else if (view->utf8)
298 ch = convert_from_utf_to_current_c (ch, view->converter);
299 else
300 #endif
302 c = convert_to_display_c (c);
304 if (!is_printable (c))
305 c = '.';
308 /* Print corresponding character on the text side */
309 if (text_start + bytes < width)
311 widget_move (view, top + row, left + text_start + bytes);
312 #ifdef HAVE_CHARSET
313 if (view->utf8)
314 tty_print_anychar (ch);
315 else
316 #endif
318 tty_print_char (c);
322 /* Save the cursor position for mcview_place_cursor() */
323 if (from == view->hex_cursor && view->hexview_in_text)
325 view->cursor_row = row;
326 view->cursor_col = text_start + bytes;
331 /* Be polite to the other functions */
332 tty_setcolor (NORMAL_COLOR);
334 mcview_place_cursor (view);
335 view->dpy_end = from;
338 /* --------------------------------------------------------------------------------------------- */
340 gboolean
341 mcview_hexedit_save_changes (mcview_t * view)
343 int answer = 0;
345 if (view->change_list == NULL)
346 return TRUE;
348 while (answer == 0)
350 int fp;
351 char *text;
352 struct hexedit_change_node *curr, *next;
354 assert (view->filename != NULL);
356 fp = mc_open (view->filename, O_WRONLY);
357 if (fp != -1)
359 for (curr = view->change_list; curr != NULL; curr = next)
361 next = curr->next;
363 if (mc_lseek (fp, curr->offset, SEEK_SET) == -1
364 || mc_write (fp, &(curr->value), 1) != 1)
365 goto save_error;
367 /* delete the saved item from the change list */
368 view->change_list = next;
369 view->dirty++;
370 mcview_set_byte (view, curr->offset, curr->value);
371 g_free (curr);
374 view->change_list = NULL;
376 if (view->locked)
377 view->locked = mcview_unlock_file (view);
379 if (mc_close (fp) == -1)
380 message (D_ERROR, _("Save file"),
381 _("Error while closing the file:\n%s\n"
382 "Data may have been written or not"), unix_error_string (errno));
384 view->dirty++;
385 return TRUE;
388 save_error:
389 text = g_strdup_printf (_("Cannot save file:\n%s"), unix_error_string (errno));
390 (void) mc_close (fp);
392 answer = query_dialog (_("Save file"), text, D_ERROR, 2, _("&Retry"), _("&Cancel"));
393 g_free (text);
396 return FALSE;
399 /* --------------------------------------------------------------------------------------------- */
401 void
402 mcview_toggle_hexedit_mode (mcview_t * view)
404 view->hexedit_mode = !view->hexedit_mode;
405 view->dpy_bbar_dirty = TRUE;
406 view->dirty++;
409 /* --------------------------------------------------------------------------------------------- */
411 void
412 mcview_hexedit_free_change_list (mcview_t * view)
414 struct hexedit_change_node *curr, *next;
416 for (curr = view->change_list; curr != NULL; curr = next)
418 next = curr->next;
419 g_free (curr);
421 view->change_list = NULL;
423 if (view->locked)
424 view->locked = mcview_unlock_file (view);
426 view->dirty++;
429 /* --------------------------------------------------------------------------------------------- */
431 void
432 mcview_enqueue_change (struct hexedit_change_node **head, struct hexedit_change_node *node)
434 /* chnode always either points to the head of the list or
435 * to one of the ->next fields in the list. The value at
436 * this location will be overwritten with the new node. */
437 struct hexedit_change_node **chnode = head;
439 while (*chnode != NULL && (*chnode)->offset < node->offset)
440 chnode = &((*chnode)->next);
442 node->next = *chnode;
443 *chnode = node;
446 /* --------------------------------------------------------------------------------------------- */