4 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2007, 2009, 2011
6 The Free Software Foundation, Inc.
9 Miguel de Icaza, 1994, 1995, 1996
10 Janne Kukonlehto, 1994, 1995, 1996
11 Dugan Porter, 1994, 1995, 1996
12 Jakub Jelinek, 1994, 1995, 1996
13 Mauricio Plaza, 1994, 1995, 1996
15 The file_date routine is mostly from GNU's fileutils package,
16 written by Richard Stallman and David MacKenzie.
18 This file is part of the Midnight Commander.
20 The Midnight Commander is free software: you can redistribute it
21 and/or modify it under the terms of the GNU General Public License as
22 published by the Free Software Foundation, either version 3 of the License,
23 or (at your option) any later version.
25 The Midnight Commander is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
30 You should have received a copy of the GNU General Public License
31 along with this program. If not, see <http://www.gnu.org/licenses/>.
35 * \brief Source: various utilities
48 #include <sys/types.h>
52 #include "lib/global.h"
53 #include "lib/mcconfig.h"
54 #include "lib/fileloc.h"
55 #include "lib/vfs/vfs.h"
56 #include "lib/strutil.h"
59 /*** global variables ****************************************************************************/
61 /*** file scope macro definitions ****************************************************************/
63 #define ismode(n,m) ((n & m) == m)
65 /* Number of attempts to create a temporary file */
70 #define TMP_SUFFIX ".tmp"
72 #define ASCII_A (0x40 + 1)
73 #define ASCII_Z (0x40 + 26)
74 #define ASCII_a (0x60 + 1)
75 #define ASCII_z (0x60 + 26)
77 /*** file scope type declarations ****************************************************************/
79 /*** file scope variables ************************************************************************/
81 /*** file scope functions ************************************************************************/
82 /* --------------------------------------------------------------------------------------------- */
85 is_7bit_printable (unsigned char c
)
87 return (c
> 31 && c
< 127);
90 /* --------------------------------------------------------------------------------------------- */
93 is_iso_printable (unsigned char c
)
95 return ((c
> 31 && c
< 127) || c
>= 160);
98 /* --------------------------------------------------------------------------------------------- */
101 is_8bit_printable (unsigned char c
)
103 /* "Full 8 bits output" doesn't work on xterm */
104 if (mc_global
.tty
.xterm_flag
)
105 return is_iso_printable (c
);
107 return (c
> 31 && c
!= 127 && c
!= 155);
110 /* --------------------------------------------------------------------------------------------- */
113 resolve_symlinks (const char *path
)
115 char *buf
, *buf2
, *q
, *r
, c
;
121 if (*path
!= PATH_SEP
)
124 vpath
= vfs_path_from_str (path
);
125 r
= buf
= g_malloc (MC_MAXPATHLEN
);
126 buf2
= g_malloc (MC_MAXPATHLEN
);
133 q
= strchr (p
+ 1, PATH_SEP
);
136 q
= strchr (p
+ 1, 0);
142 if (mc_lstat (vpath
, &mybuf
) < 0)
149 if (!S_ISLNK (mybuf
.st_mode
))
153 len
= mc_readlink (vpath
, buf2
, MC_MAXPATHLEN
- 1);
162 if (*buf2
== PATH_SEP
)
167 canonicalize_pathname (buf
);
169 if (!*r
|| *(r
- 1) != PATH_SEP
)
180 strcpy (buf
, PATH_SEP_STR
);
181 else if (*(r
- 1) == PATH_SEP
&& r
!= buf
+ 1)
186 vfs_path_free (vpath
);
190 /* --------------------------------------------------------------------------------------------- */
193 mc_util_write_backup_content (const char *from_file_name
, const char *to_file_name
)
198 gboolean ret1
= TRUE
;
200 if (!g_file_get_contents (from_file_name
, &contents
, &length
, NULL
))
203 backup_fd
= fopen (to_file_name
, "w");
204 if (backup_fd
== NULL
)
210 if (fwrite ((const void *) contents
, 1, length
, backup_fd
) != length
)
214 ret2
= fflush (backup_fd
);
215 ret2
= fclose (backup_fd
);
221 /* --------------------------------------------------------------------------------------------- */
222 /*** public functions ****************************************************************************/
223 /* --------------------------------------------------------------------------------------------- */
231 /* "Display bits" is ignored, since the user controls the output
232 by setting the output codepage */
233 return is_8bit_printable (c
);
235 if (!mc_global
.eight_bit_clean
)
236 return is_7bit_printable (c
);
238 if (mc_global
.full_eight_bits
)
240 return is_8bit_printable (c
);
243 return is_iso_printable (c
);
244 #endif /* !HAVE_CHARSET */
247 /* --------------------------------------------------------------------------------------------- */
249 * Quote the filename for the purpose of inserting it into the command
250 * line. If quote_percent is 1, replace "%" with "%%" - the percent is
251 * processed by the mc command line.
254 name_quote (const char *s
, int quote_percent
)
258 d
= ret
= g_malloc (strlen (s
) * 2 + 2 + 1);
310 /* --------------------------------------------------------------------------------------------- */
313 fake_name_quote (const char *s
, int quote_percent
)
315 (void) quote_percent
;
319 /* --------------------------------------------------------------------------------------------- */
321 * path_trunc() is the same as str_trunc() but
322 * it deletes possible password from path for security
327 path_trunc (const char *path
, size_t trunc_len
)
329 char *secure_path
= strip_password (g_strdup (path
), 1);
331 const char *ret
= str_trunc (secure_path
, trunc_len
);
332 g_free (secure_path
);
337 /* --------------------------------------------------------------------------------------------- */
340 size_trunc (uintmax_t size
, gboolean use_si
)
342 static char x
[BUF_TINY
];
343 uintmax_t divisor
= 1;
344 const char *xtra
= "";
346 if (size
> 999999999UL)
348 divisor
= use_si
? 1000 : 1024;
349 xtra
= use_si
? "k" : "K";
351 if (size
/ divisor
> 999999999UL)
353 divisor
= use_si
? (1000 * 1000) : (1024 * 1024);
354 xtra
= use_si
? "m" : "M";
356 if (size
/ divisor
> 999999999UL)
358 divisor
= use_si
? (1000 * 1000 * 1000) : (1024 * 1024 * 1024);
359 xtra
= use_si
? "g" : "G";
363 g_snprintf (x
, sizeof (x
), "%.0f%s", 1.0 * size
/ divisor
, xtra
);
367 /* --------------------------------------------------------------------------------------------- */
370 size_trunc_sep (uintmax_t size
, gboolean use_si
)
377 p
= y
= size_trunc (size
, use_si
);
379 d
= x
+ sizeof (x
) - 1;
381 while (p
>= y
&& isalpha ((unsigned char) *p
))
383 for (count
= 0; p
>= y
; count
++)
398 /* --------------------------------------------------------------------------------------------- */
400 * Print file SIZE to BUFFER, but don't exceed LEN characters,
401 * not including trailing 0. BUFFER should be at least LEN+1 long.
402 * This function is called for every file on panels, so avoid
403 * floating point by any means.
405 * Units: size units (filesystem sizes are 1K blocks)
406 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
410 size_trunc_len (char *buffer
, unsigned int len
, uintmax_t size
, int units
, gboolean use_si
)
412 /* Avoid taking power for every file. */
414 static const uintmax_t power10
[] = {
415 /* we hope that size of uintmax_t is 4 bytes at least */
426 /* maximmum value of uintmax_t (in case of 4 bytes) is
429 #if SIZEOF_UINTMAX_T == 8
437 10000000000000000ULL,
438 100000000000000000ULL,
439 1000000000000000000ULL,
440 10000000000000000000ULL
441 /* maximmum value of uintmax_t (in case of 8 bytes) is
447 static const char *const suffix
[] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL
};
448 static const char *const suffix_lc
[] = { "", "k", "m", "g", "t", "p", "e", "z", "y", NULL
};
453 #if SIZEOF_UINTMAX_T == 8
454 /* 20 decimal digits are required to represent 8 bytes */
458 /* 10 decimal digits are required to represent 4 bytes */
464 * recalculate from 1024 base to 1000 base if units>0
465 * We can't just multiply by 1024 - that might cause overflow
466 * if off_t type is too small
469 for (j
= 0; j
< units
; j
++)
471 uintmax_t size_remain
;
473 size_remain
= ((size
% 125) * 1024) / 1000; /* size mod 125, recalculated */
474 size
= size
/ 125; /* 128/125 = 1024/1000 */
475 size
= size
* 128; /* This will convert size from multiple of 1024 to multiple of 1000 */
476 size
+= size_remain
; /* Re-add remainder lost by division/multiplication */
479 for (j
= units
; suffix
[j
] != NULL
; j
++)
485 /* Empty files will print "0" even with minimal width. */
486 g_snprintf (buffer
, len
+ 1, "0");
490 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
491 g_snprintf (buffer
, len
+ 1, (len
> 1) ? "~%s" : "%s",
492 (j
> 1) ? (use_si
? suffix_lc
[j
- 1] : suffix
[j
- 1]) : "B");
496 if (size
< power10
[len
- (j
> 0 ? 1 : 0)])
498 g_snprintf (buffer
, len
+ 1, "%" PRIuMAX
"%s", size
, use_si
? suffix_lc
[j
] : suffix
[j
]);
502 /* Powers of 1000 or 1024, with rounding. */
504 size
= (size
+ 500) / 1000;
506 size
= (size
+ 512) >> 10;
510 /* --------------------------------------------------------------------------------------------- */
513 string_perm (mode_t mode_bits
)
515 static char mode
[11];
517 strcpy (mode
, "----------");
518 if (S_ISDIR (mode_bits
))
520 if (S_ISCHR (mode_bits
))
522 if (S_ISBLK (mode_bits
))
524 if (S_ISLNK (mode_bits
))
526 if (S_ISFIFO (mode_bits
))
528 if (S_ISNAM (mode_bits
))
530 if (S_ISSOCK (mode_bits
))
532 if (S_ISDOOR (mode_bits
))
534 if (ismode (mode_bits
, S_IXOTH
))
536 if (ismode (mode_bits
, S_IWOTH
))
538 if (ismode (mode_bits
, S_IROTH
))
540 if (ismode (mode_bits
, S_IXGRP
))
542 if (ismode (mode_bits
, S_IWGRP
))
544 if (ismode (mode_bits
, S_IRGRP
))
546 if (ismode (mode_bits
, S_IXUSR
))
548 if (ismode (mode_bits
, S_IWUSR
))
550 if (ismode (mode_bits
, S_IRUSR
))
553 if (ismode (mode_bits
, S_ISUID
))
554 mode
[3] = (mode
[3] == 'x') ? 's' : 'S';
557 if (ismode (mode_bits
, S_ISGID
))
558 mode
[6] = (mode
[6] == 'x') ? 's' : 'S';
561 if (ismode (mode_bits
, S_ISVTX
))
562 mode
[9] = (mode
[9] == 'x') ? 't' : 'T';
567 /* --------------------------------------------------------------------------------------------- */
569 * p: string which might contain an url with a password (this parameter is
570 * modified in place).
571 * has_prefix = 0: The first parameter is an url without a prefix
572 * (user[:pass]@]machine[:port][remote-dir). Delete
574 * has_prefix = 1: Search p for known url prefixes. If found delete
575 * the password from the url.
576 * Caveat: only the first url is found
580 strip_password (char *p
, int has_prefix
)
599 char *at
, *inner_colon
, *dir
;
603 for (i
= 0; i
< sizeof (prefixes
) / sizeof (prefixes
[0]); i
++)
609 q
= strstr (p
, prefixes
[i
].name
);
613 p
= q
+ prefixes
[i
].len
;
616 dir
= strchr (p
, PATH_SEP
);
620 /* search for any possible user */
621 at
= strrchr (p
, '@');
626 /* We have a username */
629 inner_colon
= memchr (p
, ':', at
- p
);
631 memmove (inner_colon
, at
, strlen (at
) + 1);
638 /* --------------------------------------------------------------------------------------------- */
641 strip_home_and_password (const char *dir
)
644 static char newdir
[MC_MAXPATHLEN
];
646 len
= strlen (mc_config_get_home_dir ());
647 if (mc_config_get_home_dir () != NULL
&& strncmp (dir
, mc_config_get_home_dir (), len
) == 0 &&
648 (dir
[len
] == PATH_SEP
|| dir
[len
] == '\0'))
651 g_strlcpy (&newdir
[1], &dir
[len
], sizeof (newdir
) - 1);
655 /* We do not strip homes in /#ftp tree, I do not like ~'s there
657 g_strlcpy (newdir
, dir
, sizeof (newdir
));
658 strip_password (newdir
, 1);
662 /* --------------------------------------------------------------------------------------------- */
665 extension (const char *filename
)
667 const char *d
= strrchr (filename
, '.');
668 return (d
!= NULL
) ? d
+ 1 : "";
671 /* --------------------------------------------------------------------------------------------- */
674 load_mc_home_file (const char *from
, const char *filename
, char **allocated_filename
)
676 char *hintfile_base
, *hintfile
;
680 hintfile_base
= g_build_filename (from
, filename
, (char *) NULL
);
681 lang
= guess_message_value ();
683 hintfile
= g_strconcat (hintfile_base
, ".", lang
, (char *) NULL
);
684 if (!g_file_get_contents (hintfile
, &data
, NULL
, NULL
))
686 /* Fall back to the two-letter language code */
687 if (lang
[0] != '\0' && lang
[1] != '\0')
690 hintfile
= g_strconcat (hintfile_base
, ".", lang
, (char *) NULL
);
691 if (!g_file_get_contents (hintfile
, &data
, NULL
, NULL
))
694 hintfile
= hintfile_base
;
695 g_file_get_contents (hintfile_base
, &data
, NULL
, NULL
);
701 if (hintfile
!= hintfile_base
)
702 g_free (hintfile_base
);
704 if (allocated_filename
!= NULL
)
705 *allocated_filename
= hintfile
;
712 /* --------------------------------------------------------------------------------------------- */
715 extract_line (const char *s
, const char *top
)
717 static char tmp_line
[BUF_MEDIUM
];
720 while (*s
&& *s
!= '\n' && (size_t) (t
- tmp_line
) < sizeof (tmp_line
) - 1 && s
< top
)
726 /* --------------------------------------------------------------------------------------------- */
728 * The basename routine
732 x_basename (const char *s
)
734 const char *url_delim
, *path_sep
;
736 url_delim
= g_strrstr (s
, VFS_PATH_URL_DELIMITER
);
737 path_sep
= strrchr (s
, PATH_SEP
);
739 if (url_delim
== NULL
740 || url_delim
< path_sep
- strlen (VFS_PATH_URL_DELIMITER
)
741 || url_delim
- s
+ strlen (VFS_PATH_URL_DELIMITER
) < strlen (s
))
743 /* avoid trailing PATH_SEP, if present */
744 if (s
[strlen (s
) - 1] == PATH_SEP
)
746 while (--path_sep
> s
&& *path_sep
!= PATH_SEP
);
747 return (path_sep
!= s
) ? path_sep
+ 1 : s
;
750 return (path_sep
!= NULL
) ? path_sep
+ 1 : s
;
753 while (--url_delim
> s
&& *url_delim
!= PATH_SEP
);
754 while (--url_delim
> s
&& *url_delim
!= PATH_SEP
);
756 return (url_delim
== s
) ? s
: url_delim
+ 1;
759 /* --------------------------------------------------------------------------------------------- */
762 unix_error_string (int error_num
)
764 static char buffer
[BUF_LARGE
];
765 gchar
*strerror_currentlocale
;
767 strerror_currentlocale
= g_locale_from_utf8 (g_strerror (error_num
), -1, NULL
, NULL
, NULL
);
768 g_snprintf (buffer
, sizeof (buffer
), "%s (%d)", strerror_currentlocale
, error_num
);
769 g_free (strerror_currentlocale
);
774 /* --------------------------------------------------------------------------------------------- */
777 skip_separators (const char *s
)
781 for (; *su
; str_cnext_char (&su
))
782 if (*su
!= ' ' && *su
!= '\t' && *su
!= ',')
788 /* --------------------------------------------------------------------------------------------- */
791 skip_numbers (const char *s
)
795 for (; *su
; str_cnext_char (&su
))
796 if (!str_isdigit (su
))
802 /* --------------------------------------------------------------------------------------------- */
804 * Remove all control sequences from the argument string. We define
805 * "control sequence", in a sort of pidgin BNF, as follows:
807 * control-seq = Esc non-'['
808 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
810 * This scheme works for all the terminals described in my termcap /
811 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
812 * terminals. If I hear from a single person who uses such a terminal
813 * with MC, I'll be glad to add support for it. (Dugan)
814 * Non-printable characters are also removed.
818 strip_ctrl_codes (char *s
)
820 char *w
; /* Current position where the stripped data is written */
821 char *r
; /* Current position where the original data is read */
827 for (w
= s
, r
= s
; *r
;)
831 /* Skip the control sequence's arguments */ ;
832 /* '(' need to avoid strange 'B' letter in *Suse (if mc runs under root user) */
833 if (*(++r
) == '[' || *r
== '(')
835 /* strchr() matches trailing binary 0 */
836 while (*(++r
) && strchr ("0123456789;?", *r
));
841 * Skip xterm's OSC (Operating System Command)
842 * http://www.xfree86.org/current/ctlseqs.html
848 for (; *new_r
; ++new_r
)
858 if (*(new_r
+ 1) == '\\')
869 * Now we are at the last character of the sequence.
870 * Skip it unless it's binary 0.
877 n
= str_get_next_char (r
);
880 memmove (w
, r
, n
- r
);
889 /* --------------------------------------------------------------------------------------------- */
891 enum compression_type
892 get_compression_type (int fd
, const char *name
)
894 unsigned char magic
[16];
897 /* Read the magic signature */
898 if (mc_read (fd
, (char *) magic
, 4) != 4)
899 return COMPRESSION_NONE
;
901 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
902 if (magic
[0] == 037 && (magic
[1] == 0213 || magic
[1] == 0236))
904 return COMPRESSION_GZIP
;
908 if (magic
[0] == 0120 && magic
[1] == 0113 && magic
[2] == 003 && magic
[3] == 004)
910 /* Read compression type */
911 mc_lseek (fd
, 8, SEEK_SET
);
912 if (mc_read (fd
, (char *) magic
, 2) != 2)
913 return COMPRESSION_NONE
;
915 /* Gzip can handle only deflated (8) or stored (0) files */
916 if ((magic
[0] != 8 && magic
[0] != 0) || magic
[1] != 0)
917 return COMPRESSION_NONE
;
919 /* Compatible with gzip */
920 return COMPRESSION_GZIP
;
923 /* PACK_MAGIC and LZH_MAGIC and compress magic */
924 if (magic
[0] == 037 && (magic
[1] == 036 || magic
[1] == 0240 || magic
[1] == 0235))
926 /* Compatible with gzip */
927 return COMPRESSION_GZIP
;
930 /* BZIP and BZIP2 files */
931 if ((magic
[0] == 'B') && (magic
[1] == 'Z') && (magic
[3] >= '1') && (magic
[3] <= '9'))
936 return COMPRESSION_BZIP
;
938 return COMPRESSION_BZIP2
;
942 /* Support for LZMA (only utils format with magic in header).
943 * This is the default format of LZMA utils 4.32.1 and later. */
945 if (mc_read (fd
, (char *) magic
+ 4, 2) != 2)
946 return COMPRESSION_NONE
;
948 /* LZMA utils format */
951 && magic
[2] == 'Z' && magic
[3] == 'M' && magic
[4] == 'A' && magic
[5] == 0x00)
952 return COMPRESSION_LZMA
;
954 /* XZ compression magic */
957 && magic
[2] == 0x7A && magic
[3] == 0x58 && magic
[4] == 0x5A && magic
[5] == 0x00)
958 return COMPRESSION_XZ
;
960 str_len
= strlen (name
);
961 /* HACK: we must belive to extention of LZMA file :) ... */
962 if ((str_len
> 5 && strcmp (&name
[str_len
- 5], ".lzma") == 0) ||
963 (str_len
> 4 && strcmp (&name
[str_len
- 4], ".tlz") == 0))
964 return COMPRESSION_LZMA
;
966 return COMPRESSION_NONE
;
969 /* --------------------------------------------------------------------------------------------- */
972 decompress_extension (int type
)
976 case COMPRESSION_GZIP
:
977 return "/ugz" VFS_PATH_URL_DELIMITER
;
978 case COMPRESSION_BZIP
:
979 return "/ubz" VFS_PATH_URL_DELIMITER
;
980 case COMPRESSION_BZIP2
:
981 return "/ubz2" VFS_PATH_URL_DELIMITER
;
982 case COMPRESSION_LZMA
:
983 return "/ulzma" VFS_PATH_URL_DELIMITER
;
985 return "/uxz" VFS_PATH_URL_DELIMITER
;
987 /* Should never reach this place */
988 fprintf (stderr
, "Fatal: decompress_extension called with an unknown argument\n");
992 /* --------------------------------------------------------------------------------------------- */
995 wipe_password (char *passwd
)
1006 /* --------------------------------------------------------------------------------------------- */
1008 * Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key
1009 * @returns a newly allocated string
1013 convert_controls (const char *p
)
1015 char *valcopy
= g_strdup (p
);
1018 /* Parse the escape special character */
1019 for (q
= valcopy
; *p
;)
1024 if ((*p
== 'e') || (*p
== 'E'))
1039 char c
= (*p
| 0x20);
1040 if (c
>= 'a' && c
<= 'z')
1057 /* --------------------------------------------------------------------------------------------- */
1059 * Finds out a relative path from first to second, i.e. goes as many ..
1060 * as needed up in first and then goes down using second
1064 diff_two_paths (const vfs_path_t
* vpath1
, const vfs_path_t
* vpath2
)
1066 char *p
, *q
, *r
, *s
, *buf
= NULL
;
1067 int i
, j
, prevlen
= -1, currlen
;
1068 char *my_first
= NULL
, *my_second
= NULL
;
1071 path_str
= vfs_path_to_str (vpath1
);
1072 my_first
= resolve_symlinks (path_str
);
1074 if (my_first
== NULL
)
1077 path_str
= vfs_path_to_str (vpath2
);
1078 my_second
= resolve_symlinks (path_str
);
1080 if (my_second
== NULL
)
1086 for (j
= 0; j
< 2; j
++)
1092 r
= strchr (p
, PATH_SEP
);
1093 s
= strchr (q
, PATH_SEP
);
1113 for (i
= 0; (p
= strchr (p
+ 1, PATH_SEP
)) != NULL
; i
++);
1114 currlen
= (i
+ 1) * 3 + strlen (q
) + 1;
1117 if (currlen
< prevlen
)
1126 p
= buf
= g_malloc (currlen
);
1128 for (; i
>= 0; i
--, p
+= 3)
1137 /* --------------------------------------------------------------------------------------------- */
1139 * Append text to GList, remove all entries with the same text
1143 list_append_unique (GList
* list
, char *text
)
1148 * Go to the last position and traverse the list backwards
1149 * starting from the second last entry to make sure that we
1150 * are not removing the current link.
1152 list
= g_list_append (list
, text
);
1153 list
= g_list_last (list
);
1154 lc_link
= g_list_previous (list
);
1156 while (lc_link
!= NULL
)
1160 newlink
= g_list_previous (lc_link
);
1161 if (strcmp ((char *) lc_link
->data
, text
) == 0)
1165 g_free (lc_link
->data
);
1166 tmp
= g_list_remove_link (list
, lc_link
);
1167 g_list_free_1 (lc_link
);
1175 /* --------------------------------------------------------------------------------------------- */
1177 * Read and restore position for the given filename.
1178 * If there is no stored data, return line 1 and col 0.
1182 load_file_position (const vfs_path_t
* filename_vpath
, long *line
, long *column
, off_t
* offset
,
1183 GArray
** bookmarks
)
1187 char buf
[MC_MAXPATHLEN
+ 100];
1188 const size_t len
= vfs_path_len (filename_vpath
);
1196 /* open file with positions */
1197 fn
= mc_config_get_full_path (MC_FILEPOS_FILE
);
1198 f
= fopen (fn
, "r");
1203 /* prepare array for serialized bookmarks */
1204 *bookmarks
= g_array_sized_new (FALSE
, FALSE
, sizeof (size_t), MAX_SAVED_BOOKMARKS
);
1205 filename
= vfs_path_to_str (filename_vpath
);
1207 while (fgets (buf
, sizeof (buf
), f
) != NULL
)
1212 /* check if the filename matches the beginning of string */
1213 if (strncmp (buf
, filename
, len
) != 0)
1216 /* followed by single space */
1217 if (buf
[len
] != ' ')
1220 /* and string without spaces */
1222 if (strchr (p
, ' ') != NULL
)
1225 pos_tokens
= g_strsplit (p
, ";", 3 + MAX_SAVED_BOOKMARKS
);
1226 if (pos_tokens
[0] == NULL
)
1234 *line
= strtol (pos_tokens
[0], NULL
, 10);
1235 if (pos_tokens
[1] == NULL
)
1242 *column
= strtol (pos_tokens
[1], NULL
, 10);
1243 if (pos_tokens
[2] == NULL
)
1249 *offset
= strtoll (pos_tokens
[2], NULL
, 10);
1251 for (i
= 0; i
< MAX_SAVED_BOOKMARKS
&& pos_tokens
[3 + i
] != NULL
; i
++)
1255 val
= strtoul (pos_tokens
[3 + i
], NULL
, 10);
1256 g_array_append_val (*bookmarks
, val
);
1262 g_strfreev (pos_tokens
);
1269 /* --------------------------------------------------------------------------------------------- */
1271 * Save position for the given file
1275 save_file_position (const vfs_path_t
* filename_vpath
, long line
, long column
, off_t offset
,
1278 static size_t filepos_max_saved_entries
= 0;
1281 char buf
[MC_MAXPATHLEN
+ 100];
1283 const size_t len
= vfs_path_len (filename_vpath
);
1284 gboolean src_error
= FALSE
;
1287 if (filepos_max_saved_entries
== 0)
1288 filepos_max_saved_entries
= mc_config_get_int (mc_main_config
, CONFIG_APP_SECTION
,
1289 "filepos_max_saved_entries", 1024);
1291 fn
= mc_config_get_full_path (MC_FILEPOS_FILE
);
1295 mc_util_make_backup_if_possible (fn
, TMP_SUFFIX
);
1298 f
= fopen (fn
, "w");
1300 goto open_target_error
;
1302 tmp_fn
= g_strdup_printf ("%s" TMP_SUFFIX
, fn
);
1303 tmp_f
= fopen (tmp_fn
, "r");
1307 goto open_source_error
;
1310 filename
= vfs_path_to_str (filename_vpath
);
1311 /* put the new record */
1312 if (line
!= 1 || column
!= 0 || bookmarks
!= NULL
)
1314 if (fprintf (f
, "%s %ld;%ld;%" PRIuMAX
, filename
, line
, column
, (uintmax_t) offset
) < 0)
1315 goto write_position_error
;
1316 if (bookmarks
!= NULL
)
1317 for (i
= 0; i
< bookmarks
->len
&& i
< MAX_SAVED_BOOKMARKS
; i
++)
1318 if (fprintf (f
, ";%zu", g_array_index (bookmarks
, size_t, i
)) < 0)
1319 goto write_position_error
;
1321 if (fprintf (f
, "\n") < 0)
1322 goto write_position_error
;
1326 while (fgets (buf
, sizeof (buf
), tmp_f
) != NULL
)
1328 if (buf
[len
] == ' ' && strncmp (buf
, filename
, len
) == 0
1329 && strchr (&buf
[len
+ 1], ' ') == NULL
)
1332 fprintf (f
, "%s", buf
);
1333 if (++i
> filepos_max_saved_entries
)
1337 write_position_error
:
1344 mc_util_restore_from_backup_if_possible (fn
, TMP_SUFFIX
);
1346 mc_util_unlink_backup_if_possible (fn
, TMP_SUFFIX
);
1350 if (bookmarks
!= NULL
)
1351 g_array_free (bookmarks
, TRUE
);
1354 /* --------------------------------------------------------------------------------------------- */
1357 ascii_alpha_to_cntrl (int ch
)
1359 if ((ch
>= ASCII_A
&& ch
<= ASCII_Z
) || (ch
>= ASCII_a
&& ch
<= ASCII_z
))
1366 /* --------------------------------------------------------------------------------------------- */
1371 const char *result
, *sep
;
1374 sep
= strchr (result
, '|');
1375 return (sep
!= NULL
) ? sep
+ 1 : result
;
1378 /* --------------------------------------------------------------------------------------------- */
1381 mc_util_make_backup_if_possible (const char *file_name
, const char *backup_suffix
)
1383 struct stat stat_buf
;
1386 if (!exist_file (file_name
))
1389 backup_path
= g_strdup_printf ("%s%s", file_name
, backup_suffix
);
1391 if (backup_path
== NULL
)
1394 ret
= mc_util_write_backup_content (file_name
, backup_path
);
1398 /* Backup file will have same ownership with main file. */
1399 if (stat (file_name
, &stat_buf
) == 0)
1400 chmod (backup_path
, stat_buf
.st_mode
);
1402 chmod (backup_path
, S_IRUSR
| S_IWUSR
);
1405 g_free (backup_path
);
1410 /* --------------------------------------------------------------------------------------------- */
1413 mc_util_restore_from_backup_if_possible (const char *file_name
, const char *backup_suffix
)
1418 backup_path
= g_strdup_printf ("%s%s", file_name
, backup_suffix
);
1419 if (backup_path
== NULL
)
1422 ret
= mc_util_write_backup_content (backup_path
, file_name
);
1423 g_free (backup_path
);
1428 /* --------------------------------------------------------------------------------------------- */
1431 mc_util_unlink_backup_if_possible (const char *file_name
, const char *backup_suffix
)
1435 backup_path
= g_strdup_printf ("%s%s", file_name
, backup_suffix
);
1436 if (backup_path
== NULL
)
1439 if (exist_file (backup_path
))
1443 vpath
= vfs_path_from_str (backup_path
);
1445 vfs_path_free (vpath
);
1448 g_free (backup_path
);
1452 /* --------------------------------------------------------------------------------------------- */
1454 * partly taken from dcigettext.c, returns "" for default locale
1455 * value should be freed by calling function g_free()
1459 guess_message_value (void)
1461 static const char *const var
[] = {
1462 /* Setting of LC_ALL overwrites all other. */
1463 /* Do not use LANGUAGE for check user locale and drowing hints */
1465 /* Next comes the name of the desired category. */
1467 /* Last possibility is the LANG environment variable. */
1469 /* NULL exit loops */
1474 const char *locale
= NULL
;
1476 while (var
[i
] != NULL
)
1478 locale
= getenv (var
[i
]);
1479 if (locale
!= NULL
&& locale
[0] != '\0')
1487 return g_strdup (locale
);
1490 /* --------------------------------------------------------------------------------------------- */