Changed interface of function mc_open() for handle vfs_path_t object as parameter
[midnight-commander.git] / src / viewer / hex.c
blob00752fb001ab314c23c8958bb348db0886640350
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, 2011
7 The Free Software Foundation, Inc.
9 Written by:
10 Miguel de Icaza, 1994, 1995, 1998
11 Janne Kukonlehto, 1994, 1995
12 Jakub Jelinek, 1995
13 Joseph M. Hinkle, 1996
14 Norbert Warmuth, 1997
15 Pavel Machek, 1998
16 Roland Illig <roland.illig@gmx.de>, 2004, 2005
17 Slava Zanko <slavazanko@google.com>, 2009
18 Andrew Borodin <aborodin@vmail.ru>, 2009
19 Ilia Maslakov <il.smind@gmail.com>, 2009
21 This file is part of the Midnight Commander.
23 The Midnight Commander is free software: you can redistribute it
24 and/or modify it under the terms of the GNU General Public License as
25 published by the Free Software Foundation, either version 3 of the License,
26 or (at your option) any later version.
28 The Midnight Commander is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 GNU General Public License for more details.
33 You should have received a copy of the GNU General Public License
34 along with this program. If not, see <http://www.gnu.org/licenses/>.
37 #include <config.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <inttypes.h> /* uintmax_t */
43 #include "lib/global.h"
44 #include "lib/tty/tty.h"
45 #include "lib/skin.h"
46 #include "lib/vfs/vfs.h"
47 #include "lib/util.h"
48 #include "lib/widget.h"
49 #include "lib/charsets.h"
51 #include "internal.h"
53 /*** global variables ****************************************************************************/
55 /*** file scope macro definitions ****************************************************************/
57 /*** file scope type declarations ****************************************************************/
59 typedef enum
61 MARK_NORMAL,
62 MARK_SELECTED,
63 MARK_CURSOR,
64 MARK_CHANGED
65 } mark_t;
67 /*** file scope variables ************************************************************************/
69 static const char hex_char[] = "0123456789ABCDEF";
71 /*** file scope functions ************************************************************************/
72 /* --------------------------------------------------------------------------------------------- */
74 static int
75 utf8_to_int (char *str, int *char_width, gboolean * result)
77 int res = -1;
78 gunichar ch;
79 gchar *next_ch = NULL;
80 int width = 0;
82 *result = TRUE;
84 if (str == NULL)
86 *result = FALSE;
87 width = 0;
88 return 0;
91 res = g_utf8_get_char_validated (str, -1);
93 if (res < 0)
95 ch = *str;
96 width = 0;
98 else
100 ch = res;
101 /* Calculate UTF-8 char width */
102 next_ch = g_utf8_next_char (str);
103 if (next_ch)
105 width = next_ch - str;
107 else
109 ch = 0;
110 width = 0;
113 *char_width = width;
114 return ch;
117 /* --------------------------------------------------------------------------------------------- */
118 /*** public functions ****************************************************************************/
119 /* --------------------------------------------------------------------------------------------- */
121 void
122 mcview_display_hex (mcview_t * view)
124 const screen_dimen top = view->data_area.top;
125 const screen_dimen left = view->data_area.left;
126 const screen_dimen height = view->data_area.height;
127 const screen_dimen width = view->data_area.width;
128 const int ngroups = view->bytes_per_line / 4;
129 const screen_dimen text_start = 8 + 13 * ngroups + ((width < 80) ? 0 : (ngroups - 1 + 1));
130 /* 8 characters are used for the file offset, and every hex group
131 * takes 13 characters. On ``big'' screens, the groups are separated
132 * by an extra vertical line, and there is an extra space before the
133 * text column.
136 screen_dimen row, col;
137 off_t from;
138 int c;
139 mark_t boldflag = MARK_NORMAL;
140 struct hexedit_change_node *curr = view->change_list;
141 int ch = 0;
143 char hex_buff[10]; /* A temporary buffer for sprintf and mvwaddstr */
144 int bytes; /* Number of bytes already printed on the line */
146 mcview_display_clean (view);
148 /* Find the first displayable changed byte */
149 from = view->dpy_start;
150 while (curr && (curr->offset < from))
152 curr = curr->next;
155 for (row = 0; mcview_get_byte (view, from, NULL) == TRUE && row < height; row++)
157 size_t i;
158 col = 0;
160 /* Print the hex offset */
161 g_snprintf (hex_buff, sizeof (hex_buff), "%08" PRIXMAX " ", (uintmax_t) from);
162 widget_move (view, top + row, left);
163 tty_setcolor (VIEW_BOLD_COLOR);
164 for (i = 0; col < width && hex_buff[i] != '\0'; i++)
166 tty_print_char (hex_buff[i]);
167 /* tty_print_char(hex_buff[i]); */
168 col += 1;
170 tty_setcolor (NORMAL_COLOR);
172 for (bytes = 0; bytes < view->bytes_per_line; bytes++, from++)
175 #ifdef HAVE_CHARSET
176 if (view->utf8)
178 char corr_buf[6 + 1];
179 int cnt, cw = 1;
180 gboolean read_res = TRUE;
181 ch = mcview_get_utf (view, from, &cw, &read_res);
182 if (!read_res)
183 break;
184 /* char width is greater 0 bytes */
185 if (cw != 0)
187 struct hexedit_change_node *corr = curr;
188 int res = g_unichar_to_utf8 (ch, (char *) corr_buf);
189 for (cnt = 0; cnt < cw; cnt++)
191 if (curr != NULL && from + cnt == curr->offset)
193 /* replace only changed bytes in array of multibyte char */
194 corr_buf[cnt] = curr->value;
195 curr = curr->next;
198 corr_buf[res] = '\0';
199 /* Determine the state of the current multibyte char */
200 ch = utf8_to_int ((char *) corr_buf, &cw, &read_res);
201 curr = corr;
204 #endif
205 if (!mcview_get_byte (view, from, &c))
206 break;
208 /* Save the cursor position for mcview_place_cursor() */
209 if (from == view->hex_cursor && !view->hexview_in_text)
211 view->cursor_row = row;
212 view->cursor_col = col;
215 /* Determine the state of the current byte */
216 boldflag =
217 (from == view->hex_cursor) ? MARK_CURSOR
218 : (curr != NULL && from == curr->offset) ? MARK_CHANGED
219 : (view->search_start <= from &&
220 from < view->search_end) ? MARK_SELECTED : MARK_NORMAL;
222 /* Determine the value of the current byte */
223 if (curr != NULL && from == curr->offset)
225 c = curr->value;
226 curr = curr->next;
229 /* Select the color for the hex number */
230 tty_setcolor (boldflag == MARK_NORMAL ? NORMAL_COLOR :
231 boldflag == MARK_SELECTED ? VIEW_BOLD_COLOR :
232 boldflag == MARK_CHANGED ? VIEW_UNDERLINED_COLOR :
233 /* boldflag == MARK_CURSOR */
234 view->hexview_in_text ? VIEW_SELECTED_COLOR : VIEW_UNDERLINED_COLOR);
236 /* Print the hex number */
237 widget_move (view, top + row, left + col);
238 if (col < width)
240 tty_print_char (hex_char[c / 16]);
241 col += 1;
243 if (col < width)
245 tty_print_char (hex_char[c % 16]);
246 col += 1;
249 /* Print the separator */
250 tty_setcolor (NORMAL_COLOR);
251 if (bytes != view->bytes_per_line - 1)
253 if (col < width)
255 tty_print_char (' ');
256 col += 1;
259 /* After every four bytes, print a group separator */
260 if (bytes % 4 == 3)
262 if (view->data_area.width >= 80 && col < width)
264 tty_print_one_vline (TRUE);
265 col += 1;
267 if (col < width)
269 tty_print_char (' ');
270 col += 1;
275 /* Select the color for the character; this differs from the
276 * hex color when boldflag == MARK_CURSOR */
277 tty_setcolor (boldflag == MARK_NORMAL ? NORMAL_COLOR :
278 boldflag == MARK_SELECTED ? VIEW_BOLD_COLOR :
279 boldflag == MARK_CHANGED ? VIEW_UNDERLINED_COLOR :
280 /* boldflag == MARK_CURSOR */
281 view->hexview_in_text ? VIEW_SELECTED_COLOR : MARKED_SELECTED_COLOR);
284 #ifdef HAVE_CHARSET
285 if (mc_global.utf8_display)
287 if (!view->utf8)
289 c = convert_from_8bit_to_utf_c ((unsigned char) c, view->converter);
291 if (!g_unichar_isprint (c))
292 c = '.';
294 else if (view->utf8)
295 ch = convert_from_utf_to_current_c (ch, view->converter);
296 else
297 #endif
299 c = convert_to_display_c (c);
301 if (!is_printable (c))
302 c = '.';
305 /* Print corresponding character on the text side */
306 if (text_start + bytes < width)
308 widget_move (view, top + row, left + text_start + bytes);
309 #ifdef HAVE_CHARSET
310 if (view->utf8)
311 tty_print_anychar (ch);
312 else
313 #endif
315 tty_print_char (c);
319 /* Save the cursor position for mcview_place_cursor() */
320 if (from == view->hex_cursor && view->hexview_in_text)
322 view->cursor_row = row;
323 view->cursor_col = text_start + bytes;
328 /* Be polite to the other functions */
329 tty_setcolor (NORMAL_COLOR);
331 mcview_place_cursor (view);
332 view->dpy_end = from;
335 /* --------------------------------------------------------------------------------------------- */
337 gboolean
338 mcview_hexedit_save_changes (mcview_t * view)
340 int answer = 0;
342 if (view->change_list == NULL)
343 return TRUE;
345 while (answer == 0)
347 int fp;
348 char *text;
349 struct hexedit_change_node *curr, *next;
350 vfs_path_t *tmp_vpath;
352 #ifdef HAVE_ASSERT_H
353 assert (view->filename != NULL);
354 #endif
356 tmp_vpath = vfs_path_from_str (view->filename);
357 fp = mc_open (tmp_vpath, O_WRONLY);
358 vfs_path_free (tmp_vpath);
359 if (fp != -1)
361 for (curr = view->change_list; curr != NULL; curr = next)
363 next = curr->next;
365 if (mc_lseek (fp, curr->offset, SEEK_SET) == -1
366 || mc_write (fp, &(curr->value), 1) != 1)
367 goto save_error;
369 /* delete the saved item from the change list */
370 view->change_list = next;
371 view->dirty++;
372 mcview_set_byte (view, curr->offset, curr->value);
373 g_free (curr);
376 view->change_list = NULL;
378 if (view->locked)
379 view->locked = mcview_unlock_file (view);
381 if (mc_close (fp) == -1)
382 message (D_ERROR, _("Save file"),
383 _("Error while closing the file:\n%s\n"
384 "Data may have been written or not"), unix_error_string (errno));
386 view->dirty++;
387 return TRUE;
390 save_error:
391 text = g_strdup_printf (_("Cannot save file:\n%s"), unix_error_string (errno));
392 (void) mc_close (fp);
394 answer = query_dialog (_("Save file"), text, D_ERROR, 2, _("&Retry"), _("&Cancel"));
395 g_free (text);
398 return FALSE;
401 /* --------------------------------------------------------------------------------------------- */
403 void
404 mcview_toggle_hexedit_mode (mcview_t * view)
406 view->hexedit_mode = !view->hexedit_mode;
407 view->dpy_bbar_dirty = TRUE;
408 view->dirty++;
411 /* --------------------------------------------------------------------------------------------- */
413 void
414 mcview_hexedit_free_change_list (mcview_t * view)
416 struct hexedit_change_node *curr, *next;
418 for (curr = view->change_list; curr != NULL; curr = next)
420 next = curr->next;
421 g_free (curr);
423 view->change_list = NULL;
425 if (view->locked)
426 view->locked = mcview_unlock_file (view);
428 view->dirty++;
431 /* --------------------------------------------------------------------------------------------- */
433 void
434 mcview_enqueue_change (struct hexedit_change_node **head, struct hexedit_change_node *node)
436 /* chnode always either points to the head of the list or
437 * to one of the ->next fields in the list. The value at
438 * this location will be overwritten with the new node. */
439 struct hexedit_change_node **chnode = head;
441 while (*chnode != NULL && (*chnode)->offset < node->offset)
442 chnode = &((*chnode)->next);
444 node->next = *chnode;
445 *chnode = node;
448 /* --------------------------------------------------------------------------------------------- */