Update translations from Transifex
[midnight-commander.git] / lib / util.c
blob6b4b5c8f1ab128d4591d09b4b95a62cb2f0dfb4e
1 /*
2 Various utilities
4 Copyright (C) 1994-2019
5 Free Software Foundation, Inc.
7 Written by:
8 Miguel de Icaza, 1994, 1995, 1996
9 Janne Kukonlehto, 1994, 1995, 1996
10 Dugan Porter, 1994, 1995, 1996
11 Jakub Jelinek, 1994, 1995, 1996
12 Mauricio Plaza, 1994, 1995, 1996
13 Slava Zanko <slavazanko@gmail.com>, 2013
15 This file is part of the Midnight Commander.
17 The Midnight Commander is free software: you can redistribute it
18 and/or modify it under the terms of the GNU General Public License as
19 published by the Free Software Foundation, either version 3 of the License,
20 or (at your option) any later version.
22 The Midnight Commander is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 GNU General Public License for more details.
27 You should have received a copy of the GNU General Public License
28 along with this program. If not, see <http://www.gnu.org/licenses/>.
31 /** \file lib/util.c
32 * \brief Source: various utilities
35 #include <config.h>
37 #include <ctype.h>
38 #include <limits.h>
39 #include <stdarg.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <sys/time.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <unistd.h>
48 #include "lib/global.h"
49 #include "lib/mcconfig.h"
50 #include "lib/fileloc.h"
51 #include "lib/vfs/vfs.h"
52 #include "lib/strutil.h"
53 #include "lib/util.h"
54 #include "lib/timer.h"
56 /*** global variables ****************************************************************************/
58 /*** file scope macro definitions ****************************************************************/
60 #define ismode(n,m) ((n & m) == m)
62 /* Number of attempts to create a temporary file */
63 #ifndef TMP_MAX
64 #define TMP_MAX 16384
65 #endif /* !TMP_MAX */
67 #define TMP_SUFFIX ".tmp"
69 #define ASCII_A (0x40 + 1)
70 #define ASCII_Z (0x40 + 26)
71 #define ASCII_a (0x60 + 1)
72 #define ASCII_z (0x60 + 26)
74 /*** file scope type declarations ****************************************************************/
76 /*** file scope variables ************************************************************************/
78 /* --------------------------------------------------------------------------------------------- */
79 /*** file scope functions ************************************************************************/
80 /* --------------------------------------------------------------------------------------------- */
82 #ifndef HAVE_CHARSET
83 static inline int
84 is_7bit_printable (unsigned char c)
86 return (c > 31 && c < 127);
88 #endif
90 /* --------------------------------------------------------------------------------------------- */
92 static inline int
93 is_iso_printable (unsigned char c)
95 return ((c > 31 && c < 127) || c >= 160);
98 /* --------------------------------------------------------------------------------------------- */
100 static inline int
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 /* --------------------------------------------------------------------------------------------- */
112 static char *
113 resolve_symlinks (const vfs_path_t * vpath)
115 char *p, *p2;
116 char *buf, *buf2, *q, *r, c;
117 struct stat mybuf;
119 if (vpath->relative)
120 return NULL;
122 p = p2 = g_strdup (vfs_path_as_str (vpath));
123 r = buf = g_malloc (MC_MAXPATHLEN);
124 buf2 = g_malloc (MC_MAXPATHLEN);
125 *r++ = PATH_SEP;
126 *r = '\0';
130 q = strchr (p + 1, PATH_SEP);
131 if (q == NULL)
133 q = strchr (p + 1, '\0');
134 if (q == p + 1)
135 break;
137 c = *q;
138 *q = '\0';
139 if (mc_lstat (vpath, &mybuf) < 0)
141 MC_PTR_FREE (buf);
142 goto ret;
144 if (!S_ISLNK (mybuf.st_mode))
145 strcpy (r, p + 1);
146 else
148 int len;
150 len = mc_readlink (vpath, buf2, MC_MAXPATHLEN - 1);
151 if (len < 0)
153 MC_PTR_FREE (buf);
154 goto ret;
156 buf2[len] = '\0';
157 if (IS_PATH_SEP (*buf2))
158 strcpy (buf, buf2);
159 else
160 strcpy (r, buf2);
162 canonicalize_pathname (buf);
163 r = strchr (buf, '\0');
164 if (*r == '\0' || !IS_PATH_SEP (r[-1]))
165 /* FIXME: this condition is always true because r points to the EOL */
167 *r++ = PATH_SEP;
168 *r = '\0';
170 *q = c;
171 p = q;
173 while (c != '\0');
175 if (*buf == '\0')
176 strcpy (buf, PATH_SEP_STR);
177 else if (IS_PATH_SEP (r[-1]) && r != buf + 1)
178 r[-1] = '\0';
180 ret:
181 g_free (buf2);
182 g_free (p2);
183 return buf;
186 /* --------------------------------------------------------------------------------------------- */
188 static gboolean
189 mc_util_write_backup_content (const char *from_file_name, const char *to_file_name)
191 FILE *backup_fd;
192 char *contents;
193 gsize length;
194 gboolean ret1 = TRUE;
196 if (!g_file_get_contents (from_file_name, &contents, &length, NULL))
197 return FALSE;
199 backup_fd = fopen (to_file_name, "w");
200 if (backup_fd == NULL)
202 g_free (contents);
203 return FALSE;
206 if (fwrite ((const void *) contents, 1, length, backup_fd) != length)
207 ret1 = FALSE;
210 int ret2;
212 /* cppcheck-suppress redundantAssignment */
213 ret2 = fflush (backup_fd);
214 /* cppcheck-suppress redundantAssignment */
215 ret2 = fclose (backup_fd);
216 (void) ret2;
219 g_free (contents);
220 return ret1;
223 /* --------------------------------------------------------------------------------------------- */
224 /*** public functions ****************************************************************************/
225 /* --------------------------------------------------------------------------------------------- */
228 is_printable (int c)
230 c &= 0xff;
232 #ifdef HAVE_CHARSET
233 /* "Display bits" is ignored, since the user controls the output
234 by setting the output codepage */
235 return is_8bit_printable (c);
236 #else
237 if (!mc_global.eight_bit_clean)
238 return is_7bit_printable (c);
240 if (mc_global.full_eight_bits)
241 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 TRUE, replace "%" with "%%" - the percent is
251 * processed by the mc command line.
253 char *
254 name_quote (const char *s, gboolean quote_percent)
256 GString *ret;
258 ret = g_string_sized_new (64);
260 if (*s == '-')
261 g_string_append (ret, "." PATH_SEP_STR);
263 for (; *s != '\0'; s++)
265 switch (*s)
267 case '%':
268 if (quote_percent)
269 g_string_append_c (ret, '%');
270 break;
271 case '\'':
272 case '\\':
273 case '\r':
274 case '\n':
275 case '\t':
276 case '"':
277 case ';':
278 case ' ':
279 case '?':
280 case '|':
281 case '[':
282 case ']':
283 case '{':
284 case '}':
285 case '<':
286 case '>':
287 case '`':
288 case '!':
289 case '$':
290 case '&':
291 case '*':
292 case '(':
293 case ')':
294 g_string_append_c (ret, '\\');
295 break;
296 case '~':
297 case '#':
298 if (ret->len == 0)
299 g_string_append_c (ret, '\\');
300 break;
301 default:
302 break;
304 g_string_append_c (ret, *s);
307 return g_string_free (ret, FALSE);
310 /* --------------------------------------------------------------------------------------------- */
312 char *
313 fake_name_quote (const char *s, gboolean quote_percent)
315 (void) quote_percent;
316 return g_strdup (s);
319 /* --------------------------------------------------------------------------------------------- */
321 * path_trunc() is the same as str_trunc() but
322 * it deletes possible password from path for security
323 * reasons.
326 const char *
327 path_trunc (const char *path, size_t trunc_len)
329 vfs_path_t *vpath;
330 char *secure_path;
331 const char *ret;
333 vpath = vfs_path_from_str (path);
334 secure_path = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
335 vfs_path_free (vpath);
337 ret = str_trunc (secure_path, trunc_len);
338 g_free (secure_path);
340 return ret;
343 /* --------------------------------------------------------------------------------------------- */
345 const char *
346 size_trunc (uintmax_t size, gboolean use_si)
348 static char x[BUF_TINY];
349 uintmax_t divisor = 1;
350 const char *xtra = _("B");
352 if (size > 999999999UL)
354 divisor = use_si ? 1000 : 1024;
355 xtra = use_si ? _("kB") : _("KiB");
357 if (size / divisor > 999999999UL)
359 divisor = use_si ? (1000 * 1000) : (1024 * 1024);
360 xtra = use_si ? _("MB") : _("MiB");
362 if (size / divisor > 999999999UL)
364 divisor = use_si ? (1000 * 1000 * 1000) : (1024 * 1024 * 1024);
365 xtra = use_si ? _("GB") : _("GiB");
369 g_snprintf (x, sizeof (x), "%.0f %s", 1.0 * size / divisor, xtra);
370 return x;
373 /* --------------------------------------------------------------------------------------------- */
375 const char *
376 size_trunc_sep (uintmax_t size, gboolean use_si)
378 static char x[60];
379 int count;
380 const char *p, *y;
381 char *d;
383 p = y = size_trunc (size, use_si);
384 p += strlen (p) - 1;
385 d = x + sizeof (x) - 1;
386 *d-- = '\0';
387 /* @size format is "size unit", i.e. "[digits][space][letters]".
388 Copy all charactes after digits. */
389 while (p >= y && !g_ascii_isdigit (*p))
390 *d-- = *p--;
391 for (count = 0; p >= y; count++)
393 if (count == 3)
395 *d-- = ',';
396 count = 0;
398 *d-- = *p--;
400 d++;
401 if (*d == ',')
402 d++;
403 return d;
406 /* --------------------------------------------------------------------------------------------- */
408 * Print file SIZE to BUFFER, but don't exceed LEN characters,
409 * not including trailing 0. BUFFER should be at least LEN+1 long.
410 * This function is called for every file on panels, so avoid
411 * floating point by any means.
413 * Units: size units (filesystem sizes are 1K blocks)
414 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
417 void
418 size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gboolean use_si)
420 /* Avoid taking power for every file. */
421 /* *INDENT-OFF* */
422 static const uintmax_t power10[] = {
423 /* we hope that size of uintmax_t is 4 bytes at least */
424 1ULL,
425 10ULL,
426 100ULL,
427 1000ULL,
428 10000ULL,
429 100000ULL,
430 1000000ULL,
431 10000000ULL,
432 100000000ULL,
433 1000000000ULL
434 /* maximum value of uintmax_t (in case of 4 bytes) is
435 4294967295
437 #if SIZEOF_UINTMAX_T == 8
439 10000000000ULL,
440 100000000000ULL,
441 1000000000000ULL,
442 10000000000000ULL,
443 100000000000000ULL,
444 1000000000000000ULL,
445 10000000000000000ULL,
446 100000000000000000ULL,
447 1000000000000000000ULL,
448 10000000000000000000ULL
449 /* maximum value of uintmax_t (in case of 8 bytes) is
450 18447644073710439615
452 #endif
454 /* *INDENT-ON* */
455 static const char *const suffix[] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL };
456 static const char *const suffix_lc[] = { "", "k", "m", "g", "t", "p", "e", "z", "y", NULL };
458 const char *const *sfx = use_si ? suffix_lc : suffix;
459 int j = 0;
461 if (len == 0)
462 len = 9;
463 #if SIZEOF_UINTMAX_T == 8
464 /* 20 decimal digits are required to represent 8 bytes */
465 else if (len > 19)
466 len = 19;
467 #else
468 /* 10 decimal digits are required to represent 4 bytes */
469 else if (len > 9)
470 len = 9;
471 #endif
474 * recalculate from 1024 base to 1000 base if units>0
475 * We can't just multiply by 1024 - that might cause overflow
476 * if uintmax_t type is too small
478 if (use_si)
479 for (j = 0; j < units; j++)
481 uintmax_t size_remain;
483 size_remain = ((size % 125) * 1024) / 1000; /* size mod 125, recalculated */
484 size /= 125; /* 128/125 = 1024/1000 */
485 size *= 128; /* This will convert size from multiple of 1024 to multiple of 1000 */
486 size += size_remain; /* Re-add remainder lost by division/multiplication */
489 for (j = units; sfx[j] != NULL; j++)
491 if (size == 0)
493 if (j == units)
495 /* Empty files will print "0" even with minimal width. */
496 g_snprintf (buffer, len + 1, "%s", "0");
498 else
500 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
501 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s", (j > 1) ? sfx[j - 1] : "B");
503 break;
506 if (size < power10[len - (j > 0 ? 1 : 0)])
508 g_snprintf (buffer, len + 1, "%" PRIuMAX "%s", size, sfx[j]);
509 break;
512 /* Powers of 1000 or 1024, with rounding. */
513 if (use_si)
514 size = (size + 500) / 1000;
515 else
516 size = (size + 512) >> 10;
520 /* --------------------------------------------------------------------------------------------- */
522 const char *
523 string_perm (mode_t mode_bits)
525 static char mode[11];
527 strcpy (mode, "----------");
528 if (S_ISDIR (mode_bits))
529 mode[0] = 'd';
530 if (S_ISCHR (mode_bits))
531 mode[0] = 'c';
532 if (S_ISBLK (mode_bits))
533 mode[0] = 'b';
534 if (S_ISLNK (mode_bits))
535 mode[0] = 'l';
536 if (S_ISFIFO (mode_bits))
537 mode[0] = 'p';
538 if (S_ISNAM (mode_bits))
539 mode[0] = 'n';
540 if (S_ISSOCK (mode_bits))
541 mode[0] = 's';
542 if (S_ISDOOR (mode_bits))
543 mode[0] = 'D';
544 if (ismode (mode_bits, S_IXOTH))
545 mode[9] = 'x';
546 if (ismode (mode_bits, S_IWOTH))
547 mode[8] = 'w';
548 if (ismode (mode_bits, S_IROTH))
549 mode[7] = 'r';
550 if (ismode (mode_bits, S_IXGRP))
551 mode[6] = 'x';
552 if (ismode (mode_bits, S_IWGRP))
553 mode[5] = 'w';
554 if (ismode (mode_bits, S_IRGRP))
555 mode[4] = 'r';
556 if (ismode (mode_bits, S_IXUSR))
557 mode[3] = 'x';
558 if (ismode (mode_bits, S_IWUSR))
559 mode[2] = 'w';
560 if (ismode (mode_bits, S_IRUSR))
561 mode[1] = 'r';
562 #ifdef S_ISUID
563 if (ismode (mode_bits, S_ISUID))
564 mode[3] = (mode[3] == 'x') ? 's' : 'S';
565 #endif /* S_ISUID */
566 #ifdef S_ISGID
567 if (ismode (mode_bits, S_ISGID))
568 mode[6] = (mode[6] == 'x') ? 's' : 'S';
569 #endif /* S_ISGID */
570 #ifdef S_ISVTX
571 if (ismode (mode_bits, S_ISVTX))
572 mode[9] = (mode[9] == 'x') ? 't' : 'T';
573 #endif /* S_ISVTX */
574 return mode;
577 /* --------------------------------------------------------------------------------------------- */
579 const char *
580 extension (const char *filename)
582 const char *d;
584 d = strrchr (filename, '.');
586 return d != NULL ? d + 1 : "";
589 /* --------------------------------------------------------------------------------------------- */
591 char *
592 load_mc_home_file (const char *from, const char *filename, char **allocated_filename,
593 size_t * length)
595 char *hintfile_base, *hintfile;
596 char *lang;
597 char *data;
599 hintfile_base = g_build_filename (from, filename, (char *) NULL);
600 lang = guess_message_value ();
602 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
603 if (!g_file_get_contents (hintfile, &data, length, NULL))
605 /* Fall back to the two-letter language code */
606 if (lang[0] != '\0' && lang[1] != '\0')
607 lang[2] = '\0';
608 g_free (hintfile);
609 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
610 if (!g_file_get_contents (hintfile, &data, length, NULL))
612 g_free (hintfile);
613 hintfile = hintfile_base;
614 g_file_get_contents (hintfile_base, &data, length, NULL);
618 g_free (lang);
620 if (hintfile != hintfile_base)
621 g_free (hintfile_base);
623 if (allocated_filename != NULL)
624 *allocated_filename = hintfile;
625 else
626 g_free (hintfile);
628 return data;
631 /* --------------------------------------------------------------------------------------------- */
633 const char *
634 extract_line (const char *s, const char *top)
636 static char tmp_line[BUF_MEDIUM];
637 char *t = tmp_line;
639 while (*s != '\0' && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line) - 1 && s < top)
640 *t++ = *s++;
641 *t = '\0';
642 return tmp_line;
645 /* --------------------------------------------------------------------------------------------- */
647 * The basename routine
650 const char *
651 x_basename (const char *s)
653 const char *url_delim, *path_sep;
655 url_delim = g_strrstr (s, VFS_PATH_URL_DELIMITER);
656 path_sep = strrchr (s, PATH_SEP);
658 if (path_sep == NULL)
659 return s;
661 if (url_delim == NULL
662 || url_delim < path_sep - strlen (VFS_PATH_URL_DELIMITER)
663 || url_delim - s + strlen (VFS_PATH_URL_DELIMITER) < strlen (s))
665 /* avoid trailing PATH_SEP, if present */
666 if (!IS_PATH_SEP (s[strlen (s) - 1]))
667 return (path_sep != NULL) ? path_sep + 1 : s;
669 while (--path_sep > s && !IS_PATH_SEP (*path_sep))
671 return (path_sep != s) ? path_sep + 1 : s;
674 while (--url_delim > s && !IS_PATH_SEP (*url_delim))
676 while (--url_delim > s && !IS_PATH_SEP (*url_delim))
679 return url_delim == s ? s : url_delim + 1;
682 /* --------------------------------------------------------------------------------------------- */
684 const char *
685 unix_error_string (int error_num)
687 static char buffer[BUF_LARGE];
688 gchar *strerror_currentlocale;
690 strerror_currentlocale = g_locale_from_utf8 (g_strerror (error_num), -1, NULL, NULL, NULL);
691 g_snprintf (buffer, sizeof (buffer), "%s (%d)", strerror_currentlocale, error_num);
692 g_free (strerror_currentlocale);
694 return buffer;
697 /* --------------------------------------------------------------------------------------------- */
699 const char *
700 skip_separators (const char *s)
702 const char *su = s;
704 for (; *su != '\0'; str_cnext_char (&su))
705 if (!whitespace (*su) && *su != ',')
706 break;
708 return su;
711 /* --------------------------------------------------------------------------------------------- */
713 const char *
714 skip_numbers (const char *s)
716 const char *su = s;
718 for (; *su != '\0'; str_cnext_char (&su))
719 if (!str_isdigit (su))
720 break;
722 return su;
725 /* --------------------------------------------------------------------------------------------- */
727 * Remove all control sequences from the argument string. We define
728 * "control sequence", in a sort of pidgin BNF, as follows:
730 * control-seq = Esc non-'['
731 * | Esc '[' (0 or more digits or ';' or ':' or '?') (any other char)
733 * The 256-color and true-color escape sequences should allow either ';' or ':' inside as separator,
734 * actually, ':' is the more correct according to ECMA-48.
735 * Some terminal emulators (e.g. xterm, gnome-terminal) support this.
737 * Non-printable characters are also removed.
740 char *
741 strip_ctrl_codes (char *s)
743 char *w; /* Current position where the stripped data is written */
744 char *r; /* Current position where the original data is read */
746 if (s == NULL)
747 return NULL;
749 for (w = s, r = s; *r != '\0';)
751 if (*r == ESC_CHAR)
753 /* Skip the control sequence's arguments */ ;
754 /* '(' need to avoid strange 'B' letter in *Suse (if mc runs under root user) */
755 if (*(++r) == '[' || *r == '(')
757 /* strchr() matches trailing binary 0 */
758 while (*(++r) != '\0' && strchr ("0123456789;:?", *r) != NULL)
761 else if (*r == ']')
764 * Skip xterm's OSC (Operating System Command)
765 * http://www.xfree86.org/current/ctlseqs.html
766 * OSC P s ; P t ST
767 * OSC P s ; P t BEL
769 char *new_r;
771 for (new_r = r; *new_r != '\0'; new_r++)
773 switch (*new_r)
775 /* BEL */
776 case '\a':
777 r = new_r;
778 goto osc_out;
779 case ESC_CHAR:
780 /* ST */
781 if (new_r[1] == '\\')
783 r = new_r + 1;
784 goto osc_out;
786 default:
787 break;
790 osc_out:
795 * Now we are at the last character of the sequence.
796 * Skip it unless it's binary 0.
798 if (*r != '\0')
799 r++;
801 else
803 char *n;
805 n = str_get_next_char (r);
806 if (str_isprint (r))
808 memmove (w, r, n - r);
809 w += n - r;
811 r = n;
815 *w = '\0';
816 return s;
819 /* --------------------------------------------------------------------------------------------- */
821 enum compression_type
822 get_compression_type (int fd, const char *name)
824 unsigned char magic[16];
825 size_t str_len;
827 /* Read the magic signature */
828 if (mc_read (fd, (char *) magic, 4) != 4)
829 return COMPRESSION_NONE;
831 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
832 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236))
833 return COMPRESSION_GZIP;
835 /* PKZIP_MAGIC */
836 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003 && magic[3] == 004)
838 /* Read compression type */
839 mc_lseek (fd, 8, SEEK_SET);
840 if (mc_read (fd, (char *) magic, 2) != 2)
841 return COMPRESSION_NONE;
843 /* Gzip can handle only deflated (8) or stored (0) files */
844 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
845 return COMPRESSION_NONE;
847 /* Compatible with gzip */
848 return COMPRESSION_GZIP;
851 /* PACK_MAGIC and LZH_MAGIC and compress magic */
852 if (magic[0] == 037 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235))
853 /* Compatible with gzip */
854 return COMPRESSION_GZIP;
856 /* BZIP and BZIP2 files */
857 if ((magic[0] == 'B') && (magic[1] == 'Z') && (magic[3] >= '1') && (magic[3] <= '9'))
858 switch (magic[2])
860 case '0':
861 return COMPRESSION_BZIP;
862 case 'h':
863 return COMPRESSION_BZIP2;
864 default:
865 break;
868 /* LZ4 format - v1.5.0 - 0x184D2204 (little endian) */
869 if (magic[0] == 0x04 && magic[1] == 0x22 && magic[2] == 0x4d && magic[3] == 0x18)
870 return COMPRESSION_LZ4;
872 if (mc_read (fd, (char *) magic + 4, 2) != 2)
873 return COMPRESSION_NONE;
875 /* LZIP files */
876 if (magic[0] == 'L'
877 && magic[1] == 'Z'
878 && magic[2] == 'I' && magic[3] == 'P' && (magic[4] == 0x00 || magic[4] == 0x01))
879 return COMPRESSION_LZIP;
881 /* Support for LZMA (only utils format with magic in header).
882 * This is the default format of LZMA utils 4.32.1 and later. */
883 if (magic[0] == 0xFF
884 && magic[1] == 'L'
885 && magic[2] == 'Z' && magic[3] == 'M' && magic[4] == 'A' && magic[5] == 0x00)
886 return COMPRESSION_LZMA;
888 /* XZ compression magic */
889 if (magic[0] == 0xFD
890 && magic[1] == 0x37
891 && magic[2] == 0x7A && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00)
892 return COMPRESSION_XZ;
894 if (magic[0] == 0x28 && magic[1] == 0xB5 && magic[2] == 0x2F && magic[3] == 0xFD)
895 return COMPRESSION_ZSTD;
897 str_len = strlen (name);
898 /* HACK: we must belive to extension of LZMA file :) ... */
899 if ((str_len > 5 && strcmp (&name[str_len - 5], ".lzma") == 0) ||
900 (str_len > 4 && strcmp (&name[str_len - 4], ".tlz") == 0))
901 return COMPRESSION_LZMA;
903 return COMPRESSION_NONE;
906 /* --------------------------------------------------------------------------------------------- */
908 const char *
909 decompress_extension (int type)
911 switch (type)
913 case COMPRESSION_GZIP:
914 return "/ugz" VFS_PATH_URL_DELIMITER;
915 case COMPRESSION_BZIP:
916 return "/ubz" VFS_PATH_URL_DELIMITER;
917 case COMPRESSION_BZIP2:
918 return "/ubz2" VFS_PATH_URL_DELIMITER;
919 case COMPRESSION_LZIP:
920 return "/ulz" VFS_PATH_URL_DELIMITER;
921 case COMPRESSION_LZ4:
922 return "/ulz4" VFS_PATH_URL_DELIMITER;
923 case COMPRESSION_LZMA:
924 return "/ulzma" VFS_PATH_URL_DELIMITER;
925 case COMPRESSION_XZ:
926 return "/uxz" VFS_PATH_URL_DELIMITER;
927 case COMPRESSION_ZSTD:
928 return "/uzst" VFS_PATH_URL_DELIMITER;
929 default:
930 break;
932 /* Should never reach this place */
933 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
934 return 0;
937 /* --------------------------------------------------------------------------------------------- */
939 void
940 wipe_password (char *passwd)
942 if (passwd != NULL)
944 char *p;
946 for (p = passwd; *p != '\0'; p++)
947 *p = '\0';
948 g_free (passwd);
952 /* --------------------------------------------------------------------------------------------- */
954 * Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key
956 * @param p pointer to string
958 * @return newly allocated string
961 char *
962 convert_controls (const char *p)
964 char *valcopy;
965 char *q;
967 valcopy = g_strdup (p);
969 /* Parse the escape special character */
970 for (q = valcopy; *p != '\0';)
971 switch (*p)
973 case '\\':
974 p++;
976 if (*p == 'e' || *p == 'E')
978 p++;
979 *q++ = ESC_CHAR;
981 break;
983 case '^':
984 p++;
985 if (*p == '^')
986 *q++ = *p++;
987 else
989 char c;
991 c = *p | 0x20;
992 if (c >= 'a' && c <= 'z')
994 *q++ = c - 'a' + 1;
995 p++;
997 else if (*p != '\0')
998 p++;
1000 break;
1002 default:
1003 *q++ = *p++;
1006 *q = '\0';
1007 return valcopy;
1010 /* --------------------------------------------------------------------------------------------- */
1012 * Finds out a relative path from first to second, i.e. goes as many ..
1013 * as needed up in first and then goes down using second
1016 char *
1017 diff_two_paths (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
1019 int j, prevlen = -1, currlen;
1020 char *my_first = NULL, *my_second = NULL;
1021 char *buf = NULL;
1023 my_first = resolve_symlinks (vpath1);
1024 if (my_first == NULL)
1025 goto ret;
1027 my_second = resolve_symlinks (vpath2);
1028 if (my_second == NULL)
1029 goto ret;
1031 for (j = 0; j < 2; j++)
1033 char *p, *q;
1034 int i;
1036 p = my_first;
1037 q = my_second;
1039 while (TRUE)
1041 char *r, *s;
1042 ptrdiff_t len;
1044 r = strchr (p, PATH_SEP);
1045 if (r == NULL)
1046 break;
1047 s = strchr (q, PATH_SEP);
1048 if (s == NULL)
1049 break;
1051 len = r - p;
1052 if (len != (s - q) || strncmp (p, q, (size_t) len) != 0)
1053 break;
1055 p = r + 1;
1056 q = s + 1;
1058 p--;
1059 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++)
1061 currlen = (i + 1) * 3 + strlen (q) + 1;
1062 if (j != 0)
1064 if (currlen < prevlen)
1065 g_free (buf);
1066 else
1067 goto ret;
1069 p = buf = g_malloc (currlen);
1070 prevlen = currlen;
1071 for (; i >= 0; i--, p += 3)
1072 strcpy (p, "../");
1073 strcpy (p, q);
1076 ret:
1077 g_free (my_first);
1078 g_free (my_second);
1079 return buf;
1082 /* --------------------------------------------------------------------------------------------- */
1084 * Append text to GList, remove all entries with the same text
1087 GList *
1088 list_append_unique (GList * list, char *text)
1090 GList *lc_link;
1093 * Go to the last position and traverse the list backwards
1094 * starting from the second last entry to make sure that we
1095 * are not removing the current link.
1097 list = g_list_append (list, text);
1098 list = g_list_last (list);
1099 lc_link = g_list_previous (list);
1101 while (lc_link != NULL)
1103 GList *newlink;
1105 newlink = g_list_previous (lc_link);
1106 if (strcmp ((char *) lc_link->data, text) == 0)
1108 GList *tmp;
1110 g_free (lc_link->data);
1111 tmp = g_list_remove_link (list, lc_link);
1112 (void) tmp;
1113 g_list_free_1 (lc_link);
1115 lc_link = newlink;
1118 return list;
1121 /* --------------------------------------------------------------------------------------------- */
1123 * Read and restore position for the given filename.
1124 * If there is no stored data, return line 1 and col 0.
1127 void
1128 load_file_position (const vfs_path_t * filename_vpath, long *line, long *column, off_t * offset,
1129 GArray ** bookmarks)
1131 char *fn;
1132 FILE *f;
1133 char buf[MC_MAXPATHLEN + 100];
1134 const size_t len = vfs_path_len (filename_vpath);
1136 /* defaults */
1137 *line = 1;
1138 *column = 0;
1139 *offset = 0;
1141 /* open file with positions */
1142 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1143 f = fopen (fn, "r");
1144 g_free (fn);
1145 if (f == NULL)
1146 return;
1148 /* prepare array for serialized bookmarks */
1149 if (bookmarks != NULL)
1150 *bookmarks = g_array_sized_new (FALSE, FALSE, sizeof (size_t), MAX_SAVED_BOOKMARKS);
1152 while (fgets (buf, sizeof (buf), f) != NULL)
1154 const char *p;
1155 gchar **pos_tokens;
1157 /* check if the filename matches the beginning of string */
1158 if (strncmp (buf, vfs_path_as_str (filename_vpath), len) != 0)
1159 continue;
1161 /* followed by single space */
1162 if (buf[len] != ' ')
1163 continue;
1165 /* and string without spaces */
1166 p = &buf[len + 1];
1167 if (strchr (p, ' ') != NULL)
1168 continue;
1170 pos_tokens = g_strsplit (p, ";", 3 + MAX_SAVED_BOOKMARKS);
1171 if (pos_tokens[0] == NULL)
1173 *line = 1;
1174 *column = 0;
1175 *offset = 0;
1177 else
1179 *line = strtol (pos_tokens[0], NULL, 10);
1180 if (pos_tokens[1] == NULL)
1182 *column = 0;
1183 *offset = 0;
1185 else
1187 *column = strtol (pos_tokens[1], NULL, 10);
1188 if (pos_tokens[2] == NULL)
1189 *offset = 0;
1190 else if (bookmarks != NULL)
1192 size_t i;
1194 *offset = (off_t) g_ascii_strtoll (pos_tokens[2], NULL, 10);
1196 for (i = 0; i < MAX_SAVED_BOOKMARKS && pos_tokens[3 + i] != NULL; i++)
1198 size_t val;
1200 val = strtoul (pos_tokens[3 + i], NULL, 10);
1201 g_array_append_val (*bookmarks, val);
1207 g_strfreev (pos_tokens);
1210 fclose (f);
1213 /* --------------------------------------------------------------------------------------------- */
1215 * Save position for the given file
1218 void
1219 save_file_position (const vfs_path_t * filename_vpath, long line, long column, off_t offset,
1220 GArray * bookmarks)
1222 static size_t filepos_max_saved_entries = 0;
1223 char *fn, *tmp_fn;
1224 FILE *f, *tmp_f;
1225 char buf[MC_MAXPATHLEN + 100];
1226 size_t i;
1227 const size_t len = vfs_path_len (filename_vpath);
1228 gboolean src_error = FALSE;
1230 if (filepos_max_saved_entries == 0)
1231 filepos_max_saved_entries = mc_config_get_int (mc_global.main_config, CONFIG_APP_SECTION,
1232 "filepos_max_saved_entries", 1024);
1234 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1235 if (fn == NULL)
1236 goto early_error;
1238 mc_util_make_backup_if_possible (fn, TMP_SUFFIX);
1240 /* open file */
1241 f = fopen (fn, "w");
1242 if (f == NULL)
1243 goto open_target_error;
1245 tmp_fn = g_strdup_printf ("%s" TMP_SUFFIX, fn);
1246 tmp_f = fopen (tmp_fn, "r");
1247 if (tmp_f == NULL)
1249 src_error = TRUE;
1250 goto open_source_error;
1253 /* put the new record */
1254 if (line != 1 || column != 0 || bookmarks != NULL)
1256 if (fprintf
1257 (f, "%s %ld;%ld;%" PRIuMAX, vfs_path_as_str (filename_vpath), line, column,
1258 (uintmax_t) offset) < 0)
1259 goto write_position_error;
1260 if (bookmarks != NULL)
1261 for (i = 0; i < bookmarks->len && i < MAX_SAVED_BOOKMARKS; i++)
1262 if (fprintf (f, ";%zu", g_array_index (bookmarks, size_t, i)) < 0)
1263 goto write_position_error;
1265 if (fprintf (f, "\n") < 0)
1266 goto write_position_error;
1269 i = 1;
1270 while (fgets (buf, sizeof (buf), tmp_f) != NULL)
1272 if (buf[len] == ' ' && strncmp (buf, vfs_path_as_str (filename_vpath), len) == 0
1273 && strchr (&buf[len + 1], ' ') == NULL)
1274 continue;
1276 fprintf (f, "%s", buf);
1277 if (++i > filepos_max_saved_entries)
1278 break;
1281 write_position_error:
1282 fclose (tmp_f);
1283 open_source_error:
1284 g_free (tmp_fn);
1285 fclose (f);
1286 if (src_error)
1287 mc_util_restore_from_backup_if_possible (fn, TMP_SUFFIX);
1288 else
1289 mc_util_unlink_backup_if_possible (fn, TMP_SUFFIX);
1290 open_target_error:
1291 g_free (fn);
1292 early_error:
1293 if (bookmarks != NULL)
1294 g_array_free (bookmarks, TRUE);
1297 /* --------------------------------------------------------------------------------------------- */
1299 extern int
1300 ascii_alpha_to_cntrl (int ch)
1302 if ((ch >= ASCII_A && ch <= ASCII_Z) || (ch >= ASCII_a && ch <= ASCII_z))
1303 ch &= 0x1f;
1305 return ch;
1308 /* --------------------------------------------------------------------------------------------- */
1310 const char *
1311 Q_ (const char *s)
1313 const char *result, *sep;
1315 result = _(s);
1316 sep = strchr (result, '|');
1318 return sep != NULL ? sep + 1 : result;
1321 /* --------------------------------------------------------------------------------------------- */
1323 gboolean
1324 mc_util_make_backup_if_possible (const char *file_name, const char *backup_suffix)
1326 struct stat stat_buf;
1327 char *backup_path;
1328 gboolean ret;
1330 if (!exist_file (file_name))
1331 return FALSE;
1333 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1334 if (backup_path == NULL)
1335 return FALSE;
1337 ret = mc_util_write_backup_content (file_name, backup_path);
1338 if (ret)
1340 /* Backup file will have same ownership with main file. */
1341 if (stat (file_name, &stat_buf) == 0)
1342 chmod (backup_path, stat_buf.st_mode);
1343 else
1344 chmod (backup_path, S_IRUSR | S_IWUSR);
1347 g_free (backup_path);
1349 return ret;
1352 /* --------------------------------------------------------------------------------------------- */
1354 gboolean
1355 mc_util_restore_from_backup_if_possible (const char *file_name, const char *backup_suffix)
1357 gboolean ret;
1358 char *backup_path;
1360 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1361 if (backup_path == NULL)
1362 return FALSE;
1364 ret = mc_util_write_backup_content (backup_path, file_name);
1365 g_free (backup_path);
1367 return ret;
1370 /* --------------------------------------------------------------------------------------------- */
1372 gboolean
1373 mc_util_unlink_backup_if_possible (const char *file_name, const char *backup_suffix)
1375 char *backup_path;
1377 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1378 if (backup_path == NULL)
1379 return FALSE;
1381 if (exist_file (backup_path))
1383 vfs_path_t *vpath;
1385 vpath = vfs_path_from_str (backup_path);
1386 mc_unlink (vpath);
1387 vfs_path_free (vpath);
1390 g_free (backup_path);
1391 return TRUE;
1394 /* --------------------------------------------------------------------------------------------- */
1396 * partly taken from dcigettext.c, returns "" for default locale
1397 * value should be freed by calling function g_free()
1400 char *
1401 guess_message_value (void)
1403 static const char *const var[] = {
1404 /* Setting of LC_ALL overwrites all other. */
1405 /* Do not use LANGUAGE for check user locale and drowing hints */
1406 "LC_ALL",
1407 /* Next comes the name of the desired category. */
1408 "LC_MESSAGES",
1409 /* Last possibility is the LANG environment variable. */
1410 "LANG",
1411 /* NULL exit loops */
1412 NULL
1415 size_t i;
1416 const char *locale = NULL;
1418 for (i = 0; var[i] != NULL; i++)
1420 locale = getenv (var[i]);
1421 if (locale != NULL && locale[0] != '\0')
1422 break;
1425 if (locale == NULL)
1426 locale = "";
1428 return g_strdup (locale);
1431 /* --------------------------------------------------------------------------------------------- */
1434 * The "profile root" is the tree under which all of MC's user data &
1435 * settings are stored.
1437 * It defaults to the user's home dir. The user may override this default
1438 * with the environment variable $MC_PROFILE_ROOT.
1440 const char *
1441 mc_get_profile_root (void)
1443 static const char *profile_root = NULL;
1445 if (profile_root == NULL)
1447 profile_root = g_getenv ("MC_PROFILE_ROOT");
1448 if (profile_root == NULL || *profile_root == '\0')
1449 profile_root = mc_config_get_home_dir ();
1452 return profile_root;
1455 /* --------------------------------------------------------------------------------------------- */
1457 * Propagate error in simple way.
1459 * @param dest error return location
1460 * @param code error code
1461 * @param format printf()-style format for error message
1462 * @param ... parameters for message format
1465 void
1466 mc_propagate_error (GError ** dest, int code, const char *format, ...)
1468 if (dest != NULL && *dest == NULL)
1470 GError *tmp_error;
1471 va_list args;
1473 va_start (args, format);
1474 tmp_error = g_error_new_valist (MC_ERROR, code, format, args);
1475 va_end (args);
1477 g_propagate_error (dest, tmp_error);
1481 /* --------------------------------------------------------------------------------------------- */
1483 * Replace existing error in simple way.
1485 * @param dest error return location
1486 * @param code error code
1487 * @param format printf()-style format for error message
1488 * @param ... parameters for message format
1491 void
1492 mc_replace_error (GError ** dest, int code, const char *format, ...)
1494 if (dest != NULL)
1496 GError *tmp_error;
1497 va_list args;
1499 va_start (args, format);
1500 tmp_error = g_error_new_valist (MC_ERROR, code, format, args);
1501 va_end (args);
1503 g_error_free (*dest);
1504 *dest = NULL;
1505 g_propagate_error (dest, tmp_error);
1509 /* --------------------------------------------------------------------------------------------- */
1512 * Returns if the given duration has elapsed since the given timestamp,
1513 * and if it has then updates the timestamp.
1515 * @param timestamp the last timestamp in microseconds, updated if the given time elapsed
1516 * @param deleay amount of time in microseconds
1518 * @return TRUE if clock skew detected, FALSE otherwise
1520 gboolean
1521 mc_time_elapsed (guint64 * timestamp, guint64 delay)
1523 guint64 now;
1525 now = mc_timer_elapsed (mc_global.timer);
1527 if (now >= *timestamp && now < *timestamp + delay)
1528 return FALSE;
1530 *timestamp = now;
1531 return TRUE;
1534 /* --------------------------------------------------------------------------------------------- */