2 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
3 2004, 2005, 2007, 2009 Free Software Foundation, Inc.
4 Written 1994, 1995, 1996 by:
5 Miguel de Icaza, Janne Kukonlehto, Dugan Porter,
6 Jakub Jelinek, Mauricio Plaza.
8 The file_date routine is mostly from GNU's fileutils package,
9 written by Richard Stallman and David MacKenzie.
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
26 * \brief Source: various utilities
39 #include <sys/types.h>
43 #include "lib/global.h"
44 #include "lib/tty/win.h" /* xterm_flag */
45 #include "lib/mcconfig.h"
46 #include "lib/fileloc.h"
47 #include "lib/vfs/vfs.h"
48 #include "lib/strutil.h"
51 /*** global variables ****************************************************************************/
53 /*** file scope macro definitions ****************************************************************/
55 #define ismode(n,m) ((n & m) == m)
57 /* Number of attempts to create a temporary file */
62 #define TMP_SUFFIX ".tmp"
64 #define ASCII_A (0x40 + 1)
65 #define ASCII_Z (0x40 + 26)
66 #define ASCII_a (0x60 + 1)
67 #define ASCII_z (0x60 + 26)
69 /*** file scope type declarations ****************************************************************/
71 /*** file scope variables ************************************************************************/
73 /*** file scope functions ************************************************************************/
74 /* --------------------------------------------------------------------------------------------- */
77 is_7bit_printable (unsigned char c
)
79 return (c
> 31 && c
< 127);
82 /* --------------------------------------------------------------------------------------------- */
85 is_iso_printable (unsigned char c
)
87 return ((c
> 31 && c
< 127) || c
>= 160);
90 /* --------------------------------------------------------------------------------------------- */
93 is_8bit_printable (unsigned char c
)
95 /* "Full 8 bits output" doesn't work on xterm */
97 return is_iso_printable (c
);
99 return (c
> 31 && c
!= 127 && c
!= 155);
102 /* --------------------------------------------------------------------------------------------- */
105 resolve_symlinks (const char *path
)
107 char *buf
, *buf2
, *q
, *r
, c
;
112 if (*path
!= PATH_SEP
)
114 r
= buf
= g_malloc (MC_MAXPATHLEN
);
115 buf2
= g_malloc (MC_MAXPATHLEN
);
121 q
= strchr (p
+ 1, PATH_SEP
);
124 q
= strchr (p
+ 1, 0);
130 if (mc_lstat (path
, &mybuf
) < 0)
137 if (!S_ISLNK (mybuf
.st_mode
))
141 len
= mc_readlink (path
, buf2
, MC_MAXPATHLEN
- 1);
150 if (*buf2
== PATH_SEP
)
155 canonicalize_pathname (buf
);
157 if (!*r
|| *(r
- 1) != PATH_SEP
)
168 strcpy (buf
, PATH_SEP_STR
);
169 else if (*(r
- 1) == PATH_SEP
&& r
!= buf
+ 1)
175 /* --------------------------------------------------------------------------------------------- */
178 mc_util_write_backup_content (const char *from_file_name
, const char *to_file_name
)
183 gboolean ret1
= TRUE
;
185 if (!g_file_get_contents (from_file_name
, &contents
, &length
, NULL
))
188 backup_fd
= fopen (to_file_name
, "w");
189 if (backup_fd
== NULL
)
195 if (fwrite ((const void *) contents
, 1, length
, backup_fd
) != length
)
199 ret2
= fflush (backup_fd
);
200 ret2
= fclose (backup_fd
);
206 /* --------------------------------------------------------------------------------------------- */
207 /*** public functions ****************************************************************************/
208 /* --------------------------------------------------------------------------------------------- */
216 /* "Display bits" is ignored, since the user controls the output
217 by setting the output codepage */
218 return is_8bit_printable (c
);
220 if (!mc_global
.eight_bit_clean
)
221 return is_7bit_printable (c
);
223 if (mc_global
.full_eight_bits
)
225 return is_8bit_printable (c
);
228 return is_iso_printable (c
);
229 #endif /* !HAVE_CHARSET */
232 /* --------------------------------------------------------------------------------------------- */
234 * Quote the filename for the purpose of inserting it into the command
235 * line. If quote_percent is 1, replace "%" with "%%" - the percent is
236 * processed by the mc command line.
239 name_quote (const char *s
, int quote_percent
)
243 d
= ret
= g_malloc (strlen (s
) * 2 + 2 + 1);
295 /* --------------------------------------------------------------------------------------------- */
298 fake_name_quote (const char *s
, int quote_percent
)
300 (void) quote_percent
;
304 /* --------------------------------------------------------------------------------------------- */
306 * path_trunc() is the same as str_trunc() but
307 * it deletes possible password from path for security
312 path_trunc (const char *path
, size_t trunc_len
)
314 char *secure_path
= strip_password (g_strdup (path
), 1);
316 const char *ret
= str_trunc (secure_path
, trunc_len
);
317 g_free (secure_path
);
322 /* --------------------------------------------------------------------------------------------- */
325 size_trunc (uintmax_t size
, gboolean use_si
)
327 static char x
[BUF_TINY
];
328 uintmax_t divisor
= 1;
329 const char *xtra
= "";
331 if (size
> 999999999UL)
333 divisor
= use_si
? 1000 : 1024;
334 xtra
= use_si
? "k" : "K";
336 if (size
/ divisor
> 999999999UL)
338 divisor
= use_si
? (1000 * 1000) : (1024 * 1024);
339 xtra
= use_si
? "m" : "M";
341 if (size
/ divisor
> 999999999UL)
343 divisor
= use_si
? (1000 * 1000 * 1000) : (1024 * 1024 * 1024);
344 xtra
= use_si
? "g" : "G";
348 g_snprintf (x
, sizeof (x
), "%.0f%s", 1.0 * size
/ divisor
, xtra
);
352 /* --------------------------------------------------------------------------------------------- */
355 size_trunc_sep (uintmax_t size
, gboolean use_si
)
362 p
= y
= size_trunc (size
, use_si
);
364 d
= x
+ sizeof (x
) - 1;
366 while (p
>= y
&& isalpha ((unsigned char) *p
))
368 for (count
= 0; p
>= y
; count
++)
383 /* --------------------------------------------------------------------------------------------- */
385 * Print file SIZE to BUFFER, but don't exceed LEN characters,
386 * not including trailing 0. BUFFER should be at least LEN+1 long.
387 * This function is called for every file on panels, so avoid
388 * floating point by any means.
390 * Units: size units (filesystem sizes are 1K blocks)
391 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
395 size_trunc_len (char *buffer
, unsigned int len
, uintmax_t size
, int units
, gboolean use_si
)
397 /* Avoid taking power for every file. */
398 static const uintmax_t power10
[] = {
399 /* we hope that size of uintmax_t is 4 bytes at least */
410 /* maximmum value of uintmax_t (in case of 4 bytes) is
413 #if SIZEOF_UINTMAX_T == 8
421 10000000000000000ULL,
422 100000000000000000ULL,
423 1000000000000000000ULL,
424 10000000000000000000ULL
425 /* maximmum value of uintmax_t (in case of 8 bytes) is
430 static const char *const suffix
[] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL
};
431 static const char *const suffix_lc
[] = { "", "k", "m", "g", "t", "p", "e", "z", "y", NULL
};
437 #if SIZEOF_UINTMAX_T == 8
438 /* 20 decimal digits are required to represent 8 bytes */
442 /* 10 decimal digits are required to represent 4 bytes */
448 * recalculate from 1024 base to 1000 base if units>0
449 * We can't just multiply by 1024 - that might cause overflow
450 * if off_t type is too small
453 for (j
= 0; j
< units
; j
++)
455 size_remain
= ((size
% 125) * 1024) / 1000; /* size mod 125, recalculated */
456 size
= size
/ 125; /* 128/125 = 1024/1000 */
457 size
= size
* 128; /* This will convert size from multiple of 1024 to multiple of 1000 */
458 size
+= size_remain
; /* Re-add remainder lost by division/multiplication */
461 for (j
= units
; suffix
[j
] != NULL
; j
++)
467 /* Empty files will print "0" even with minimal width. */
468 g_snprintf (buffer
, len
+ 1, "0");
472 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
473 g_snprintf (buffer
, len
+ 1, (len
> 1) ? "~%s" : "%s",
474 (j
> 1) ? (use_si
? suffix_lc
[j
- 1] : suffix
[j
- 1]) : "B");
478 if (size
< power10
[len
- (j
> 0 ? 1 : 0)])
480 g_snprintf (buffer
, len
+ 1, "%" PRIuMAX
"%s", size
, use_si
? suffix_lc
[j
] : suffix
[j
]);
484 /* Powers of 1000 or 1024, with rounding. */
486 size
= (size
+ 500) / 1000;
488 size
= (size
+ 512) >> 10;
492 /* --------------------------------------------------------------------------------------------- */
495 string_perm (mode_t mode_bits
)
497 static char mode
[11];
499 strcpy (mode
, "----------");
500 if (S_ISDIR (mode_bits
))
502 if (S_ISCHR (mode_bits
))
504 if (S_ISBLK (mode_bits
))
506 if (S_ISLNK (mode_bits
))
508 if (S_ISFIFO (mode_bits
))
510 if (S_ISNAM (mode_bits
))
512 if (S_ISSOCK (mode_bits
))
514 if (S_ISDOOR (mode_bits
))
516 if (ismode (mode_bits
, S_IXOTH
))
518 if (ismode (mode_bits
, S_IWOTH
))
520 if (ismode (mode_bits
, S_IROTH
))
522 if (ismode (mode_bits
, S_IXGRP
))
524 if (ismode (mode_bits
, S_IWGRP
))
526 if (ismode (mode_bits
, S_IRGRP
))
528 if (ismode (mode_bits
, S_IXUSR
))
530 if (ismode (mode_bits
, S_IWUSR
))
532 if (ismode (mode_bits
, S_IRUSR
))
535 if (ismode (mode_bits
, S_ISUID
))
536 mode
[3] = (mode
[3] == 'x') ? 's' : 'S';
539 if (ismode (mode_bits
, S_ISGID
))
540 mode
[6] = (mode
[6] == 'x') ? 's' : 'S';
543 if (ismode (mode_bits
, S_ISVTX
))
544 mode
[9] = (mode
[9] == 'x') ? 't' : 'T';
549 /* --------------------------------------------------------------------------------------------- */
551 * p: string which might contain an url with a password (this parameter is
552 * modified in place).
553 * has_prefix = 0: The first parameter is an url without a prefix
554 * (user[:pass]@]machine[:port][remote-dir). Delete
556 * has_prefix = 1: Search p for known url prefixes. If found delete
557 * the password from the url.
558 * Caveat: only the first url is found
562 strip_password (char *p
, int has_prefix
)
581 char *at
, *inner_colon
, *dir
;
585 for (i
= 0; i
< sizeof (prefixes
) / sizeof (prefixes
[0]); i
++)
591 q
= strstr (p
, prefixes
[i
].name
);
595 p
= q
+ prefixes
[i
].len
;
598 dir
= strchr (p
, PATH_SEP
);
602 /* search for any possible user */
603 at
= strrchr (p
, '@');
608 /* We have a username */
611 inner_colon
= memchr (p
, ':', at
- p
);
613 memmove (inner_colon
, at
, strlen (at
) + 1);
620 /* --------------------------------------------------------------------------------------------- */
623 strip_home_and_password (const char *dir
)
626 static char newdir
[MC_MAXPATHLEN
];
628 len
= strlen (mc_config_get_home_dir ());
629 if (mc_config_get_home_dir () != NULL
&& strncmp (dir
, mc_config_get_home_dir (), len
) == 0 &&
630 (dir
[len
] == PATH_SEP
|| dir
[len
] == '\0'))
633 g_strlcpy (&newdir
[1], &dir
[len
], sizeof (newdir
) - 1);
637 /* We do not strip homes in /#ftp tree, I do not like ~'s there
639 g_strlcpy (newdir
, dir
, sizeof (newdir
));
640 strip_password (newdir
, 1);
644 /* --------------------------------------------------------------------------------------------- */
647 extension (const char *filename
)
649 const char *d
= strrchr (filename
, '.');
650 return (d
!= NULL
) ? d
+ 1 : "";
653 /* --------------------------------------------------------------------------------------------- */
656 load_mc_home_file (const char *from
, const char *filename
, char **allocated_filename
)
658 char *hintfile_base
, *hintfile
;
662 hintfile_base
= g_build_filename (from
, filename
, (char *) NULL
);
663 lang
= guess_message_value ();
665 hintfile
= g_strconcat (hintfile_base
, ".", lang
, (char *) NULL
);
666 if (!g_file_get_contents (hintfile
, &data
, NULL
, NULL
))
668 /* Fall back to the two-letter language code */
669 if (lang
[0] != '\0' && lang
[1] != '\0')
672 hintfile
= g_strconcat (hintfile_base
, ".", lang
, (char *) NULL
);
673 if (!g_file_get_contents (hintfile
, &data
, NULL
, NULL
))
676 hintfile
= hintfile_base
;
677 g_file_get_contents (hintfile_base
, &data
, NULL
, NULL
);
683 if (hintfile
!= hintfile_base
)
684 g_free (hintfile_base
);
686 if (allocated_filename
!= NULL
)
687 *allocated_filename
= hintfile
;
694 /* --------------------------------------------------------------------------------------------- */
697 extract_line (const char *s
, const char *top
)
699 static char tmp_line
[BUF_MEDIUM
];
702 while (*s
&& *s
!= '\n' && (size_t) (t
- tmp_line
) < sizeof (tmp_line
) - 1 && s
< top
)
708 /* --------------------------------------------------------------------------------------------- */
710 * The basename routine
714 x_basename (const char *s
)
716 const char *url_delim
, *path_sep
;
718 url_delim
= g_strrstr (s
, VFS_PATH_URL_DELIMITER
);
719 path_sep
= strrchr (s
, PATH_SEP
);
721 if (url_delim
== NULL
722 || url_delim
< path_sep
- strlen (VFS_PATH_URL_DELIMITER
)
723 || url_delim
- s
+ strlen (VFS_PATH_URL_DELIMITER
) < strlen (s
))
725 /* avoid trailing PATH_SEP, if present */
726 if (s
[strlen (s
) - 1] == PATH_SEP
)
728 while (--path_sep
> s
&& *path_sep
!= PATH_SEP
);
729 return (path_sep
!= s
) ? path_sep
+ 1 : s
;
732 return (path_sep
!= NULL
) ? path_sep
+ 1 : s
;
735 while (--url_delim
> s
&& *url_delim
!= PATH_SEP
);
736 while (--url_delim
> s
&& *url_delim
!= PATH_SEP
);
738 return (url_delim
== s
) ? s
: url_delim
+ 1;
741 /* --------------------------------------------------------------------------------------------- */
744 unix_error_string (int error_num
)
746 static char buffer
[BUF_LARGE
];
747 gchar
*strerror_currentlocale
;
749 strerror_currentlocale
= g_locale_from_utf8 (g_strerror (error_num
), -1, NULL
, NULL
, NULL
);
750 g_snprintf (buffer
, sizeof (buffer
), "%s (%d)", strerror_currentlocale
, error_num
);
751 g_free (strerror_currentlocale
);
756 /* --------------------------------------------------------------------------------------------- */
759 skip_separators (const char *s
)
763 for (; *su
; str_cnext_char (&su
))
764 if (*su
!= ' ' && *su
!= '\t' && *su
!= ',')
770 /* --------------------------------------------------------------------------------------------- */
773 skip_numbers (const char *s
)
777 for (; *su
; str_cnext_char (&su
))
778 if (!str_isdigit (su
))
784 /* --------------------------------------------------------------------------------------------- */
786 * Remove all control sequences from the argument string. We define
787 * "control sequence", in a sort of pidgin BNF, as follows:
789 * control-seq = Esc non-'['
790 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
792 * This scheme works for all the terminals described in my termcap /
793 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
794 * terminals. If I hear from a single person who uses such a terminal
795 * with MC, I'll be glad to add support for it. (Dugan)
796 * Non-printable characters are also removed.
800 strip_ctrl_codes (char *s
)
802 char *w
; /* Current position where the stripped data is written */
803 char *r
; /* Current position where the original data is read */
809 for (w
= s
, r
= s
; *r
;)
813 /* Skip the control sequence's arguments */ ;
814 /* '(' need to avoid strange 'B' letter in *Suse (if mc runs under root user) */
815 if (*(++r
) == '[' || *r
== '(')
817 /* strchr() matches trailing binary 0 */
818 while (*(++r
) && strchr ("0123456789;?", *r
));
823 * Skip xterm's OSC (Operating System Command)
824 * http://www.xfree86.org/current/ctlseqs.html
830 for (; *new_r
; ++new_r
)
840 if (*(new_r
+ 1) == '\\')
851 * Now we are at the last character of the sequence.
852 * Skip it unless it's binary 0.
859 n
= str_get_next_char (r
);
862 memmove (w
, r
, n
- r
);
871 /* --------------------------------------------------------------------------------------------- */
873 enum compression_type
874 get_compression_type (int fd
, const char *name
)
876 unsigned char magic
[16];
879 /* Read the magic signature */
880 if (mc_read (fd
, (char *) magic
, 4) != 4)
881 return COMPRESSION_NONE
;
883 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
884 if (magic
[0] == 037 && (magic
[1] == 0213 || magic
[1] == 0236))
886 return COMPRESSION_GZIP
;
890 if (magic
[0] == 0120 && magic
[1] == 0113 && magic
[2] == 003 && magic
[3] == 004)
892 /* Read compression type */
893 mc_lseek (fd
, 8, SEEK_SET
);
894 if (mc_read (fd
, (char *) magic
, 2) != 2)
895 return COMPRESSION_NONE
;
897 /* Gzip can handle only deflated (8) or stored (0) files */
898 if ((magic
[0] != 8 && magic
[0] != 0) || magic
[1] != 0)
899 return COMPRESSION_NONE
;
901 /* Compatible with gzip */
902 return COMPRESSION_GZIP
;
905 /* PACK_MAGIC and LZH_MAGIC and compress magic */
906 if (magic
[0] == 037 && (magic
[1] == 036 || magic
[1] == 0240 || magic
[1] == 0235))
908 /* Compatible with gzip */
909 return COMPRESSION_GZIP
;
912 /* BZIP and BZIP2 files */
913 if ((magic
[0] == 'B') && (magic
[1] == 'Z') && (magic
[3] >= '1') && (magic
[3] <= '9'))
918 return COMPRESSION_BZIP
;
920 return COMPRESSION_BZIP2
;
924 /* Support for LZMA (only utils format with magic in header).
925 * This is the default format of LZMA utils 4.32.1 and later. */
927 if (mc_read (fd
, (char *) magic
+ 4, 2) != 2)
928 return COMPRESSION_NONE
;
930 /* LZMA utils format */
933 && magic
[2] == 'Z' && magic
[3] == 'M' && magic
[4] == 'A' && magic
[5] == 0x00)
934 return COMPRESSION_LZMA
;
936 /* XZ compression magic */
939 && magic
[2] == 0x7A && magic
[3] == 0x58 && magic
[4] == 0x5A && magic
[5] == 0x00)
940 return COMPRESSION_XZ
;
942 str_len
= strlen (name
);
943 /* HACK: we must belive to extention of LZMA file :) ... */
944 if ((str_len
> 5 && strcmp (&name
[str_len
- 5], ".lzma") == 0) ||
945 (str_len
> 4 && strcmp (&name
[str_len
- 4], ".tlz") == 0))
946 return COMPRESSION_LZMA
;
948 return COMPRESSION_NONE
;
951 /* --------------------------------------------------------------------------------------------- */
954 decompress_extension (int type
)
958 case COMPRESSION_GZIP
:
959 return "/ugz" VFS_PATH_URL_DELIMITER
;
960 case COMPRESSION_BZIP
:
961 return "/ubz" VFS_PATH_URL_DELIMITER
;
962 case COMPRESSION_BZIP2
:
963 return "/ubz2" VFS_PATH_URL_DELIMITER
;
964 case COMPRESSION_LZMA
:
965 return "/ulzma" VFS_PATH_URL_DELIMITER
;
967 return "/uxz" VFS_PATH_URL_DELIMITER
;
969 /* Should never reach this place */
970 fprintf (stderr
, "Fatal: decompress_extension called with an unknown argument\n");
974 /* --------------------------------------------------------------------------------------------- */
977 wipe_password (char *passwd
)
988 /* --------------------------------------------------------------------------------------------- */
990 * Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key
991 * @returns a newly allocated string
995 convert_controls (const char *p
)
997 char *valcopy
= g_strdup (p
);
1000 /* Parse the escape special character */
1001 for (q
= valcopy
; *p
;)
1006 if ((*p
== 'e') || (*p
== 'E'))
1021 char c
= (*p
| 0x20);
1022 if (c
>= 'a' && c
<= 'z')
1039 /* --------------------------------------------------------------------------------------------- */
1041 * Finds out a relative path from first to second, i.e. goes as many ..
1042 * as needed up in first and then goes down using second
1046 diff_two_paths (const char *first
, const char *second
)
1048 char *p
, *q
, *r
, *s
, *buf
= NULL
;
1049 int i
, j
, prevlen
= -1, currlen
;
1050 char *my_first
= NULL
, *my_second
= NULL
;
1052 my_first
= resolve_symlinks (first
);
1053 if (my_first
== NULL
)
1055 my_second
= resolve_symlinks (second
);
1056 if (my_second
== NULL
)
1061 for (j
= 0; j
< 2; j
++)
1067 r
= strchr (p
, PATH_SEP
);
1068 s
= strchr (q
, PATH_SEP
);
1088 for (i
= 0; (p
= strchr (p
+ 1, PATH_SEP
)) != NULL
; i
++);
1089 currlen
= (i
+ 1) * 3 + strlen (q
) + 1;
1092 if (currlen
< prevlen
)
1101 p
= buf
= g_malloc (currlen
);
1103 for (; i
>= 0; i
--, p
+= 3)
1112 /* --------------------------------------------------------------------------------------------- */
1114 * If filename is NULL, then we just append PATH_SEP to the dir
1118 concat_dir_and_file (const char *dir
, const char *file
)
1120 int i
= strlen (dir
);
1122 if (dir
[i
- 1] == PATH_SEP
)
1123 return g_strconcat (dir
, file
, (char *) NULL
);
1125 return g_strconcat (dir
, PATH_SEP_STR
, file
, (char *) NULL
);
1128 /* --------------------------------------------------------------------------------------------- */
1130 * Append text to GList, remove all entries with the same text
1134 list_append_unique (GList
* list
, char *text
)
1139 * Go to the last position and traverse the list backwards
1140 * starting from the second last entry to make sure that we
1141 * are not removing the current link.
1143 list
= g_list_append (list
, text
);
1144 list
= g_list_last (list
);
1145 lc_link
= g_list_previous (list
);
1147 while (lc_link
!= NULL
)
1151 newlink
= g_list_previous (lc_link
);
1152 if (strcmp ((char *) lc_link
->data
, text
) == 0)
1156 g_free (lc_link
->data
);
1157 tmp
= g_list_remove_link (list
, lc_link
);
1158 g_list_free_1 (lc_link
);
1166 /* --------------------------------------------------------------------------------------------- */
1167 /* Following code heavily borrows from libiberty, mkstemps.c */
1170 * pname (output) - pointer to the name of the temp file (needs g_free).
1171 * NULL if the function fails.
1172 * prefix - part of the filename before the random part.
1173 * Prepend $TMPDIR or /tmp if there are no path separators.
1174 * suffix - if not NULL, part of the filename after the random part.
1177 * handle of the open file or -1 if couldn't open any.
1181 mc_mkstemps (char **pname
, const char *prefix
, const char *suffix
)
1183 static const char letters
[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1184 static unsigned long value
;
1191 if (strchr (prefix
, PATH_SEP
) == NULL
)
1193 /* Add prefix first to find the position of XXXXXX */
1194 tmpbase
= concat_dir_and_file (mc_tmpdir (), prefix
);
1198 tmpbase
= g_strdup (prefix
);
1201 tmpname
= g_strconcat (tmpbase
, "XXXXXX", suffix
, (char *) NULL
);
1203 XXXXXX
= &tmpname
[strlen (tmpbase
)];
1206 /* Get some more or less random data. */
1207 gettimeofday (&tv
, NULL
);
1208 value
+= (tv
.tv_usec
<< 16) ^ tv
.tv_sec
^ getpid ();
1210 for (count
= 0; count
< TMP_MAX
; ++count
)
1212 unsigned long v
= value
;
1215 /* Fill in the random bits. */
1216 XXXXXX
[0] = letters
[v
% 62];
1218 XXXXXX
[1] = letters
[v
% 62];
1220 XXXXXX
[2] = letters
[v
% 62];
1222 XXXXXX
[3] = letters
[v
% 62];
1224 XXXXXX
[4] = letters
[v
% 62];
1226 XXXXXX
[5] = letters
[v
% 62];
1228 fd
= open (tmpname
, O_RDWR
| O_CREAT
| O_TRUNC
| O_EXCL
, S_IRUSR
| S_IWUSR
);
1231 /* Successfully created. */
1235 /* This is a random value. It is only necessary that the next
1236 TMP_MAX values generated by adding 7777 to VALUE are different
1237 with (module 2^32). */
1241 /* Unsuccessful. Free the filename. */
1248 /* --------------------------------------------------------------------------------------------- */
1250 * Read and restore position for the given filename.
1251 * If there is no stored data, return line 1 and col 0.
1255 load_file_position (const char *filename
, long *line
, long *column
, off_t
* offset
,
1256 GArray
** bookmarks
)
1260 char buf
[MC_MAXPATHLEN
+ 100];
1261 const size_t len
= strlen (filename
);
1268 /* open file with positions */
1269 fn
= g_build_filename (mc_config_get_cache_path (), MC_FILEPOS_FILE
, NULL
);
1270 f
= fopen (fn
, "r");
1275 /* prepare array for serialized bookmarks */
1276 *bookmarks
= g_array_sized_new (FALSE
, FALSE
, sizeof (size_t), MAX_SAVED_BOOKMARKS
);
1278 while (fgets (buf
, sizeof (buf
), f
) != NULL
)
1283 /* check if the filename matches the beginning of string */
1284 if (strncmp (buf
, filename
, len
) != 0)
1287 /* followed by single space */
1288 if (buf
[len
] != ' ')
1291 /* and string without spaces */
1293 if (strchr (p
, ' ') != NULL
)
1296 pos_tokens
= g_strsplit (p
, ";", 3 + MAX_SAVED_BOOKMARKS
);
1297 if (pos_tokens
[0] == NULL
)
1305 *line
= strtol (pos_tokens
[0], NULL
, 10);
1306 if (pos_tokens
[1] == NULL
)
1313 *column
= strtol (pos_tokens
[1], NULL
, 10);
1314 if (pos_tokens
[2] == NULL
)
1320 *offset
= strtoll (pos_tokens
[2], NULL
, 10);
1322 for (i
= 0; i
< MAX_SAVED_BOOKMARKS
&& pos_tokens
[3 + i
] != NULL
; i
++)
1326 val
= strtoul (pos_tokens
[3 + i
], NULL
, 10);
1327 g_array_append_val (*bookmarks
, val
);
1333 g_strfreev (pos_tokens
);
1339 /* --------------------------------------------------------------------------------------------- */
1341 * Save position for the given file
1345 save_file_position (const char *filename
, long line
, long column
, off_t offset
, GArray
* bookmarks
)
1347 static size_t filepos_max_saved_entries
= 0;
1350 char buf
[MC_MAXPATHLEN
+ 100];
1352 const size_t len
= strlen (filename
);
1353 gboolean src_error
= FALSE
;
1355 if (filepos_max_saved_entries
== 0)
1356 filepos_max_saved_entries
= mc_config_get_int (mc_main_config
, CONFIG_APP_SECTION
,
1357 "filepos_max_saved_entries", 1024);
1359 fn
= g_build_filename (mc_config_get_cache_path (), MC_FILEPOS_FILE
, NULL
);
1363 mc_util_make_backup_if_possible (fn
, TMP_SUFFIX
);
1366 f
= fopen (fn
, "w");
1368 goto open_target_error
;
1370 tmp_fn
= g_strdup_printf ("%s" TMP_SUFFIX
, fn
);
1371 tmp_f
= fopen (tmp_fn
, "r");
1375 goto open_source_error
;
1378 /* put the new record */
1379 if (line
!= 1 || column
!= 0 || bookmarks
!= NULL
)
1381 if (fprintf (f
, "%s %ld;%ld;%" PRIuMAX
, filename
, line
, column
, (uintmax_t) offset
) < 0)
1382 goto write_position_error
;
1383 if (bookmarks
!= NULL
)
1384 for (i
= 0; i
< bookmarks
->len
&& i
< MAX_SAVED_BOOKMARKS
; i
++)
1385 if (fprintf (f
, ";%zu", g_array_index (bookmarks
, size_t, i
)) < 0)
1386 goto write_position_error
;
1388 if (fprintf (f
, "\n") < 0)
1389 goto write_position_error
;
1393 while (fgets (buf
, sizeof (buf
), tmp_f
) != NULL
)
1395 if (buf
[len
] == ' ' && strncmp (buf
, filename
, len
) == 0
1396 && strchr (&buf
[len
+ 1], ' ') == NULL
)
1399 fprintf (f
, "%s", buf
);
1400 if (++i
> filepos_max_saved_entries
)
1404 write_position_error
:
1410 mc_util_restore_from_backup_if_possible (fn
, TMP_SUFFIX
);
1412 mc_util_unlink_backup_if_possible (fn
, TMP_SUFFIX
);
1416 if (bookmarks
!= NULL
)
1417 g_array_free (bookmarks
, TRUE
);
1420 /* --------------------------------------------------------------------------------------------- */
1423 ascii_alpha_to_cntrl (int ch
)
1425 if ((ch
>= ASCII_A
&& ch
<= ASCII_Z
) || (ch
>= ASCII_a
&& ch
<= ASCII_z
))
1432 /* --------------------------------------------------------------------------------------------- */
1437 const char *result
, *sep
;
1440 sep
= strchr (result
, '|');
1441 return (sep
!= NULL
) ? sep
+ 1 : result
;
1444 /* --------------------------------------------------------------------------------------------- */
1447 mc_util_make_backup_if_possible (const char *file_name
, const char *backup_suffix
)
1449 struct stat stat_buf
;
1452 if (!exist_file (file_name
))
1455 backup_path
= g_strdup_printf ("%s%s", file_name
, backup_suffix
);
1457 if (backup_path
== NULL
)
1460 ret
= mc_util_write_backup_content (file_name
, backup_path
);
1464 /* Backup file will have same ownership with main file. */
1465 if (stat (file_name
, &stat_buf
) == 0)
1466 chmod (backup_path
, stat_buf
.st_mode
);
1468 chmod (backup_path
, S_IRUSR
| S_IWUSR
);
1471 g_free (backup_path
);
1476 /* --------------------------------------------------------------------------------------------- */
1479 mc_util_restore_from_backup_if_possible (const char *file_name
, const char *backup_suffix
)
1484 backup_path
= g_strdup_printf ("%s%s", file_name
, backup_suffix
);
1485 if (backup_path
== NULL
)
1488 ret
= mc_util_write_backup_content (backup_path
, file_name
);
1489 g_free (backup_path
);
1494 /* --------------------------------------------------------------------------------------------- */
1497 mc_util_unlink_backup_if_possible (const char *file_name
, const char *backup_suffix
)
1501 backup_path
= g_strdup_printf ("%s%s", file_name
, backup_suffix
);
1502 if (backup_path
== NULL
)
1505 if (exist_file (backup_path
))
1506 mc_unlink (backup_path
);
1508 g_free (backup_path
);
1512 /* --------------------------------------------------------------------------------------------- */
1514 * partly taken from dcigettext.c, returns "" for default locale
1515 * value should be freed by calling function g_free()
1519 guess_message_value (void)
1521 static const char *const var
[] = {
1522 /* Setting of LC_ALL overwrites all other. */
1523 /* Do not use LANGUAGE for check user locale and drowing hints */
1525 /* Next comes the name of the desired category. */
1527 /* Last possibility is the LANG environment variable. */
1529 /* NULL exit loops */
1534 const char *locale
= NULL
;
1536 while (var
[i
] != NULL
)
1538 locale
= getenv (var
[i
]);
1539 if (locale
!= NULL
&& locale
[0] != '\0')
1547 return g_strdup (locale
);
1550 /* --------------------------------------------------------------------------------------------- */