(get_paragraph): fix of pointer difference.
[midnight-commander.git] / src / viewer / hex.c
blobb5645b4882687cf4f3fd9047c34bf6c0d01b0890
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 <inttypes.h> /* uintmax_t */
44 #include "lib/global.h"
45 #include "lib/tty/tty.h"
46 #include "lib/skin.h"
47 #include "lib/vfs/vfs.h"
48 #include "lib/util.h"
49 #include "lib/widget.h"
50 #include "lib/charsets.h"
52 #include "internal.h"
54 /*** global variables ****************************************************************************/
56 /*** file scope macro definitions ****************************************************************/
58 /*** file scope type declarations ****************************************************************/
60 typedef enum
62 MARK_NORMAL,
63 MARK_SELECTED,
64 MARK_CURSOR,
65 MARK_CHANGED
66 } mark_t;
68 /*** file scope variables ************************************************************************/
70 static const char hex_char[] = "0123456789ABCDEF";
72 /*** file scope functions ************************************************************************/
73 /* --------------------------------------------------------------------------------------------- */
75 static int
76 utf8_to_int (char *str, int *char_width, gboolean * result)
78 int res = -1;
79 gunichar ch;
80 gchar *next_ch = NULL;
81 int width = 0;
83 *result = TRUE;
85 if (str == NULL)
87 *result = FALSE;
88 width = 0;
89 return 0;
92 res = g_utf8_get_char_validated (str, -1);
94 if (res < 0)
96 ch = *str;
97 width = 0;
99 else
101 ch = res;
102 /* Calculate UTF-8 char width */
103 next_ch = g_utf8_next_char (str);
104 if (next_ch)
106 width = next_ch - str;
108 else
110 ch = 0;
111 width = 0;
114 *char_width = width;
115 return ch;
118 /* --------------------------------------------------------------------------------------------- */
119 /*** public functions ****************************************************************************/
120 /* --------------------------------------------------------------------------------------------- */
122 void
123 mcview_display_hex (mcview_t * view)
125 const screen_dimen top = view->data_area.top;
126 const screen_dimen left = view->data_area.left;
127 const screen_dimen height = view->data_area.height;
128 const screen_dimen width = view->data_area.width;
129 const int ngroups = view->bytes_per_line / 4;
130 const screen_dimen text_start = 8 + 13 * ngroups + ((width < 80) ? 0 : (ngroups - 1 + 1));
131 /* 8 characters are used for the file offset, and every hex group
132 * takes 13 characters. On ``big'' screens, the groups are separated
133 * by an extra vertical line, and there is an extra space before the
134 * text column.
137 screen_dimen row, col;
138 off_t from;
139 int c;
140 mark_t boldflag = MARK_NORMAL;
141 struct hexedit_change_node *curr = view->change_list;
142 int ch = 0;
144 char hex_buff[10]; /* A temporary buffer for sprintf and mvwaddstr */
145 int bytes; /* Number of bytes already printed on the line */
147 mcview_display_clean (view);
149 /* Find the first displayable changed byte */
150 from = view->dpy_start;
151 while (curr && (curr->offset < from))
153 curr = curr->next;
156 for (row = 0; mcview_get_byte (view, from, NULL) == TRUE && row < height; row++)
158 size_t i;
159 col = 0;
161 /* Print the hex offset */
162 g_snprintf (hex_buff, sizeof (hex_buff), "%08" PRIXMAX " ", (uintmax_t) from);
163 widget_move (view, top + row, left);
164 tty_setcolor (VIEW_BOLD_COLOR);
165 for (i = 0; col < width && hex_buff[i] != '\0'; i++)
167 tty_print_char (hex_buff[i]);
168 /* tty_print_char(hex_buff[i]); */
169 col += 1;
171 tty_setcolor (NORMAL_COLOR);
173 for (bytes = 0; bytes < view->bytes_per_line; bytes++, from++)
176 #ifdef HAVE_CHARSET
177 if (view->utf8)
179 char corr_buf[6 + 1];
180 int cnt, cw = 1;
181 gboolean read_res = TRUE;
182 ch = mcview_get_utf (view, from, &cw, &read_res);
183 if (!read_res)
184 break;
185 /* char width is greater 0 bytes */
186 if (cw != 0)
188 struct hexedit_change_node *corr = curr;
189 int res = g_unichar_to_utf8 (ch, (char *) corr_buf);
190 for (cnt = 0; cnt < cw; cnt++)
192 if (curr != NULL && from + cnt == curr->offset)
194 /* replace only changed bytes in array of multibyte char */
195 corr_buf[cnt] = curr->value;
196 curr = curr->next;
199 corr_buf[res] = '\0';
200 /* Determine the state of the current multibyte char */
201 ch = utf8_to_int ((char *) corr_buf, &cw, &read_res);
202 curr = corr;
205 #endif
206 if (!mcview_get_byte (view, from, &c))
207 break;
209 /* Save the cursor position for mcview_place_cursor() */
210 if (from == view->hex_cursor && !view->hexview_in_text)
212 view->cursor_row = row;
213 view->cursor_col = col;
216 /* Determine the state of the current byte */
217 boldflag =
218 (from == view->hex_cursor) ? MARK_CURSOR
219 : (curr != NULL && from == curr->offset) ? MARK_CHANGED
220 : (view->search_start <= from &&
221 from < view->search_end) ? MARK_SELECTED : MARK_NORMAL;
223 /* Determine the value of the current byte */
224 if (curr != NULL && from == curr->offset)
226 c = curr->value;
227 curr = curr->next;
230 /* Select the color for the hex number */
231 tty_setcolor (boldflag == MARK_NORMAL ? NORMAL_COLOR :
232 boldflag == MARK_SELECTED ? VIEW_BOLD_COLOR :
233 boldflag == MARK_CHANGED ? VIEW_UNDERLINED_COLOR :
234 /* boldflag == MARK_CURSOR */
235 view->hexview_in_text ? VIEW_SELECTED_COLOR : VIEW_UNDERLINED_COLOR);
237 /* Print the hex number */
238 widget_move (view, top + row, left + col);
239 if (col < width)
241 tty_print_char (hex_char[c / 16]);
242 col += 1;
244 if (col < width)
246 tty_print_char (hex_char[c % 16]);
247 col += 1;
250 /* Print the separator */
251 tty_setcolor (NORMAL_COLOR);
252 if (bytes != view->bytes_per_line - 1)
254 if (col < width)
256 tty_print_char (' ');
257 col += 1;
260 /* After every four bytes, print a group separator */
261 if (bytes % 4 == 3)
263 if (view->data_area.width >= 80 && col < width)
265 tty_print_one_vline (TRUE);
266 col += 1;
268 if (col < width)
270 tty_print_char (' ');
271 col += 1;
276 /* Select the color for the character; this differs from the
277 * hex color when boldflag == MARK_CURSOR */
278 tty_setcolor (boldflag == MARK_NORMAL ? NORMAL_COLOR :
279 boldflag == MARK_SELECTED ? VIEW_BOLD_COLOR :
280 boldflag == MARK_CHANGED ? VIEW_UNDERLINED_COLOR :
281 /* boldflag == MARK_CURSOR */
282 view->hexview_in_text ? VIEW_SELECTED_COLOR : MARKED_SELECTED_COLOR);
285 #ifdef HAVE_CHARSET
286 if (mc_global.utf8_display)
288 if (!view->utf8)
290 c = convert_from_8bit_to_utf_c ((unsigned char) c, view->converter);
292 if (!g_unichar_isprint (c))
293 c = '.';
295 else if (view->utf8)
296 ch = convert_from_utf_to_current_c (ch, view->converter);
297 else
298 #endif
300 c = convert_to_display_c (c);
302 if (!is_printable (c))
303 c = '.';
306 /* Print corresponding character on the text side */
307 if (text_start + bytes < width)
309 widget_move (view, top + row, left + text_start + bytes);
310 #ifdef HAVE_CHARSET
311 if (view->utf8)
312 tty_print_anychar (ch);
313 else
314 #endif
316 tty_print_char (c);
320 /* Save the cursor position for mcview_place_cursor() */
321 if (from == view->hex_cursor && view->hexview_in_text)
323 view->cursor_row = row;
324 view->cursor_col = text_start + bytes;
329 /* Be polite to the other functions */
330 tty_setcolor (NORMAL_COLOR);
332 mcview_place_cursor (view);
333 view->dpy_end = from;
336 /* --------------------------------------------------------------------------------------------- */
338 gboolean
339 mcview_hexedit_save_changes (mcview_t * view)
341 int answer = 0;
343 if (view->change_list == NULL)
344 return TRUE;
346 while (answer == 0)
348 int fp;
349 char *text;
350 struct hexedit_change_node *curr, *next;
352 assert (view->filename != NULL);
354 fp = mc_open (view->filename, O_WRONLY);
355 if (fp != -1)
357 for (curr = view->change_list; curr != NULL; curr = next)
359 next = curr->next;
361 if (mc_lseek (fp, curr->offset, SEEK_SET) == -1
362 || mc_write (fp, &(curr->value), 1) != 1)
363 goto save_error;
365 /* delete the saved item from the change list */
366 view->change_list = next;
367 view->dirty++;
368 mcview_set_byte (view, curr->offset, curr->value);
369 g_free (curr);
372 view->change_list = NULL;
374 if (view->locked)
375 view->locked = mcview_unlock_file (view);
377 if (mc_close (fp) == -1)
378 message (D_ERROR, _("Save file"),
379 _("Error while closing the file:\n%s\n"
380 "Data may have been written or not"), unix_error_string (errno));
382 view->dirty++;
383 return TRUE;
386 save_error:
387 text = g_strdup_printf (_("Cannot save file:\n%s"), unix_error_string (errno));
388 (void) mc_close (fp);
390 answer = query_dialog (_("Save file"), text, D_ERROR, 2, _("&Retry"), _("&Cancel"));
391 g_free (text);
394 return FALSE;
397 /* --------------------------------------------------------------------------------------------- */
399 void
400 mcview_toggle_hexedit_mode (mcview_t * view)
402 view->hexedit_mode = !view->hexedit_mode;
403 view->dpy_bbar_dirty = TRUE;
404 view->dirty++;
407 /* --------------------------------------------------------------------------------------------- */
409 void
410 mcview_hexedit_free_change_list (mcview_t * view)
412 struct hexedit_change_node *curr, *next;
414 for (curr = view->change_list; curr != NULL; curr = next)
416 next = curr->next;
417 g_free (curr);
419 view->change_list = NULL;
421 if (view->locked)
422 view->locked = mcview_unlock_file (view);
424 view->dirty++;
427 /* --------------------------------------------------------------------------------------------- */
429 void
430 mcview_enqueue_change (struct hexedit_change_node **head, struct hexedit_change_node *node)
432 /* chnode always either points to the head of the list or
433 * to one of the ->next fields in the list. The value at
434 * this location will be overwritten with the new node. */
435 struct hexedit_change_node **chnode = head;
437 while (*chnode != NULL && (*chnode)->offset < node->offset)
438 chnode = &((*chnode)->next);
440 node->next = *chnode;
441 *chnode = node;
444 /* --------------------------------------------------------------------------------------------- */