2 Internal file viewer for the Midnight Commander
5 Copyright (C) 1994-2014
6 Free Software Foundation, Inc.
9 Miguel de Icaza, 1994, 1995, 1998
10 Janne Kukonlehto, 1994, 1995
12 Joseph M. Hinkle, 1996
15 Roland Illig <roland.illig@gmx.de>, 2004, 2005
16 Slava Zanko <slavazanko@google.com>, 2009, 2013
17 Andrew Borodin <aborodin@vmail.ru>, 2009
18 Ilia Maslakov <il.smind@gmail.com>, 2009
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 3 of the License,
25 or (at your option) any later version.
27 The Midnight Commander is distributed in the hope that it will be useful,
28 but WITHOUT ANY WARRANTY; without even the implied warranty of
29 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 GNU 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, see <http://www.gnu.org/licenses/>.
40 #include <inttypes.h> /* uintmax_t */
42 #include "lib/global.h"
43 #include "lib/tty/tty.h"
45 #include "lib/vfs/vfs.h"
46 #include "lib/lock.h" /* lock_file() and unlock_file() */
48 #include "lib/widget.h"
50 #include "lib/charsets.h"
55 /*** global variables ****************************************************************************/
57 /*** file scope macro definitions ****************************************************************/
59 /*** file scope type declarations ****************************************************************/
69 /*** file scope variables ************************************************************************/
71 static const char hex_char
[] = "0123456789ABCDEF";
73 /*** file scope functions ************************************************************************/
74 /* --------------------------------------------------------------------------------------------- */
78 utf8_to_int (char *str
, int *char_width
, gboolean
* result
)
82 gchar
*next_ch
= NULL
;
93 res
= g_utf8_get_char_validated (str
, -1);
100 /* Calculate UTF-8 char width */
101 next_ch
= g_utf8_next_char (str
);
103 width
= next_ch
- str
;
110 #endif /* HAVE_CHARSET */
112 /* --------------------------------------------------------------------------------------------- */
113 /** Determine the state of the current byte.
115 * @param view viewer object
117 * @param curr current node
121 mcview_hex_calculate_boldflag (mcview_t
* view
, off_t from
, struct hexedit_change_node
*curr
)
123 return (from
== view
->hex_cursor
) ? MARK_CURSOR
124 : (curr
!= NULL
&& from
== curr
->offset
) ? MARK_CHANGED
125 : (view
->search_start
<= from
&& from
< view
->search_end
) ? MARK_SELECTED
: MARK_NORMAL
;
128 /* --------------------------------------------------------------------------------------------- */
129 /*** public functions ****************************************************************************/
130 /* --------------------------------------------------------------------------------------------- */
133 mcview_display_hex (mcview_t
* view
)
135 const screen_dimen top
= view
->data_area
.top
;
136 const screen_dimen left
= view
->data_area
.left
;
137 const screen_dimen height
= view
->data_area
.height
;
138 const screen_dimen width
= view
->data_area
.width
;
139 const int ngroups
= view
->bytes_per_line
/ 4;
140 const screen_dimen text_start
= 8 + 13 * ngroups
+ ((width
< 80) ? 0 : (ngroups
- 1 + 1));
141 /* 8 characters are used for the file offset, and every hex group
142 * takes 13 characters. On "big" screens, the groups are separated
143 * by an extra vertical line, and there is an extra space before the
150 mark_t boldflag
= MARK_NORMAL
;
151 struct hexedit_change_node
*curr
= view
->change_list
;
154 #endif /* HAVE_CHARSET */
156 char hex_buff
[10]; /* A temporary buffer for sprintf and mvwaddstr */
157 int bytes
; /* Number of bytes already printed on the line */
159 mcview_display_clean (view
);
161 /* Find the first displayable changed byte */
162 from
= view
->dpy_start
;
163 while (curr
&& (curr
->offset
< from
))
168 for (row
= 0; mcview_get_byte (view
, from
, NULL
) == TRUE
&& row
< height
; row
++)
170 screen_dimen col
= 0;
175 /* Print the hex offset */
176 g_snprintf (hex_buff
, sizeof (hex_buff
), "%08" PRIXMAX
" ", (uintmax_t) from
);
177 widget_move (view
, top
+ row
, left
);
178 tty_setcolor (VIEW_BOLD_COLOR
);
179 for (i
= 0; col
< width
&& hex_buff
[i
] != '\0'; i
++)
181 tty_print_char (hex_buff
[i
]);
182 /* tty_print_char(hex_buff[i]); */
185 tty_setcolor (VIEW_NORMAL_COLOR
);
187 for (bytes
= 0; bytes
< view
->bytes_per_line
; bytes
++, from
++)
194 gboolean read_res
= TRUE
;
196 ch
= mcview_get_utf (view
, from
, &cw
, &read_res
);
199 /* char width is greater 0 bytes */
203 char corr_buf
[UTF8_CHAR_LEN
+ 1];
204 struct hexedit_change_node
*corr
= curr
;
207 res
= g_unichar_to_utf8 (ch
, (char *) corr_buf
);
209 for (cnt
= 0; cnt
< cw
; cnt
++)
211 if (curr
!= NULL
&& from
+ cnt
== curr
->offset
)
213 /* replace only changed bytes in array of multibyte char */
214 corr_buf
[cnt
] = curr
->value
;
218 corr_buf
[res
] = '\0';
219 /* Determine the state of the current multibyte char */
220 ch
= utf8_to_int ((char *) corr_buf
, &cw
, &read_res
);
224 #endif /* HAVE_CHARSET */
225 if (!mcview_get_byte (view
, from
, &c
))
228 /* Save the cursor position for mcview_place_cursor() */
229 if (from
== view
->hex_cursor
&& !view
->hexview_in_text
)
231 view
->cursor_row
= row
;
232 view
->cursor_col
= col
;
235 /* Determine the state of the current byte */
236 boldflag
= mcview_hex_calculate_boldflag (view
, from
, curr
);
238 /* Determine the value of the current byte */
239 if (curr
!= NULL
&& from
== curr
->offset
)
245 /* Select the color for the hex number */
246 tty_setcolor (boldflag
== MARK_NORMAL
? VIEW_NORMAL_COLOR
:
247 boldflag
== MARK_SELECTED
? VIEW_BOLD_COLOR
:
248 boldflag
== MARK_CHANGED
? VIEW_UNDERLINED_COLOR
:
249 /* boldflag == MARK_CURSOR */
250 view
->hexview_in_text
? VIEW_SELECTED_COLOR
: VIEW_UNDERLINED_COLOR
);
252 /* Print the hex number */
253 widget_move (view
, top
+ row
, left
+ col
);
256 tty_print_char (hex_char
[c
/ 16]);
261 tty_print_char (hex_char
[c
% 16]);
265 /* Print the separator */
266 tty_setcolor (VIEW_NORMAL_COLOR
);
267 if (bytes
!= view
->bytes_per_line
- 1)
271 tty_print_char (' ');
275 /* After every four bytes, print a group separator */
278 if (view
->data_area
.width
>= 80 && col
< width
)
280 tty_print_one_vline (TRUE
);
285 tty_print_char (' ');
291 /* Select the color for the character; this differs from the
292 * hex color when boldflag == MARK_CURSOR */
293 tty_setcolor (boldflag
== MARK_NORMAL
? VIEW_NORMAL_COLOR
:
294 boldflag
== MARK_SELECTED
? VIEW_BOLD_COLOR
:
295 boldflag
== MARK_CHANGED
? VIEW_UNDERLINED_COLOR
:
296 /* boldflag == MARK_CURSOR */
297 view
->hexview_in_text
? VIEW_SELECTED_COLOR
: MARKED_SELECTED_COLOR
);
301 if (mc_global
.utf8_display
)
305 c
= convert_from_8bit_to_utf_c ((unsigned char) c
, view
->converter
);
307 if (!g_unichar_isprint (c
))
311 ch
= convert_from_utf_to_current_c (ch
, view
->converter
);
316 c
= convert_to_display_c (c
);
319 if (!is_printable (c
))
323 /* Print corresponding character on the text side */
324 if (text_start
+ bytes
< width
)
326 widget_move (view
, top
+ row
, left
+ text_start
+ bytes
);
329 tty_print_anychar (ch
);
335 /* Save the cursor position for mcview_place_cursor() */
336 if (from
== view
->hex_cursor
&& view
->hexview_in_text
)
338 view
->cursor_row
= row
;
339 view
->cursor_col
= text_start
+ bytes
;
344 /* Be polite to the other functions */
345 tty_setcolor (VIEW_NORMAL_COLOR
);
347 mcview_place_cursor (view
);
348 view
->dpy_end
= from
;
351 /* --------------------------------------------------------------------------------------------- */
354 mcview_hexedit_save_changes (mcview_t
* view
)
358 if (view
->change_list
== NULL
)
365 struct hexedit_change_node
*curr
, *next
;
368 assert (view
->filename_vpath
!= NULL
);
371 fp
= mc_open (view
->filename_vpath
, O_WRONLY
);
374 for (curr
= view
->change_list
; curr
!= NULL
; curr
= next
)
378 if (mc_lseek (fp
, curr
->offset
, SEEK_SET
) == -1
379 || mc_write (fp
, &(curr
->value
), 1) != 1)
382 /* delete the saved item from the change list */
383 view
->change_list
= next
;
385 mcview_set_byte (view
, curr
->offset
, curr
->value
);
389 view
->change_list
= NULL
;
392 view
->locked
= unlock_file (view
->filename_vpath
);
394 if (mc_close (fp
) == -1)
395 message (D_ERROR
, _("Save file"),
396 _("Error while closing the file:\n%s\n"
397 "Data may have been written or not"), unix_error_string (errno
));
404 text
= g_strdup_printf (_("Cannot save file:\n%s"), unix_error_string (errno
));
405 (void) mc_close (fp
);
407 answer
= query_dialog (_("Save file"), text
, D_ERROR
, 2, _("&Retry"), _("&Cancel"));
414 /* --------------------------------------------------------------------------------------------- */
417 mcview_toggle_hexedit_mode (mcview_t
* view
)
419 view
->hexedit_mode
= !view
->hexedit_mode
;
420 view
->dpy_bbar_dirty
= TRUE
;
424 /* --------------------------------------------------------------------------------------------- */
427 mcview_hexedit_free_change_list (mcview_t
* view
)
429 struct hexedit_change_node
*curr
, *next
;
431 for (curr
= view
->change_list
; curr
!= NULL
; curr
= next
)
436 view
->change_list
= NULL
;
439 view
->locked
= unlock_file (view
->filename_vpath
);
444 /* --------------------------------------------------------------------------------------------- */
447 mcview_enqueue_change (struct hexedit_change_node
**head
, struct hexedit_change_node
*node
)
449 /* chnode always either points to the head of the list or
450 * to one of the ->next fields in the list. The value at
451 * this location will be overwritten with the new node. */
452 struct hexedit_change_node
**chnode
= head
;
454 while (*chnode
!= NULL
&& (*chnode
)->offset
< node
->offset
)
455 chnode
= &((*chnode
)->next
);
457 node
->next
= *chnode
;
461 /* --------------------------------------------------------------------------------------------- */