Core, mceditor, mcviewer and mcdiffviewer code tweak and cleanup
[midnight-commander.git] / src / viewer / hex.c
blob2431457fb78fd4bce7de3deb04b97c42a6fd257e
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/lock.h" /* lock_file() and unlock_file() */
48 #include "lib/util.h"
49 #include "lib/widget.h"
50 #ifdef HAVE_CHARSET
51 #include "lib/charsets.h"
52 #endif
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 #ifdef HAVE_CHARSET
78 static int
79 utf8_to_int (char *str, int *char_width, gboolean * result)
81 int res = -1;
82 gunichar ch;
83 gchar *next_ch = NULL;
84 int width = 0;
86 *result = TRUE;
88 if (str == NULL)
90 *result = FALSE;
91 width = 0;
92 return 0;
95 res = g_utf8_get_char_validated (str, -1);
97 if (res < 0)
99 ch = *str;
100 width = 0;
102 else
104 ch = res;
105 /* Calculate UTF-8 char width */
106 next_ch = g_utf8_next_char (str);
107 if (next_ch)
109 width = next_ch - str;
111 else
113 ch = 0;
114 width = 0;
117 *char_width = width;
118 return ch;
120 #endif /* HAVE_CHARSET */
122 /* --------------------------------------------------------------------------------------------- */
123 /*** public functions ****************************************************************************/
124 /* --------------------------------------------------------------------------------------------- */
126 void
127 mcview_display_hex (mcview_t * view)
129 const screen_dimen top = view->data_area.top;
130 const screen_dimen left = view->data_area.left;
131 const screen_dimen height = view->data_area.height;
132 const screen_dimen width = view->data_area.width;
133 const int ngroups = view->bytes_per_line / 4;
134 const screen_dimen text_start = 8 + 13 * ngroups + ((width < 80) ? 0 : (ngroups - 1 + 1));
135 /* 8 characters are used for the file offset, and every hex group
136 * takes 13 characters. On ``big'' screens, the groups are separated
137 * by an extra vertical line, and there is an extra space before the
138 * text column.
141 screen_dimen row, col;
142 off_t from;
143 int c;
144 mark_t boldflag = MARK_NORMAL;
145 struct hexedit_change_node *curr = view->change_list;
146 #ifdef HAVE_CHARSET
147 int ch = 0;
148 #endif /* HAVE_CHARSET */
150 char hex_buff[10]; /* A temporary buffer for sprintf and mvwaddstr */
151 int bytes; /* Number of bytes already printed on the line */
153 mcview_display_clean (view);
155 /* Find the first displayable changed byte */
156 from = view->dpy_start;
157 while (curr && (curr->offset < from))
159 curr = curr->next;
162 for (row = 0; mcview_get_byte (view, from, NULL) == TRUE && row < height; row++)
164 size_t i;
165 col = 0;
167 /* Print the hex offset */
168 g_snprintf (hex_buff, sizeof (hex_buff), "%08" PRIXMAX " ", (uintmax_t) from);
169 widget_move (view, top + row, left);
170 tty_setcolor (VIEW_BOLD_COLOR);
171 for (i = 0; col < width && hex_buff[i] != '\0'; i++)
173 tty_print_char (hex_buff[i]);
174 /* tty_print_char(hex_buff[i]); */
175 col += 1;
177 tty_setcolor (NORMAL_COLOR);
179 for (bytes = 0; bytes < view->bytes_per_line; bytes++, from++)
182 #ifdef HAVE_CHARSET
183 if (view->utf8)
185 char corr_buf[6 + 1];
186 int cnt, cw = 1;
187 gboolean read_res = TRUE;
188 ch = mcview_get_utf (view, from, &cw, &read_res);
189 if (!read_res)
190 break;
191 /* char width is greater 0 bytes */
192 if (cw != 0)
194 struct hexedit_change_node *corr = curr;
195 int res = g_unichar_to_utf8 (ch, (char *) corr_buf);
196 for (cnt = 0; cnt < cw; cnt++)
198 if (curr != NULL && from + cnt == curr->offset)
200 /* replace only changed bytes in array of multibyte char */
201 corr_buf[cnt] = curr->value;
202 curr = curr->next;
205 corr_buf[res] = '\0';
206 /* Determine the state of the current multibyte char */
207 ch = utf8_to_int ((char *) corr_buf, &cw, &read_res);
208 curr = corr;
211 #endif /* HAVE_CHARSET */
212 if (!mcview_get_byte (view, from, &c))
213 break;
215 /* Save the cursor position for mcview_place_cursor() */
216 if (from == view->hex_cursor && !view->hexview_in_text)
218 view->cursor_row = row;
219 view->cursor_col = col;
222 /* Determine the state of the current byte */
223 boldflag =
224 (from == view->hex_cursor) ? MARK_CURSOR
225 : (curr != NULL && from == curr->offset) ? MARK_CHANGED
226 : (view->search_start <= from &&
227 from < view->search_end) ? MARK_SELECTED : MARK_NORMAL;
229 /* Determine the value of the current byte */
230 if (curr != NULL && from == curr->offset)
232 c = curr->value;
233 curr = curr->next;
236 /* Select the color for the hex number */
237 tty_setcolor (boldflag == MARK_NORMAL ? NORMAL_COLOR :
238 boldflag == MARK_SELECTED ? VIEW_BOLD_COLOR :
239 boldflag == MARK_CHANGED ? VIEW_UNDERLINED_COLOR :
240 /* boldflag == MARK_CURSOR */
241 view->hexview_in_text ? VIEW_SELECTED_COLOR : VIEW_UNDERLINED_COLOR);
243 /* Print the hex number */
244 widget_move (view, top + row, left + col);
245 if (col < width)
247 tty_print_char (hex_char[c / 16]);
248 col += 1;
250 if (col < width)
252 tty_print_char (hex_char[c % 16]);
253 col += 1;
256 /* Print the separator */
257 tty_setcolor (NORMAL_COLOR);
258 if (bytes != view->bytes_per_line - 1)
260 if (col < width)
262 tty_print_char (' ');
263 col += 1;
266 /* After every four bytes, print a group separator */
267 if (bytes % 4 == 3)
269 if (view->data_area.width >= 80 && col < width)
271 tty_print_one_vline (TRUE);
272 col += 1;
274 if (col < width)
276 tty_print_char (' ');
277 col += 1;
282 /* Select the color for the character; this differs from the
283 * hex color when boldflag == MARK_CURSOR */
284 tty_setcolor (boldflag == MARK_NORMAL ? NORMAL_COLOR :
285 boldflag == MARK_SELECTED ? VIEW_BOLD_COLOR :
286 boldflag == MARK_CHANGED ? VIEW_UNDERLINED_COLOR :
287 /* boldflag == MARK_CURSOR */
288 view->hexview_in_text ? VIEW_SELECTED_COLOR : MARKED_SELECTED_COLOR);
291 #ifdef HAVE_CHARSET
292 if (mc_global.utf8_display)
294 if (!view->utf8)
296 c = convert_from_8bit_to_utf_c ((unsigned char) c, view->converter);
298 if (!g_unichar_isprint (c))
299 c = '.';
301 else if (view->utf8)
302 ch = convert_from_utf_to_current_c (ch, view->converter);
303 else
304 #endif
306 #ifdef HAVE_CHARSET
307 c = convert_to_display_c (c);
308 #endif
310 if (!is_printable (c))
311 c = '.';
314 /* Print corresponding character on the text side */
315 if (text_start + bytes < width)
317 widget_move (view, top + row, left + text_start + bytes);
318 #ifdef HAVE_CHARSET
319 if (view->utf8)
320 tty_print_anychar (ch);
321 else
322 #endif
323 tty_print_char (c);
326 /* Save the cursor position for mcview_place_cursor() */
327 if (from == view->hex_cursor && view->hexview_in_text)
329 view->cursor_row = row;
330 view->cursor_col = text_start + bytes;
335 /* Be polite to the other functions */
336 tty_setcolor (NORMAL_COLOR);
338 mcview_place_cursor (view);
339 view->dpy_end = from;
342 /* --------------------------------------------------------------------------------------------- */
344 gboolean
345 mcview_hexedit_save_changes (mcview_t * view)
347 int answer = 0;
349 if (view->change_list == NULL)
350 return TRUE;
352 while (answer == 0)
354 int fp;
355 char *text;
356 struct hexedit_change_node *curr, *next;
358 #ifdef HAVE_ASSERT_H
359 assert (view->filename_vpath != NULL);
360 #endif
362 fp = mc_open (view->filename_vpath, O_WRONLY);
363 if (fp != -1)
365 for (curr = view->change_list; curr != NULL; curr = next)
367 next = curr->next;
369 if (mc_lseek (fp, curr->offset, SEEK_SET) == -1
370 || mc_write (fp, &(curr->value), 1) != 1)
371 goto save_error;
373 /* delete the saved item from the change list */
374 view->change_list = next;
375 view->dirty++;
376 mcview_set_byte (view, curr->offset, curr->value);
377 g_free (curr);
380 view->change_list = NULL;
382 if (view->locked)
383 view->locked = unlock_file (view->filename_vpath);
385 if (mc_close (fp) == -1)
386 message (D_ERROR, _("Save file"),
387 _("Error while closing the file:\n%s\n"
388 "Data may have been written or not"), unix_error_string (errno));
390 view->dirty++;
391 return TRUE;
394 save_error:
395 text = g_strdup_printf (_("Cannot save file:\n%s"), unix_error_string (errno));
396 (void) mc_close (fp);
398 answer = query_dialog (_("Save file"), text, D_ERROR, 2, _("&Retry"), _("&Cancel"));
399 g_free (text);
402 return FALSE;
405 /* --------------------------------------------------------------------------------------------- */
407 void
408 mcview_toggle_hexedit_mode (mcview_t * view)
410 view->hexedit_mode = !view->hexedit_mode;
411 view->dpy_bbar_dirty = TRUE;
412 view->dirty++;
415 /* --------------------------------------------------------------------------------------------- */
417 void
418 mcview_hexedit_free_change_list (mcview_t * view)
420 struct hexedit_change_node *curr, *next;
422 for (curr = view->change_list; curr != NULL; curr = next)
424 next = curr->next;
425 g_free (curr);
427 view->change_list = NULL;
429 if (view->locked)
430 view->locked = unlock_file (view->filename_vpath);
432 view->dirty++;
435 /* --------------------------------------------------------------------------------------------- */
437 void
438 mcview_enqueue_change (struct hexedit_change_node **head, struct hexedit_change_node *node)
440 /* chnode always either points to the head of the list or
441 * to one of the ->next fields in the list. The value at
442 * this location will be overwritten with the new node. */
443 struct hexedit_change_node **chnode = head;
445 while (*chnode != NULL && (*chnode)->offset < node->offset)
446 chnode = &((*chnode)->next);
448 node->next = *chnode;
449 *chnode = node;
452 /* --------------------------------------------------------------------------------------------- */