Fixed creation of lock files
[midnight-commander.git] / src / viewer / hex.c
blobb17ebf01d801c34750e311f90290d108a39a93b5
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 #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_vpath != NULL);
354 fp = mc_open (view->filename_vpath, 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 = unlock_file (view->filename_vpath);
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 = unlock_file (view->filename_vpath);
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 /* --------------------------------------------------------------------------------------------- */