Ticket #3828: syntax: Apache Hive query language
[midnight-commander.git] / src / viewer / hex.c
bloba672b03dae0c920014619f5e06943e0aefed501a
1 /*
2 Internal file viewer for the Midnight Commander
3 Function for hex view
5 Copyright (C) 1994-2017
6 Free Software Foundation, Inc.
8 Written by:
9 Miguel de Icaza, 1994, 1995, 1998
10 Janne Kukonlehto, 1994, 1995
11 Jakub Jelinek, 1995
12 Joseph M. Hinkle, 1996
13 Norbert Warmuth, 1997
14 Pavel Machek, 1998
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/>.
36 #include <config.h>
38 #include <errno.h>
39 #include <inttypes.h> /* uintmax_t */
41 #include "lib/global.h"
42 #include "lib/tty/tty.h"
43 #include "lib/skin.h"
44 #include "lib/vfs/vfs.h"
45 #include "lib/lock.h" /* lock_file() and unlock_file() */
46 #include "lib/util.h"
47 #include "lib/widget.h"
48 #ifdef HAVE_CHARSET
49 #include "lib/charsets.h"
50 #endif
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 /* --------------------------------------------------------------------------------------------- */
76 /** Determine the state of the current byte.
78 * @param view viewer object
79 * @param from offset
80 * @param curr current node
83 static mark_t
84 mcview_hex_calculate_boldflag (WView * view, off_t from, struct hexedit_change_node *curr,
85 gboolean force_changed)
87 return (from == view->hex_cursor) ? MARK_CURSOR
88 : ((curr != NULL && from == curr->offset) || force_changed) ? MARK_CHANGED
89 : (view->search_start <= from && from < view->search_end) ? MARK_SELECTED : MARK_NORMAL;
92 /* --------------------------------------------------------------------------------------------- */
93 /*** public functions ****************************************************************************/
94 /* --------------------------------------------------------------------------------------------- */
96 void
97 mcview_display_hex (WView * view)
99 const screen_dimen top = view->data_area.top;
100 const screen_dimen left = view->data_area.left;
101 const screen_dimen height = view->data_area.height;
102 const screen_dimen width = view->data_area.width;
103 const int ngroups = view->bytes_per_line / 4;
104 /* 8 characters are used for the file offset, and every hex group
105 * takes 13 characters. Starting at width of 80 columns, the groups
106 * are separated by an extra vertical line. Starting at width of 81,
107 * there is an extra space before the text column. There is always a
108 * mostly empty column on the right, to allow overflowing CJKs.
110 const screen_dimen text_start = 8 + 13 * ngroups +
111 ((width < 80) ? 0 : (width == 80) ? (ngroups - 1) : (ngroups - 1 + 1));
113 int row;
114 off_t from;
115 mark_t boldflag_byte = MARK_NORMAL;
116 mark_t boldflag_char = MARK_NORMAL;
117 struct hexedit_change_node *curr = view->change_list;
118 #ifdef HAVE_CHARSET
119 int cont_bytes = 0; /* number of continuation bytes remanining from current UTF-8 */
120 gboolean cjk_right = FALSE; /* whether the second byte of a CJK is to be processed */
121 #endif /* HAVE_CHARSET */
122 gboolean utf8_changed = FALSE; /* whether any of the bytes in the UTF-8 were changed */
124 char hex_buff[10]; /* A temporary buffer for sprintf and mvwaddstr */
126 mcview_display_clean (view);
128 /* Find the first displayable changed byte */
129 /* In UTF-8 mode, go back by 1 or maybe 2 lines to handle continuation bytes properly. */
130 from = view->dpy_start;
131 row = 0;
132 #ifdef HAVE_CHARSET
133 if (view->utf8)
135 if (from >= view->bytes_per_line)
137 row--;
138 from -= view->bytes_per_line;
140 if (view->bytes_per_line == 4 && from >= view->bytes_per_line)
142 row--;
143 from -= view->bytes_per_line;
146 #endif /* HAVE_CHARSET */
147 while (curr && (curr->offset < from))
149 curr = curr->next;
152 for (; mcview_get_byte (view, from, NULL) && row < (int) height; row++)
154 screen_dimen col = 0;
155 size_t i;
156 int bytes; /* Number of bytes already printed on the line */
158 /* Print the hex offset */
159 if (row >= 0)
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'; col++, i++)
165 tty_print_char (hex_buff[i]);
166 tty_setcolor (VIEW_NORMAL_COLOR);
169 for (bytes = 0; bytes < view->bytes_per_line; bytes++, from++)
171 int c;
172 #ifdef HAVE_CHARSET
173 int ch = 0;
175 if (view->utf8)
177 struct hexedit_change_node *corr = curr;
179 if (cont_bytes != 0)
181 /* UTF-8 continuation bytes, print a space (with proper attributes)... */
182 cont_bytes--;
183 ch = ' ';
184 if (cjk_right)
186 /* ... except when it'd wipe out the right half of a CJK, then print nothing */
187 cjk_right = FALSE;
188 ch = -1;
191 else
193 int j;
194 gchar utf8buf[UTF8_CHAR_LEN + 1];
195 int res;
196 int first_changed = -1;
198 for (j = 0; j < UTF8_CHAR_LEN; j++)
200 if (mcview_get_byte (view, from + j, &res))
201 utf8buf[j] = res;
202 else
204 utf8buf[j] = '\0';
205 break;
207 if (curr != NULL && from + j == curr->offset)
209 utf8buf[j] = curr->value;
210 if (first_changed == -1)
211 first_changed = j;
213 if (curr != NULL && from + j >= curr->offset)
214 curr = curr->next;
216 utf8buf[UTF8_CHAR_LEN] = '\0';
218 /* Determine the state of the current multibyte char */
219 ch = g_utf8_get_char_validated (utf8buf, -1);
220 if (ch == -1 || ch == -2)
222 ch = '.';
224 else
226 gchar *next_ch;
228 next_ch = g_utf8_next_char (utf8buf);
229 cont_bytes = next_ch - utf8buf - 1;
230 if (g_unichar_iswide (ch))
231 cjk_right = TRUE;
234 utf8_changed = (first_changed >= 0 && first_changed <= cont_bytes);
235 curr = corr;
238 #endif /* HAVE_CHARSET */
240 /* For negative rows, the only thing we care about is overflowing
241 * UTF-8 continuation bytes which were handled above. */
242 if (row < 0)
244 if (curr != NULL && from == curr->offset)
245 curr = curr->next;
246 continue;
249 if (!mcview_get_byte (view, from, &c))
250 break;
252 /* Save the cursor position for mcview_place_cursor() */
253 if (from == view->hex_cursor && !view->hexview_in_text)
255 view->cursor_row = row;
256 view->cursor_col = col;
259 /* Determine the state of the current byte */
260 boldflag_byte = mcview_hex_calculate_boldflag (view, from, curr, FALSE);
261 boldflag_char = mcview_hex_calculate_boldflag (view, from, curr, utf8_changed);
263 /* Determine the value of the current byte */
264 if (curr != NULL && from == curr->offset)
266 c = curr->value;
267 curr = curr->next;
270 /* Select the color for the hex number */
271 tty_setcolor (boldflag_byte == MARK_NORMAL ? VIEW_NORMAL_COLOR :
272 boldflag_byte == MARK_SELECTED ? VIEW_BOLD_COLOR :
273 boldflag_byte == MARK_CHANGED ? VIEW_UNDERLINED_COLOR :
274 /* boldflag_byte == MARK_CURSOR */
275 view->hexview_in_text ? VIEW_SELECTED_COLOR : VIEW_UNDERLINED_COLOR);
277 /* Print the hex number */
278 widget_move (view, top + row, left + col);
279 if (col < width)
281 tty_print_char (hex_char[c / 16]);
282 col += 1;
284 if (col < width)
286 tty_print_char (hex_char[c % 16]);
287 col += 1;
290 /* Print the separator */
291 tty_setcolor (VIEW_NORMAL_COLOR);
292 if (bytes != view->bytes_per_line - 1)
294 if (col < width)
296 tty_print_char (' ');
297 col += 1;
300 /* After every four bytes, print a group separator */
301 if (bytes % 4 == 3)
303 if (view->data_area.width >= 80 && col < width)
305 tty_print_one_vline (TRUE);
306 col += 1;
308 if (col < width)
310 tty_print_char (' ');
311 col += 1;
316 /* Select the color for the character; this differs from the
317 * hex color when boldflag == MARK_CURSOR */
318 tty_setcolor (boldflag_char == MARK_NORMAL ? VIEW_NORMAL_COLOR :
319 boldflag_char == MARK_SELECTED ? VIEW_BOLD_COLOR :
320 boldflag_char == MARK_CHANGED ? VIEW_UNDERLINED_COLOR :
321 /* boldflag_char == MARK_CURSOR */
322 view->hexview_in_text ? VIEW_SELECTED_COLOR : MARKED_SELECTED_COLOR);
325 #ifdef HAVE_CHARSET
326 if (mc_global.utf8_display)
328 if (!view->utf8)
330 c = convert_from_8bit_to_utf_c ((unsigned char) c, view->converter);
332 if (!g_unichar_isprint (c))
333 c = '.';
335 else if (view->utf8)
336 ch = convert_from_utf_to_current_c (ch, view->converter);
337 else
338 #endif
340 #ifdef HAVE_CHARSET
341 c = convert_to_display_c (c);
342 #endif
344 if (!is_printable (c))
345 c = '.';
348 /* Print corresponding character on the text side */
349 if (text_start + bytes < width)
351 widget_move (view, top + row, left + text_start + bytes);
352 #ifdef HAVE_CHARSET
353 if (view->utf8)
354 tty_print_anychar (ch);
355 else
356 #endif
357 tty_print_char (c);
360 /* Save the cursor position for mcview_place_cursor() */
361 if (from == view->hex_cursor && view->hexview_in_text)
363 view->cursor_row = row;
364 view->cursor_col = text_start + bytes;
369 /* Be polite to the other functions */
370 tty_setcolor (VIEW_NORMAL_COLOR);
372 mcview_place_cursor (view);
373 view->dpy_end = from;
376 /* --------------------------------------------------------------------------------------------- */
378 gboolean
379 mcview_hexedit_save_changes (WView * view)
381 int answer = 0;
383 if (view->change_list == NULL)
384 return TRUE;
386 while (answer == 0)
388 int fp;
389 char *text;
390 struct hexedit_change_node *curr, *next;
392 g_assert (view->filename_vpath != NULL);
394 fp = mc_open (view->filename_vpath, O_WRONLY);
395 if (fp != -1)
397 for (curr = view->change_list; curr != NULL; curr = next)
399 next = curr->next;
401 if (mc_lseek (fp, curr->offset, SEEK_SET) == -1
402 || mc_write (fp, &(curr->value), 1) != 1)
403 goto save_error;
405 /* delete the saved item from the change list */
406 view->change_list = next;
407 view->dirty++;
408 mcview_set_byte (view, curr->offset, curr->value);
409 g_free (curr);
412 view->change_list = NULL;
414 if (view->locked)
415 view->locked = unlock_file (view->filename_vpath);
417 if (mc_close (fp) == -1)
418 message (D_ERROR, _("Save file"),
419 _("Error while closing the file:\n%s\n"
420 "Data may have been written or not"), unix_error_string (errno));
422 view->dirty++;
423 return TRUE;
426 save_error:
427 text = g_strdup_printf (_("Cannot save file:\n%s"), unix_error_string (errno));
428 (void) mc_close (fp);
430 answer = query_dialog (_("Save file"), text, D_ERROR, 2, _("&Retry"), _("&Cancel"));
431 g_free (text);
434 return FALSE;
437 /* --------------------------------------------------------------------------------------------- */
439 void
440 mcview_toggle_hexedit_mode (WView * view)
442 view->hexedit_mode = !view->hexedit_mode;
443 view->dpy_bbar_dirty = TRUE;
444 view->dirty++;
447 /* --------------------------------------------------------------------------------------------- */
449 void
450 mcview_hexedit_free_change_list (WView * view)
452 struct hexedit_change_node *curr, *next;
454 for (curr = view->change_list; curr != NULL; curr = next)
456 next = curr->next;
457 g_free (curr);
459 view->change_list = NULL;
461 if (view->locked)
462 view->locked = unlock_file (view->filename_vpath);
464 view->dirty++;
467 /* --------------------------------------------------------------------------------------------- */
469 void
470 mcview_enqueue_change (struct hexedit_change_node **head, struct hexedit_change_node *node)
472 /* chnode always either points to the head of the list or
473 * to one of the ->next fields in the list. The value at
474 * this location will be overwritten with the new node. */
475 struct hexedit_change_node **chnode = head;
477 while (*chnode != NULL && (*chnode)->offset < node->offset)
478 chnode = &((*chnode)->next);
480 node->next = *chnode;
481 *chnode = node;
484 /* --------------------------------------------------------------------------------------------- */