Ticket 3948: can't create network link from panel...
[midnight-commander.git] / lib / util.c
blobaaa8c5f5e4671079a2c9820175465d7a5df57768
1 /*
2 Various utilities
4 Copyright (C) 1994-2018
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 /*** file scope functions ************************************************************************/
79 /* --------------------------------------------------------------------------------------------- */
81 #ifndef HAVE_CHARSET
82 static inline int
83 is_7bit_printable (unsigned char c)
85 return (c > 31 && c < 127);
87 #endif
89 /* --------------------------------------------------------------------------------------------- */
91 static inline int
92 is_iso_printable (unsigned char c)
94 return ((c > 31 && c < 127) || c >= 160);
97 /* --------------------------------------------------------------------------------------------- */
99 static inline int
100 is_8bit_printable (unsigned char c)
102 /* "Full 8 bits output" doesn't work on xterm */
103 if (mc_global.tty.xterm_flag)
104 return is_iso_printable (c);
106 return (c > 31 && c != 127 && c != 155);
109 /* --------------------------------------------------------------------------------------------- */
111 static char *
112 resolve_symlinks (const vfs_path_t * vpath)
114 char *p, *p2;
115 char *buf, *buf2, *q, *r, c;
116 struct stat mybuf;
118 if (vpath->relative)
119 return NULL;
121 p = p2 = g_strdup (vfs_path_as_str (vpath));
122 r = buf = g_malloc (MC_MAXPATHLEN);
123 buf2 = g_malloc (MC_MAXPATHLEN);
124 *r++ = PATH_SEP;
125 *r = 0;
129 q = strchr (p + 1, PATH_SEP);
130 if (!q)
132 q = strchr (p + 1, 0);
133 if (q == p + 1)
134 break;
136 c = *q;
137 *q = 0;
138 if (mc_lstat (vpath, &mybuf) < 0)
140 MC_PTR_FREE (buf);
141 goto ret;
143 if (!S_ISLNK (mybuf.st_mode))
144 strcpy (r, p + 1);
145 else
147 int len;
149 len = mc_readlink (vpath, buf2, MC_MAXPATHLEN - 1);
150 if (len < 0)
152 MC_PTR_FREE (buf);
153 goto ret;
155 buf2[len] = 0;
156 if (IS_PATH_SEP (*buf2))
157 strcpy (buf, buf2);
158 else
159 strcpy (r, buf2);
161 canonicalize_pathname (buf);
162 r = strchr (buf, 0);
163 if (*r == '\0' || !IS_PATH_SEP (r[-1]))
164 /* FIXME: this condition is always true because r points to the EOL */
166 *r++ = PATH_SEP;
167 *r = '\0';
169 *q = c;
170 p = q;
172 while (c != '\0');
174 if (*buf == '\0')
175 strcpy (buf, PATH_SEP_STR);
176 else if (IS_PATH_SEP (r[-1]) && r != buf + 1)
177 r[-1] = '\0';
179 ret:
180 g_free (buf2);
181 g_free (p2);
182 return buf;
185 /* --------------------------------------------------------------------------------------------- */
187 static gboolean
188 mc_util_write_backup_content (const char *from_file_name, const char *to_file_name)
190 FILE *backup_fd;
191 char *contents;
192 gsize length;
193 gboolean ret1 = TRUE;
195 if (!g_file_get_contents (from_file_name, &contents, &length, NULL))
196 return FALSE;
198 backup_fd = fopen (to_file_name, "w");
199 if (backup_fd == NULL)
201 g_free (contents);
202 return FALSE;
205 if (fwrite ((const void *) contents, 1, length, backup_fd) != length)
206 ret1 = FALSE;
208 int ret2;
210 /* cppcheck-suppress redundantAssignment */
211 ret2 = fflush (backup_fd);
212 /* cppcheck-suppress redundantAssignment */
213 ret2 = fclose (backup_fd);
214 (void) ret2;
216 g_free (contents);
217 return ret1;
220 /* --------------------------------------------------------------------------------------------- */
221 /*** public functions ****************************************************************************/
222 /* --------------------------------------------------------------------------------------------- */
225 is_printable (int c)
227 c &= 0xff;
229 #ifdef HAVE_CHARSET
230 /* "Display bits" is ignored, since the user controls the output
231 by setting the output codepage */
232 return is_8bit_printable (c);
233 #else
234 if (!mc_global.eight_bit_clean)
235 return is_7bit_printable (c);
237 if (mc_global.full_eight_bits)
239 return is_8bit_printable (c);
241 else
242 return is_iso_printable (c);
243 #endif /* !HAVE_CHARSET */
246 /* --------------------------------------------------------------------------------------------- */
248 * Quote the filename for the purpose of inserting it into the command
249 * line. If quote_percent is TRUE, replace "%" with "%%" - the percent is
250 * processed by the mc command line.
252 char *
253 name_quote (const char *s, gboolean quote_percent)
255 GString *ret;
257 ret = g_string_sized_new (64);
259 if (*s == '-')
260 g_string_append (ret, "." PATH_SEP_STR);
262 for (; *s != '\0'; s++)
264 switch (*s)
266 case '%':
267 if (quote_percent)
268 g_string_append_c (ret, '%');
269 break;
270 case '\'':
271 case '\\':
272 case '\r':
273 case '\n':
274 case '\t':
275 case '"':
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 g_string_append_c (ret, '\\');
294 break;
295 case '~':
296 case '#':
297 if (ret->len == 0)
298 g_string_append_c (ret, '\\');
299 break;
300 default:
301 break;
303 g_string_append_c (ret, *s);
306 return g_string_free (ret, FALSE);
309 /* --------------------------------------------------------------------------------------------- */
311 char *
312 fake_name_quote (const char *s, gboolean quote_percent)
314 (void) quote_percent;
315 return g_strdup (s);
318 /* --------------------------------------------------------------------------------------------- */
320 * path_trunc() is the same as str_trunc() but
321 * it deletes possible password from path for security
322 * reasons.
325 const char *
326 path_trunc (const char *path, size_t trunc_len)
328 vfs_path_t *vpath;
329 char *secure_path;
330 const char *ret;
332 vpath = vfs_path_from_str (path);
333 secure_path = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
334 vfs_path_free (vpath);
336 ret = str_trunc (secure_path, trunc_len);
337 g_free (secure_path);
339 return ret;
342 /* --------------------------------------------------------------------------------------------- */
344 const char *
345 size_trunc (uintmax_t size, gboolean use_si)
347 static char x[BUF_TINY];
348 uintmax_t divisor = 1;
349 const char *xtra = _("B");
351 if (size > 999999999UL)
353 divisor = use_si ? 1000 : 1024;
354 xtra = use_si ? _("kB") : _("KiB");
356 if (size / divisor > 999999999UL)
358 divisor = use_si ? (1000 * 1000) : (1024 * 1024);
359 xtra = use_si ? _("MB") : _("MiB");
361 if (size / divisor > 999999999UL)
363 divisor = use_si ? (1000 * 1000 * 1000) : (1024 * 1024 * 1024);
364 xtra = use_si ? _("GB") : _("GiB");
368 g_snprintf (x, sizeof (x), "%.0f %s", 1.0 * size / divisor, xtra);
369 return x;
372 /* --------------------------------------------------------------------------------------------- */
374 const char *
375 size_trunc_sep (uintmax_t size, gboolean use_si)
377 static char x[60];
378 int count;
379 const char *p, *y;
380 char *d;
382 p = y = size_trunc (size, use_si);
383 p += strlen (p) - 1;
384 d = x + sizeof (x) - 1;
385 *d-- = '\0';
386 /* @size format is "size unit", i.e. "[digits][space][letters]".
387 Copy all charactes after digits. */
388 while (p >= y && !g_ascii_isdigit (*p))
389 *d-- = *p--;
390 for (count = 0; p >= y; count++)
392 if (count == 3)
394 *d-- = ',';
395 count = 0;
397 *d-- = *p--;
399 d++;
400 if (*d == ',')
401 d++;
402 return d;
405 /* --------------------------------------------------------------------------------------------- */
407 * Print file SIZE to BUFFER, but don't exceed LEN characters,
408 * not including trailing 0. BUFFER should be at least LEN+1 long.
409 * This function is called for every file on panels, so avoid
410 * floating point by any means.
412 * Units: size units (filesystem sizes are 1K blocks)
413 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
416 void
417 size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gboolean use_si)
419 /* Avoid taking power for every file. */
420 /* *INDENT-OFF* */
421 static const uintmax_t power10[] = {
422 /* we hope that size of uintmax_t is 4 bytes at least */
423 1ULL,
424 10ULL,
425 100ULL,
426 1000ULL,
427 10000ULL,
428 100000ULL,
429 1000000ULL,
430 10000000ULL,
431 100000000ULL,
432 1000000000ULL
433 /* maximum value of uintmax_t (in case of 4 bytes) is
434 4294967295
436 #if SIZEOF_UINTMAX_T == 8
438 10000000000ULL,
439 100000000000ULL,
440 1000000000000ULL,
441 10000000000000ULL,
442 100000000000000ULL,
443 1000000000000000ULL,
444 10000000000000000ULL,
445 100000000000000000ULL,
446 1000000000000000000ULL,
447 10000000000000000000ULL
448 /* maximum value of uintmax_t (in case of 8 bytes) is
449 18447644073710439615
451 #endif
453 /* *INDENT-ON* */
454 static const char *const suffix[] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL };
455 static const char *const suffix_lc[] = { "", "k", "m", "g", "t", "p", "e", "z", "y", NULL };
457 const char *const *sfx = use_si ? suffix_lc : suffix;
458 int j = 0;
460 if (len == 0)
461 len = 9;
462 #if SIZEOF_UINTMAX_T == 8
463 /* 20 decimal digits are required to represent 8 bytes */
464 else if (len > 19)
465 len = 19;
466 #else
467 /* 10 decimal digits are required to represent 4 bytes */
468 else if (len > 9)
469 len = 9;
470 #endif
473 * recalculate from 1024 base to 1000 base if units>0
474 * We can't just multiply by 1024 - that might cause overflow
475 * if uintmax_t type is too small
477 if (use_si)
478 for (j = 0; j < units; j++)
480 uintmax_t size_remain;
482 size_remain = ((size % 125) * 1024) / 1000; /* size mod 125, recalculated */
483 size /= 125; /* 128/125 = 1024/1000 */
484 size *= 128; /* This will convert size from multiple of 1024 to multiple of 1000 */
485 size += size_remain; /* Re-add remainder lost by division/multiplication */
488 for (j = units; sfx[j] != NULL; j++)
490 if (size == 0)
492 if (j == units)
494 /* Empty files will print "0" even with minimal width. */
495 g_snprintf (buffer, len + 1, "%s", "0");
497 else
499 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
500 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s", (j > 1) ? sfx[j - 1] : "B");
502 break;
505 if (size < power10[len - (j > 0 ? 1 : 0)])
507 g_snprintf (buffer, len + 1, "%" PRIuMAX "%s", size, sfx[j]);
508 break;
511 /* Powers of 1000 or 1024, with rounding. */
512 if (use_si)
513 size = (size + 500) / 1000;
514 else
515 size = (size + 512) >> 10;
519 /* --------------------------------------------------------------------------------------------- */
521 const char *
522 string_perm (mode_t mode_bits)
524 static char mode[11];
526 strcpy (mode, "----------");
527 if (S_ISDIR (mode_bits))
528 mode[0] = 'd';
529 if (S_ISCHR (mode_bits))
530 mode[0] = 'c';
531 if (S_ISBLK (mode_bits))
532 mode[0] = 'b';
533 if (S_ISLNK (mode_bits))
534 mode[0] = 'l';
535 if (S_ISFIFO (mode_bits))
536 mode[0] = 'p';
537 if (S_ISNAM (mode_bits))
538 mode[0] = 'n';
539 if (S_ISSOCK (mode_bits))
540 mode[0] = 's';
541 if (S_ISDOOR (mode_bits))
542 mode[0] = 'D';
543 if (ismode (mode_bits, S_IXOTH))
544 mode[9] = 'x';
545 if (ismode (mode_bits, S_IWOTH))
546 mode[8] = 'w';
547 if (ismode (mode_bits, S_IROTH))
548 mode[7] = 'r';
549 if (ismode (mode_bits, S_IXGRP))
550 mode[6] = 'x';
551 if (ismode (mode_bits, S_IWGRP))
552 mode[5] = 'w';
553 if (ismode (mode_bits, S_IRGRP))
554 mode[4] = 'r';
555 if (ismode (mode_bits, S_IXUSR))
556 mode[3] = 'x';
557 if (ismode (mode_bits, S_IWUSR))
558 mode[2] = 'w';
559 if (ismode (mode_bits, S_IRUSR))
560 mode[1] = 'r';
561 #ifdef S_ISUID
562 if (ismode (mode_bits, S_ISUID))
563 mode[3] = (mode[3] == 'x') ? 's' : 'S';
564 #endif /* S_ISUID */
565 #ifdef S_ISGID
566 if (ismode (mode_bits, S_ISGID))
567 mode[6] = (mode[6] == 'x') ? 's' : 'S';
568 #endif /* S_ISGID */
569 #ifdef S_ISVTX
570 if (ismode (mode_bits, S_ISVTX))
571 mode[9] = (mode[9] == 'x') ? 't' : 'T';
572 #endif /* S_ISVTX */
573 return mode;
576 /* --------------------------------------------------------------------------------------------- */
578 const char *
579 extension (const char *filename)
581 const char *d = strrchr (filename, '.');
582 return (d != NULL) ? d + 1 : "";
585 /* --------------------------------------------------------------------------------------------- */
587 char *
588 load_mc_home_file (const char *from, const char *filename, char **allocated_filename,
589 size_t * length)
591 char *hintfile_base, *hintfile;
592 char *lang;
593 char *data;
595 hintfile_base = g_build_filename (from, filename, (char *) NULL);
596 lang = guess_message_value ();
598 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
599 if (!g_file_get_contents (hintfile, &data, length, NULL))
601 /* Fall back to the two-letter language code */
602 if (lang[0] != '\0' && lang[1] != '\0')
603 lang[2] = '\0';
604 g_free (hintfile);
605 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
606 if (!g_file_get_contents (hintfile, &data, length, NULL))
608 g_free (hintfile);
609 hintfile = hintfile_base;
610 g_file_get_contents (hintfile_base, &data, length, NULL);
614 g_free (lang);
616 if (hintfile != hintfile_base)
617 g_free (hintfile_base);
619 if (allocated_filename != NULL)
620 *allocated_filename = hintfile;
621 else
622 g_free (hintfile);
624 return data;
627 /* --------------------------------------------------------------------------------------------- */
629 const char *
630 extract_line (const char *s, const char *top)
632 static char tmp_line[BUF_MEDIUM];
633 char *t = tmp_line;
635 while (*s && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line) - 1 && s < top)
636 *t++ = *s++;
637 *t = 0;
638 return tmp_line;
641 /* --------------------------------------------------------------------------------------------- */
643 * The basename routine
646 const char *
647 x_basename (const char *s)
649 const char *url_delim, *path_sep;
651 url_delim = g_strrstr (s, VFS_PATH_URL_DELIMITER);
652 path_sep = strrchr (s, PATH_SEP);
654 if (path_sep == NULL)
655 return s;
657 if (url_delim == NULL
658 || url_delim < path_sep - strlen (VFS_PATH_URL_DELIMITER)
659 || url_delim - s + strlen (VFS_PATH_URL_DELIMITER) < strlen (s))
661 /* avoid trailing PATH_SEP, if present */
662 if (!IS_PATH_SEP (s[strlen (s) - 1]))
663 return (path_sep != NULL) ? path_sep + 1 : s;
665 while (--path_sep > s && !IS_PATH_SEP (*path_sep))
667 return (path_sep != s) ? path_sep + 1 : s;
670 while (--url_delim > s && !IS_PATH_SEP (*url_delim))
672 while (--url_delim > s && !IS_PATH_SEP (*url_delim))
675 return (url_delim == s) ? s : url_delim + 1;
678 /* --------------------------------------------------------------------------------------------- */
680 const char *
681 unix_error_string (int error_num)
683 static char buffer[BUF_LARGE];
684 gchar *strerror_currentlocale;
686 strerror_currentlocale = g_locale_from_utf8 (g_strerror (error_num), -1, NULL, NULL, NULL);
687 g_snprintf (buffer, sizeof (buffer), "%s (%d)", strerror_currentlocale, error_num);
688 g_free (strerror_currentlocale);
690 return buffer;
693 /* --------------------------------------------------------------------------------------------- */
695 const char *
696 skip_separators (const char *s)
698 const char *su = s;
700 for (; *su != '\0'; str_cnext_char (&su))
701 if (!whitespace (*su) && *su != ',')
702 break;
704 return su;
707 /* --------------------------------------------------------------------------------------------- */
709 const char *
710 skip_numbers (const char *s)
712 const char *su = s;
714 for (; *su != '\0'; str_cnext_char (&su))
715 if (!str_isdigit (su))
716 break;
718 return su;
721 /* --------------------------------------------------------------------------------------------- */
723 * Remove all control sequences from the argument string. We define
724 * "control sequence", in a sort of pidgin BNF, as follows:
726 * control-seq = Esc non-'['
727 * | Esc '[' (0 or more digits or ';' or ':' or '?') (any other char)
729 * The 256-color and true-color escape sequences should allow either ';' or ':' inside as separator,
730 * actually, ':' is the more correct according to ECMA-48.
731 * Some terminal emulators (e.g. xterm, gnome-terminal) support this.
733 * Non-printable characters are also removed.
736 char *
737 strip_ctrl_codes (char *s)
739 char *w; /* Current position where the stripped data is written */
740 char *r; /* Current position where the original data is read */
742 if (s == NULL)
743 return NULL;
745 for (w = s, r = s; *r != '\0';)
747 if (*r == ESC_CHAR)
749 /* Skip the control sequence's arguments */ ;
750 /* '(' need to avoid strange 'B' letter in *Suse (if mc runs under root user) */
751 if (*(++r) == '[' || *r == '(')
753 /* strchr() matches trailing binary 0 */
754 while (*(++r) != '\0' && strchr ("0123456789;:?", *r) != NULL)
757 else if (*r == ']')
760 * Skip xterm's OSC (Operating System Command)
761 * http://www.xfree86.org/current/ctlseqs.html
762 * OSC P s ; P t ST
763 * OSC P s ; P t BEL
765 char *new_r = r;
767 for (; *new_r != '\0'; ++new_r)
769 switch (*new_r)
771 /* BEL */
772 case '\a':
773 r = new_r;
774 goto osc_out;
775 case ESC_CHAR:
776 /* ST */
777 if (*(new_r + 1) == '\\')
779 r = new_r + 1;
780 goto osc_out;
782 default:
783 break;
786 osc_out:
791 * Now we are at the last character of the sequence.
792 * Skip it unless it's binary 0.
794 if (*r != '\0')
795 r++;
797 else
799 char *n;
801 n = str_get_next_char (r);
802 if (str_isprint (r))
804 memmove (w, r, n - r);
805 w += n - r;
807 r = n;
811 *w = '\0';
812 return s;
815 /* --------------------------------------------------------------------------------------------- */
817 enum compression_type
818 get_compression_type (int fd, const char *name)
820 unsigned char magic[16];
821 size_t str_len;
823 /* Read the magic signature */
824 if (mc_read (fd, (char *) magic, 4) != 4)
825 return COMPRESSION_NONE;
827 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
828 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236))
830 return COMPRESSION_GZIP;
833 /* PKZIP_MAGIC */
834 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003 && magic[3] == 004)
836 /* Read compression type */
837 mc_lseek (fd, 8, SEEK_SET);
838 if (mc_read (fd, (char *) magic, 2) != 2)
839 return COMPRESSION_NONE;
841 /* Gzip can handle only deflated (8) or stored (0) files */
842 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
843 return COMPRESSION_NONE;
845 /* Compatible with gzip */
846 return COMPRESSION_GZIP;
849 /* PACK_MAGIC and LZH_MAGIC and compress magic */
850 if (magic[0] == 037 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235))
852 /* Compatible with gzip */
853 return COMPRESSION_GZIP;
856 /* BZIP and BZIP2 files */
857 if ((magic[0] == 'B') && (magic[1] == 'Z') && (magic[3] >= '1') && (magic[3] <= '9'))
859 switch (magic[2])
861 case '0':
862 return COMPRESSION_BZIP;
863 case 'h':
864 return COMPRESSION_BZIP2;
865 default:
866 break;
870 /* LZ4 format - v1.5.0 - 0x184D2204 (little endian) */
871 if (magic[0] == 0x04 && magic[1] == 0x22 && magic[2] == 0x4d && magic[3] == 0x18)
872 return COMPRESSION_LZ4;
874 if (mc_read (fd, (char *) magic + 4, 2) != 2)
875 return COMPRESSION_NONE;
877 /* LZIP files */
878 if (magic[0] == 'L'
879 && magic[1] == 'Z'
880 && magic[2] == 'I' && magic[3] == 'P' && (magic[4] == 0x00 || magic[4] == 0x01))
881 return COMPRESSION_LZIP;
883 /* Support for LZMA (only utils format with magic in header).
884 * This is the default format of LZMA utils 4.32.1 and later. */
885 if (magic[0] == 0xFF
886 && magic[1] == 'L'
887 && magic[2] == 'Z' && magic[3] == 'M' && magic[4] == 'A' && magic[5] == 0x00)
888 return COMPRESSION_LZMA;
890 /* XZ compression magic */
891 if (magic[0] == 0xFD
892 && magic[1] == 0x37
893 && magic[2] == 0x7A && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00)
894 return COMPRESSION_XZ;
896 if (magic[0] == 0x28 && magic[1] == 0xB5 && magic[2] == 0x2F && magic[3] == 0xFD)
897 return COMPRESSION_ZSTD;
899 str_len = strlen (name);
900 /* HACK: we must belive to extension of LZMA file :) ... */
901 if ((str_len > 5 && strcmp (&name[str_len - 5], ".lzma") == 0) ||
902 (str_len > 4 && strcmp (&name[str_len - 4], ".tlz") == 0))
903 return COMPRESSION_LZMA;
905 return COMPRESSION_NONE;
908 /* --------------------------------------------------------------------------------------------- */
910 const char *
911 decompress_extension (int type)
913 switch (type)
915 case COMPRESSION_GZIP:
916 return "/ugz" VFS_PATH_URL_DELIMITER;
917 case COMPRESSION_BZIP:
918 return "/ubz" VFS_PATH_URL_DELIMITER;
919 case COMPRESSION_BZIP2:
920 return "/ubz2" VFS_PATH_URL_DELIMITER;
921 case COMPRESSION_LZIP:
922 return "/ulz" VFS_PATH_URL_DELIMITER;
923 case COMPRESSION_LZ4:
924 return "/ulz4" VFS_PATH_URL_DELIMITER;
925 case COMPRESSION_LZMA:
926 return "/ulzma" VFS_PATH_URL_DELIMITER;
927 case COMPRESSION_XZ:
928 return "/uxz" VFS_PATH_URL_DELIMITER;
929 case COMPRESSION_ZSTD:
930 return "/uzst" VFS_PATH_URL_DELIMITER;
931 default:
932 break;
934 /* Should never reach this place */
935 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
936 return 0;
939 /* --------------------------------------------------------------------------------------------- */
941 void
942 wipe_password (char *passwd)
944 char *p = passwd;
946 if (!p)
947 return;
948 for (; *p; p++)
949 *p = 0;
950 g_free (passwd);
953 /* --------------------------------------------------------------------------------------------- */
955 * Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key
957 * @param p pointer to string
959 * @return newly allocated string
962 char *
963 convert_controls (const char *p)
965 char *valcopy = g_strdup (p);
966 char *q;
968 /* Parse the escape special character */
969 for (q = valcopy; *p;)
971 if (*p == '\\')
973 p++;
974 if ((*p == 'e') || (*p == 'E'))
976 p++;
977 *q++ = ESC_CHAR;
980 else
982 if (*p == '^')
984 p++;
985 if (*p == '^')
986 *q++ = *p++;
987 else
989 char c = (*p | 0x20);
990 if (c >= 'a' && c <= 'z')
992 *q++ = c - 'a' + 1;
993 p++;
995 else if (*p)
996 p++;
999 else
1000 *q++ = *p++;
1003 *q = 0;
1004 return valcopy;
1007 /* --------------------------------------------------------------------------------------------- */
1009 * Finds out a relative path from first to second, i.e. goes as many ..
1010 * as needed up in first and then goes down using second
1013 char *
1014 diff_two_paths (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
1016 int j, prevlen = -1, currlen;
1017 char *my_first = NULL, *my_second = NULL;
1018 char *buf = NULL;
1020 my_first = resolve_symlinks (vpath1);
1021 if (my_first == NULL)
1022 goto ret;
1024 my_second = resolve_symlinks (vpath2);
1025 if (my_second == NULL)
1026 goto ret;
1028 for (j = 0; j < 2; j++)
1030 char *p, *q;
1031 int i;
1033 p = my_first;
1034 q = my_second;
1035 while (TRUE)
1037 char *r, *s;
1038 ptrdiff_t len;
1040 r = strchr (p, PATH_SEP);
1041 if (r == NULL)
1042 break;
1043 s = strchr (q, PATH_SEP);
1044 if (s == NULL)
1045 break;
1047 len = r - p;
1048 if (len != (s - q) || strncmp (p, q, (size_t) len) != 0)
1049 break;
1051 p = r + 1;
1052 q = s + 1;
1054 p--;
1055 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++)
1057 currlen = (i + 1) * 3 + strlen (q) + 1;
1058 if (j != 0)
1060 if (currlen < prevlen)
1061 g_free (buf);
1062 else
1063 goto ret;
1065 p = buf = g_malloc (currlen);
1066 prevlen = currlen;
1067 for (; i >= 0; i--, p += 3)
1068 strcpy (p, "../");
1069 strcpy (p, q);
1072 ret:
1073 g_free (my_first);
1074 g_free (my_second);
1075 return buf;
1078 /* --------------------------------------------------------------------------------------------- */
1080 * Append text to GList, remove all entries with the same text
1083 GList *
1084 list_append_unique (GList * list, char *text)
1086 GList *lc_link;
1089 * Go to the last position and traverse the list backwards
1090 * starting from the second last entry to make sure that we
1091 * are not removing the current link.
1093 list = g_list_append (list, text);
1094 list = g_list_last (list);
1095 lc_link = g_list_previous (list);
1097 while (lc_link != NULL)
1099 GList *newlink;
1101 newlink = g_list_previous (lc_link);
1102 if (strcmp ((char *) lc_link->data, text) == 0)
1104 GList *tmp;
1106 g_free (lc_link->data);
1107 tmp = g_list_remove_link (list, lc_link);
1108 (void) tmp;
1109 g_list_free_1 (lc_link);
1111 lc_link = newlink;
1114 return list;
1117 /* --------------------------------------------------------------------------------------------- */
1119 * Read and restore position for the given filename.
1120 * If there is no stored data, return line 1 and col 0.
1123 void
1124 load_file_position (const vfs_path_t * filename_vpath, long *line, long *column, off_t * offset,
1125 GArray ** bookmarks)
1127 char *fn;
1128 FILE *f;
1129 char buf[MC_MAXPATHLEN + 100];
1130 const size_t len = vfs_path_len (filename_vpath);
1132 /* defaults */
1133 *line = 1;
1134 *column = 0;
1135 *offset = 0;
1137 /* open file with positions */
1138 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1139 f = fopen (fn, "r");
1140 g_free (fn);
1141 if (f == NULL)
1142 return;
1144 /* prepare array for serialized bookmarks */
1145 if (bookmarks != NULL)
1146 *bookmarks = g_array_sized_new (FALSE, FALSE, sizeof (size_t), MAX_SAVED_BOOKMARKS);
1148 while (fgets (buf, sizeof (buf), f) != NULL)
1150 const char *p;
1151 gchar **pos_tokens;
1153 /* check if the filename matches the beginning of string */
1154 if (strncmp (buf, vfs_path_as_str (filename_vpath), len) != 0)
1155 continue;
1157 /* followed by single space */
1158 if (buf[len] != ' ')
1159 continue;
1161 /* and string without spaces */
1162 p = &buf[len + 1];
1163 if (strchr (p, ' ') != NULL)
1164 continue;
1166 pos_tokens = g_strsplit (p, ";", 3 + MAX_SAVED_BOOKMARKS);
1167 if (pos_tokens[0] == NULL)
1169 *line = 1;
1170 *column = 0;
1171 *offset = 0;
1173 else
1175 *line = strtol (pos_tokens[0], NULL, 10);
1176 if (pos_tokens[1] == NULL)
1178 *column = 0;
1179 *offset = 0;
1181 else
1183 *column = strtol (pos_tokens[1], NULL, 10);
1184 if (pos_tokens[2] == NULL)
1185 *offset = 0;
1186 else if (bookmarks != NULL)
1188 size_t i;
1190 *offset = (off_t) g_ascii_strtoll (pos_tokens[2], NULL, 10);
1192 for (i = 0; i < MAX_SAVED_BOOKMARKS && pos_tokens[3 + i] != NULL; i++)
1194 size_t val;
1196 val = strtoul (pos_tokens[3 + i], NULL, 10);
1197 g_array_append_val (*bookmarks, val);
1203 g_strfreev (pos_tokens);
1206 fclose (f);
1209 /* --------------------------------------------------------------------------------------------- */
1211 * Save position for the given file
1214 void
1215 save_file_position (const vfs_path_t * filename_vpath, long line, long column, off_t offset,
1216 GArray * bookmarks)
1218 static size_t filepos_max_saved_entries = 0;
1219 char *fn, *tmp_fn;
1220 FILE *f, *tmp_f;
1221 char buf[MC_MAXPATHLEN + 100];
1222 size_t i;
1223 const size_t len = vfs_path_len (filename_vpath);
1224 gboolean src_error = FALSE;
1226 if (filepos_max_saved_entries == 0)
1227 filepos_max_saved_entries = mc_config_get_int (mc_global.main_config, CONFIG_APP_SECTION,
1228 "filepos_max_saved_entries", 1024);
1230 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1231 if (fn == NULL)
1232 goto early_error;
1234 mc_util_make_backup_if_possible (fn, TMP_SUFFIX);
1236 /* open file */
1237 f = fopen (fn, "w");
1238 if (f == NULL)
1239 goto open_target_error;
1241 tmp_fn = g_strdup_printf ("%s" TMP_SUFFIX, fn);
1242 tmp_f = fopen (tmp_fn, "r");
1243 if (tmp_f == NULL)
1245 src_error = TRUE;
1246 goto open_source_error;
1249 /* put the new record */
1250 if (line != 1 || column != 0 || bookmarks != NULL)
1252 if (fprintf
1253 (f, "%s %ld;%ld;%" PRIuMAX, vfs_path_as_str (filename_vpath), line, column,
1254 (uintmax_t) offset) < 0)
1255 goto write_position_error;
1256 if (bookmarks != NULL)
1257 for (i = 0; i < bookmarks->len && i < MAX_SAVED_BOOKMARKS; i++)
1258 if (fprintf (f, ";%zu", g_array_index (bookmarks, size_t, i)) < 0)
1259 goto write_position_error;
1261 if (fprintf (f, "\n") < 0)
1262 goto write_position_error;
1265 i = 1;
1266 while (fgets (buf, sizeof (buf), tmp_f) != NULL)
1268 if (buf[len] == ' ' && strncmp (buf, vfs_path_as_str (filename_vpath), len) == 0
1269 && strchr (&buf[len + 1], ' ') == NULL)
1270 continue;
1272 fprintf (f, "%s", buf);
1273 if (++i > filepos_max_saved_entries)
1274 break;
1277 write_position_error:
1278 fclose (tmp_f);
1279 open_source_error:
1280 g_free (tmp_fn);
1281 fclose (f);
1282 if (src_error)
1283 mc_util_restore_from_backup_if_possible (fn, TMP_SUFFIX);
1284 else
1285 mc_util_unlink_backup_if_possible (fn, TMP_SUFFIX);
1286 open_target_error:
1287 g_free (fn);
1288 early_error:
1289 if (bookmarks != NULL)
1290 g_array_free (bookmarks, TRUE);
1293 /* --------------------------------------------------------------------------------------------- */
1295 extern int
1296 ascii_alpha_to_cntrl (int ch)
1298 if ((ch >= ASCII_A && ch <= ASCII_Z) || (ch >= ASCII_a && ch <= ASCII_z))
1300 ch &= 0x1f;
1302 return ch;
1305 /* --------------------------------------------------------------------------------------------- */
1307 const char *
1308 Q_ (const char *s)
1310 const char *result, *sep;
1312 result = _(s);
1313 sep = strchr (result, '|');
1314 return (sep != NULL) ? sep + 1 : result;
1317 /* --------------------------------------------------------------------------------------------- */
1319 gboolean
1320 mc_util_make_backup_if_possible (const char *file_name, const char *backup_suffix)
1322 struct stat stat_buf;
1323 char *backup_path;
1324 gboolean ret;
1325 if (!exist_file (file_name))
1326 return FALSE;
1328 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1330 if (backup_path == NULL)
1331 return FALSE;
1333 ret = mc_util_write_backup_content (file_name, backup_path);
1335 if (ret)
1337 /* Backup file will have same ownership with main file. */
1338 if (stat (file_name, &stat_buf) == 0)
1339 chmod (backup_path, stat_buf.st_mode);
1340 else
1341 chmod (backup_path, S_IRUSR | S_IWUSR);
1344 g_free (backup_path);
1346 return ret;
1349 /* --------------------------------------------------------------------------------------------- */
1351 gboolean
1352 mc_util_restore_from_backup_if_possible (const char *file_name, const char *backup_suffix)
1354 gboolean ret;
1355 char *backup_path;
1357 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1358 if (backup_path == NULL)
1359 return FALSE;
1361 ret = mc_util_write_backup_content (backup_path, file_name);
1362 g_free (backup_path);
1364 return ret;
1367 /* --------------------------------------------------------------------------------------------- */
1369 gboolean
1370 mc_util_unlink_backup_if_possible (const char *file_name, const char *backup_suffix)
1372 char *backup_path;
1374 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1375 if (backup_path == NULL)
1376 return FALSE;
1378 if (exist_file (backup_path))
1380 vfs_path_t *vpath;
1382 vpath = vfs_path_from_str (backup_path);
1383 mc_unlink (vpath);
1384 vfs_path_free (vpath);
1387 g_free (backup_path);
1388 return TRUE;
1391 /* --------------------------------------------------------------------------------------------- */
1393 * partly taken from dcigettext.c, returns "" for default locale
1394 * value should be freed by calling function g_free()
1397 char *
1398 guess_message_value (void)
1400 static const char *const var[] = {
1401 /* Setting of LC_ALL overwrites all other. */
1402 /* Do not use LANGUAGE for check user locale and drowing hints */
1403 "LC_ALL",
1404 /* Next comes the name of the desired category. */
1405 "LC_MESSAGES",
1406 /* Last possibility is the LANG environment variable. */
1407 "LANG",
1408 /* NULL exit loops */
1409 NULL
1412 unsigned i = 0;
1413 const char *locale = NULL;
1415 while (var[i] != NULL)
1417 locale = getenv (var[i]);
1418 if (locale != NULL && locale[0] != '\0')
1419 break;
1420 i++;
1423 if (locale == NULL)
1424 locale = "";
1426 return g_strdup (locale);
1429 /* --------------------------------------------------------------------------------------------- */
1432 * The "profile root" is the tree under which all of MC's user data &
1433 * settings are stored.
1435 * It defaults to the user's home dir. The user may override this default
1436 * with the environment variable $MC_PROFILE_ROOT.
1438 const char *
1439 mc_get_profile_root (void)
1441 static const char *profile_root = NULL;
1443 if (profile_root == NULL)
1445 profile_root = g_getenv ("MC_PROFILE_ROOT");
1446 if (profile_root == NULL || *profile_root == '\0')
1447 profile_root = mc_config_get_home_dir ();
1450 return profile_root;
1453 /* --------------------------------------------------------------------------------------------- */
1455 * Propagate error in simple way.
1457 * @param dest error return location
1458 * @param code error code
1459 * @param format printf()-style format for error message
1460 * @param ... parameters for message format
1463 void
1464 mc_propagate_error (GError ** dest, int code, const char *format, ...)
1466 if (dest != NULL && *dest == NULL)
1468 GError *tmp_error;
1469 va_list args;
1471 va_start (args, format);
1472 tmp_error = g_error_new_valist (MC_ERROR, code, format, args);
1473 va_end (args);
1475 g_propagate_error (dest, tmp_error);
1479 /* --------------------------------------------------------------------------------------------- */
1481 * Replace existing error in simple way.
1483 * @param dest error return location
1484 * @param code error code
1485 * @param format printf()-style format for error message
1486 * @param ... parameters for message format
1489 void
1490 mc_replace_error (GError ** dest, int code, const char *format, ...)
1492 if (dest != NULL)
1494 GError *tmp_error;
1495 va_list args;
1497 va_start (args, format);
1498 tmp_error = g_error_new_valist (MC_ERROR, code, format, args);
1499 va_end (args);
1501 g_error_free (*dest);
1502 *dest = NULL;
1503 g_propagate_error (dest, tmp_error);
1507 /* --------------------------------------------------------------------------------------------- */
1510 * Returns if the given duration has elapsed since the given timestamp,
1511 * and if it has then updates the timestamp.
1513 * @param timestamp the last timestamp in microseconds, updated if the given time elapsed
1514 * @param deleay amount of time in microseconds
1516 * @return TRUE if clock skew detected, FALSE otherwise
1518 gboolean
1519 mc_time_elapsed (guint64 * timestamp, guint64 delay)
1521 guint64 now;
1523 now = mc_timer_elapsed (mc_global.timer);
1525 if (now >= *timestamp && now < *timestamp + delay)
1526 return FALSE;
1528 *timestamp = now;
1529 return TRUE;
1532 /* --------------------------------------------------------------------------------------------- */