Lock file is created and destroyed with full path.
[midnight-commander.git] / src / viewer / hex.c
blob5bd26d77307d3274d6b5fc2aec98828307adeacd
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>
43 #include "lib/global.h"
44 #include "lib/tty/tty.h"
45 #include "lib/skin.h"
46 #include "lib/vfs/mc-vfs/vfs.h"
48 #include "src/main.h"
49 #include "src/wtools.h"
50 #include "src/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" OFFSETTYPE_PRIX " ",
163 (long unsigned int) from);
164 widget_move (view, top + row, left);
165 tty_setcolor (MARKED_COLOR);
166 for (i = 0; col < width && hex_buff[i] != '\0'; i++)
168 tty_print_char (hex_buff[i]);
169 /* tty_print_char(hex_buff[i]); */
170 col += 1;
172 tty_setcolor (NORMAL_COLOR);
174 for (bytes = 0; bytes < view->bytes_per_line; bytes++, from++)
177 #ifdef HAVE_CHARSET
178 if (view->utf8)
180 char corr_buf[6 + 1];
181 int cnt, cw = 1;
182 gboolean read_res = TRUE;
183 ch = mcview_get_utf (view, from, &cw, &read_res);
184 if (!read_res)
185 break;
186 /* char width is greater 0 bytes */
187 if (cw != 0)
189 struct hexedit_change_node *corr = curr;
190 int res = g_unichar_to_utf8 (ch, (char *) corr_buf);
191 for (cnt = 0; cnt < cw; cnt++)
193 if (curr != NULL && from + cnt == curr->offset)
195 /* replace only changed bytes in array of multibyte char */
196 corr_buf[cnt] = curr->value;
197 curr = curr->next;
200 corr_buf[res] = '\0';
201 /* Determine the state of the current multibyte char */
202 ch = utf8_to_int ((char *) corr_buf, &cw, &read_res);
203 curr = corr;
206 #endif
207 if (!mcview_get_byte (view, from, &c))
208 break;
210 /* Save the cursor position for mcview_place_cursor() */
211 if (from == view->hex_cursor && !view->hexview_in_text)
213 view->cursor_row = row;
214 view->cursor_col = col;
217 /* Determine the state of the current byte */
218 boldflag =
219 (from == view->hex_cursor) ? MARK_CURSOR
220 : (curr != NULL && from == curr->offset) ? MARK_CHANGED
221 : (view->search_start <= from &&
222 from < view->search_end) ? MARK_SELECTED : MARK_NORMAL;
224 /* Determine the value of the current byte */
225 if (curr != NULL && from == curr->offset)
227 c = curr->value;
228 curr = curr->next;
231 /* Select the color for the hex number */
232 tty_setcolor (boldflag == MARK_NORMAL ? NORMAL_COLOR :
233 boldflag == MARK_SELECTED ? MARKED_COLOR :
234 boldflag == MARK_CHANGED ? VIEW_UNDERLINED_COLOR :
235 /* boldflag == MARK_CURSOR */
236 view->hexview_in_text ? MARKED_SELECTED_COLOR : VIEW_UNDERLINED_COLOR);
238 /* Print the hex number */
239 widget_move (view, top + row, left + col);
240 if (col < width)
242 tty_print_char (hex_char[c / 16]);
243 col += 1;
245 if (col < width)
247 tty_print_char (hex_char[c % 16]);
248 col += 1;
251 /* Print the separator */
252 tty_setcolor (NORMAL_COLOR);
253 if (bytes != view->bytes_per_line - 1)
255 if (col < width)
257 tty_print_char (' ');
258 col += 1;
261 /* After every four bytes, print a group separator */
262 if (bytes % 4 == 3)
264 if (view->data_area.width >= 80 && col < width)
266 tty_print_one_vline (TRUE);
267 col += 1;
269 if (col < width)
271 tty_print_char (' ');
272 col += 1;
277 /* Select the color for the character; this differs from the
278 * hex color when boldflag == MARK_CURSOR */
279 tty_setcolor (boldflag == MARK_NORMAL ? NORMAL_COLOR :
280 boldflag == MARK_SELECTED ? MARKED_COLOR :
281 boldflag == MARK_CHANGED ? VIEW_UNDERLINED_COLOR :
282 /* boldflag == MARK_CURSOR */
283 view->hexview_in_text ? VIEW_UNDERLINED_COLOR : MARKED_SELECTED_COLOR);
285 #ifdef HAVE_CHARSET
286 if (utf8_display)
288 if (!view->utf8)
290 ch = convert_from_8bit_to_utf_c ((unsigned char) ch, view->converter);
292 if (!g_unichar_isprint (ch))
293 ch = '.';
295 else
297 if (view->utf8)
299 ch = convert_from_utf_to_current_c (ch, view->converter);
301 else
303 #endif
304 ch = convert_to_display_c (ch);
305 #ifdef HAVE_CHARSET
308 #endif
309 c = convert_to_display_c (c);
310 if (!g_ascii_isprint (c))
311 c = '.';
313 /* Print corresponding character on the text side */
314 if (text_start + bytes < width)
316 widget_move (view, top + row, left + text_start + bytes);
317 if (!view->utf8)
319 tty_print_char (c);
321 else
323 tty_print_anychar (ch);
327 /* Save the cursor position for mcview_place_cursor() */
328 if (from == view->hex_cursor && view->hexview_in_text)
330 view->cursor_row = row;
331 view->cursor_col = text_start + bytes;
336 /* Be polite to the other functions */
337 tty_setcolor (NORMAL_COLOR);
339 mcview_place_cursor (view);
340 view->dpy_end = from;
343 /* --------------------------------------------------------------------------------------------- */
345 gboolean
346 mcview_hexedit_save_changes (mcview_t * view)
348 int answer = 0;
350 if (view->change_list == NULL)
351 return TRUE;
353 while (answer == 0)
355 int fp;
356 char *text;
357 struct hexedit_change_node *curr, *next;
359 assert (view->filename != NULL);
361 fp = mc_open (view->filename, O_WRONLY);
362 if (fp != -1)
364 for (curr = view->change_list; curr != NULL; curr = next)
366 next = curr->next;
368 if (mc_lseek (fp, curr->offset, SEEK_SET) == -1
369 || mc_write (fp, &(curr->value), 1) != 1)
370 goto save_error;
372 /* delete the saved item from the change list */
373 view->change_list = next;
374 view->dirty++;
375 mcview_set_byte (view, curr->offset, curr->value);
376 g_free (curr);
379 view->change_list = NULL;
381 if (view->locked)
382 view->locked = mcview_unlock_file (view);
384 if (mc_close (fp) == -1)
385 message (D_ERROR, _("Save file"),
386 _("Error while closing the file:\n%s\n"
387 "Data may have been written or not"), unix_error_string (errno));
389 view->dirty++;
390 return TRUE;
393 save_error:
394 text = g_strdup_printf (_("Cannot save file:\n%s"), unix_error_string (errno));
395 (void) mc_close (fp);
397 answer = query_dialog (_("Save file"), text, D_ERROR, 2, _("&Retry"), _("&Cancel"));
398 g_free (text);
401 return FALSE;
404 /* --------------------------------------------------------------------------------------------- */
406 void
407 mcview_toggle_hexedit_mode (mcview_t * view)
409 view->hexedit_mode = !view->hexedit_mode;
410 view->dpy_bbar_dirty = TRUE;
411 view->dirty++;
414 /* --------------------------------------------------------------------------------------------- */
416 void
417 mcview_hexedit_free_change_list (mcview_t * view)
419 struct hexedit_change_node *curr, *next;
421 for (curr = view->change_list; curr != NULL; curr = next)
423 next = curr->next;
424 g_free (curr);
426 view->change_list = NULL;
428 if (view->locked)
429 view->locked = mcview_unlock_file (view);
431 view->dirty++;
434 /* --------------------------------------------------------------------------------------------- */
436 void
437 mcview_enqueue_change (struct hexedit_change_node **head, struct hexedit_change_node *node)
439 /* chnode always either points to the head of the list or
440 * to one of the ->next fields in the list. The value at
441 * this location will be overwritten with the new node. */
442 struct hexedit_change_node **chnode = head;
444 while (*chnode != NULL && (*chnode)->offset < node->offset)
445 chnode = &((*chnode)->next);
447 node->next = *chnode;
448 *chnode = node;
451 /* --------------------------------------------------------------------------------------------- */