Add threshold for file size.
[midnight-commander.git] / lib / util.c
blob6f5125d4f4be2406615fda2d993e684aca09f638
1 /*
2 Various utilities
4 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2007, 2009, 2011, 2013
6 The Free Software Foundation, Inc.
8 Written by:
9 Miguel de Icaza, 1994, 1995, 1996
10 Janne Kukonlehto, 1994, 1995, 1996
11 Dugan Porter, 1994, 1995, 1996
12 Jakub Jelinek, 1994, 1995, 1996
13 Mauricio Plaza, 1994, 1995, 1996
14 Slava Zanko <slavazanko@gmail.com>, 2013
16 This file is part of the Midnight Commander.
18 The Midnight Commander is free software: you can redistribute it
19 and/or modify it under the terms of the GNU General Public License as
20 published by the Free Software Foundation, either version 3 of the License,
21 or (at your option) any later version.
23 The Midnight Commander is distributed in the hope that it will be useful,
24 but WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 GNU General Public License for more details.
28 You should have received a copy of the GNU General Public License
29 along with this program. If not, see <http://www.gnu.org/licenses/>.
32 /** \file lib/util.c
33 * \brief Source: various utilities
36 #include <config.h>
38 #include <ctype.h>
39 #include <limits.h>
40 #include <stdarg.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <fcntl.h>
45 #include <sys/time.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <unistd.h>
50 #include "lib/global.h"
51 #include "lib/mcconfig.h"
52 #include "lib/fileloc.h"
53 #include "lib/vfs/vfs.h"
54 #include "lib/strutil.h"
55 #include "lib/util.h"
57 /*** global variables ****************************************************************************/
59 /*** file scope macro definitions ****************************************************************/
61 #define ismode(n,m) ((n & m) == m)
63 /* Number of attempts to create a temporary file */
64 #ifndef TMP_MAX
65 #define TMP_MAX 16384
66 #endif /* !TMP_MAX */
68 #define TMP_SUFFIX ".tmp"
70 #define ASCII_A (0x40 + 1)
71 #define ASCII_Z (0x40 + 26)
72 #define ASCII_a (0x60 + 1)
73 #define ASCII_z (0x60 + 26)
75 /*** file scope type declarations ****************************************************************/
77 /*** file scope variables ************************************************************************/
79 /*** file scope functions ************************************************************************/
80 /* --------------------------------------------------------------------------------------------- */
82 static inline int
83 is_7bit_printable (unsigned char c)
85 return (c > 31 && c < 127);
88 /* --------------------------------------------------------------------------------------------- */
90 static inline int
91 is_iso_printable (unsigned char c)
93 return ((c > 31 && c < 127) || c >= 160);
96 /* --------------------------------------------------------------------------------------------- */
98 static inline int
99 is_8bit_printable (unsigned char c)
101 /* "Full 8 bits output" doesn't work on xterm */
102 if (mc_global.tty.xterm_flag)
103 return is_iso_printable (c);
105 return (c > 31 && c != 127 && c != 155);
108 /* --------------------------------------------------------------------------------------------- */
110 static char *
111 resolve_symlinks (const vfs_path_t * vpath)
113 char *p, *p2;
114 char *buf, *buf2, *q, *r, c;
115 struct stat mybuf;
117 if (vpath->relative)
118 return NULL;
120 p = p2 = g_strdup (vfs_path_as_str (vpath));
121 r = buf = g_malloc (MC_MAXPATHLEN);
122 buf2 = g_malloc (MC_MAXPATHLEN);
123 *r++ = PATH_SEP;
124 *r = 0;
128 q = strchr (p + 1, PATH_SEP);
129 if (!q)
131 q = strchr (p + 1, 0);
132 if (q == p + 1)
133 break;
135 c = *q;
136 *q = 0;
137 if (mc_lstat (vpath, &mybuf) < 0)
139 g_free (buf);
140 buf = NULL;
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 g_free (buf);
153 buf = NULL;
154 goto ret;
156 buf2[len] = 0;
157 if (*buf2 == PATH_SEP)
158 strcpy (buf, buf2);
159 else
160 strcpy (r, buf2);
162 canonicalize_pathname (buf);
163 r = strchr (buf, 0);
164 if (!*r || *(r - 1) != PATH_SEP)
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)
176 strcpy (buf, PATH_SEP_STR);
177 else if (*(r - 1) == PATH_SEP && 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;
209 int ret2;
210 ret2 = fflush (backup_fd);
211 ret2 = fclose (backup_fd);
212 (void) ret2;
214 g_free (contents);
215 return ret1;
218 /* --------------------------------------------------------------------------------------------- */
219 /*** public functions ****************************************************************************/
220 /* --------------------------------------------------------------------------------------------- */
223 is_printable (int c)
225 c &= 0xff;
227 #ifdef HAVE_CHARSET
228 /* "Display bits" is ignored, since the user controls the output
229 by setting the output codepage */
230 return is_8bit_printable (c);
231 #else
232 if (!mc_global.eight_bit_clean)
233 return is_7bit_printable (c);
235 if (mc_global.full_eight_bits)
237 return is_8bit_printable (c);
239 else
240 return is_iso_printable (c);
241 #endif /* !HAVE_CHARSET */
244 /* --------------------------------------------------------------------------------------------- */
246 * Quote the filename for the purpose of inserting it into the command
247 * line. If quote_percent is 1, replace "%" with "%%" - the percent is
248 * processed by the mc command line.
250 char *
251 name_quote (const char *s, int quote_percent)
253 char *ret, *d;
255 d = ret = g_malloc (strlen (s) * 2 + 2 + 1);
256 if (*s == '-')
258 *d++ = '.';
259 *d++ = '/';
262 for (; *s; s++, d++)
264 switch (*s)
266 case '%':
267 if (quote_percent)
268 *d++ = '%';
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 *d++ = '\\';
294 break;
295 case '~':
296 case '#':
297 if (d == ret)
298 *d++ = '\\';
299 break;
301 *d = *s;
303 *d = '\0';
304 return ret;
307 /* --------------------------------------------------------------------------------------------- */
309 char *
310 fake_name_quote (const char *s, int quote_percent)
312 (void) quote_percent;
313 return g_strdup (s);
316 /* --------------------------------------------------------------------------------------------- */
318 * path_trunc() is the same as str_trunc() but
319 * it deletes possible password from path for security
320 * reasons.
323 const char *
324 path_trunc (const char *path, size_t trunc_len)
326 vfs_path_t *vpath;
327 char *secure_path;
328 const char *ret;
330 vpath = vfs_path_from_str (path);
331 secure_path = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
332 vfs_path_free (vpath);
334 ret = str_trunc (secure_path, trunc_len);
335 g_free (secure_path);
337 return ret;
340 /* --------------------------------------------------------------------------------------------- */
342 const char *
343 size_trunc (uintmax_t size, gboolean use_si)
345 static char x[BUF_TINY];
346 uintmax_t divisor = 1;
347 const char *xtra = "";
349 if (size > 999999999UL)
351 divisor = use_si ? 1000 : 1024;
352 xtra = use_si ? "k" : "K";
354 if (size / divisor > 999999999UL)
356 divisor = use_si ? (1000 * 1000) : (1024 * 1024);
357 xtra = use_si ? "m" : "M";
359 if (size / divisor > 999999999UL)
361 divisor = use_si ? (1000 * 1000 * 1000) : (1024 * 1024 * 1024);
362 xtra = use_si ? "g" : "G";
366 g_snprintf (x, sizeof (x), "%.0f%s", 1.0 * size / divisor, xtra);
367 return x;
370 /* --------------------------------------------------------------------------------------------- */
372 const char *
373 size_trunc_sep (uintmax_t size, gboolean use_si)
375 static char x[60];
376 int count;
377 const char *p, *y;
378 char *d;
380 p = y = size_trunc (size, use_si);
381 p += strlen (p) - 1;
382 d = x + sizeof (x) - 1;
383 *d-- = '\0';
384 while (p >= y && isalpha ((unsigned char) *p))
385 *d-- = *p--;
386 for (count = 0; p >= y; count++)
388 if (count == 3)
390 *d-- = ',';
391 count = 0;
393 *d-- = *p--;
395 d++;
396 if (*d == ',')
397 d++;
398 return d;
401 /* --------------------------------------------------------------------------------------------- */
403 * Print file SIZE to BUFFER, but don't exceed LEN characters,
404 * not including trailing 0. BUFFER should be at least LEN+1 long.
405 * This function is called for every file on panels, so avoid
406 * floating point by any means.
408 * Units: size units (filesystem sizes are 1K blocks)
409 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
412 void
413 size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gboolean use_si)
415 /* Avoid taking power for every file. */
416 /* *INDENT-OFF* */
417 static const uintmax_t power10[] = {
418 /* we hope that size of uintmax_t is 4 bytes at least */
419 1ULL,
420 10ULL,
421 100ULL,
422 1000ULL,
423 10000ULL,
424 100000ULL,
425 1000000ULL,
426 10000000ULL,
427 100000000ULL,
428 1000000000ULL
429 /* maximum value of uintmax_t (in case of 4 bytes) is
430 4294967295
432 #if SIZEOF_UINTMAX_T == 8
434 10000000000ULL,
435 100000000000ULL,
436 1000000000000ULL,
437 10000000000000ULL,
438 100000000000000ULL,
439 1000000000000000ULL,
440 10000000000000000ULL,
441 100000000000000000ULL,
442 1000000000000000000ULL,
443 10000000000000000000ULL
444 /* maximum value of uintmax_t (in case of 8 bytes) is
445 18447644073710439615
447 #endif
449 /* *INDENT-ON* */
450 static const char *const suffix[] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL };
451 static const char *const suffix_lc[] = { "", "k", "m", "g", "t", "p", "e", "z", "y", NULL };
453 const char *const *sfx = use_si ? suffix_lc : suffix;
454 int j = 0;
456 if (len == 0)
457 len = 9;
458 #if SIZEOF_UINTMAX_T == 8
459 /* 20 decimal digits are required to represent 8 bytes */
460 else if (len > 19)
461 len = 19;
462 #else
463 /* 10 decimal digits are required to represent 4 bytes */
464 else if (len > 9)
465 len = 9;
466 #endif
469 * recalculate from 1024 base to 1000 base if units>0
470 * We can't just multiply by 1024 - that might cause overflow
471 * if uintmax_t type is too small
473 if (use_si)
474 for (j = 0; j < units; j++)
476 uintmax_t size_remain;
478 size_remain = ((size % 125) * 1024) / 1000; /* size mod 125, recalculated */
479 size /= 125; /* 128/125 = 1024/1000 */
480 size *= 128; /* This will convert size from multiple of 1024 to multiple of 1000 */
481 size += size_remain; /* Re-add remainder lost by division/multiplication */
484 for (j = units; sfx[j] != NULL; j++)
486 if (size == 0)
488 if (j == units)
490 /* Empty files will print "0" even with minimal width. */
491 g_snprintf (buffer, len + 1, "%s", "0");
493 else
495 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
496 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s", (j > 1) ? sfx[j - 1] : "B");
498 break;
501 if (size < power10[len - (j > 0 ? 1 : 0)])
503 g_snprintf (buffer, len + 1, "%" PRIuMAX "%s", size, sfx[j]);
504 break;
507 /* Powers of 1000 or 1024, with rounding. */
508 if (use_si)
509 size = (size + 500) / 1000;
510 else
511 size = (size + 512) >> 10;
515 /* --------------------------------------------------------------------------------------------- */
517 const char *
518 string_perm (mode_t mode_bits)
520 static char mode[11];
522 strcpy (mode, "----------");
523 if (S_ISDIR (mode_bits))
524 mode[0] = 'd';
525 if (S_ISCHR (mode_bits))
526 mode[0] = 'c';
527 if (S_ISBLK (mode_bits))
528 mode[0] = 'b';
529 if (S_ISLNK (mode_bits))
530 mode[0] = 'l';
531 if (S_ISFIFO (mode_bits))
532 mode[0] = 'p';
533 if (S_ISNAM (mode_bits))
534 mode[0] = 'n';
535 if (S_ISSOCK (mode_bits))
536 mode[0] = 's';
537 if (S_ISDOOR (mode_bits))
538 mode[0] = 'D';
539 if (ismode (mode_bits, S_IXOTH))
540 mode[9] = 'x';
541 if (ismode (mode_bits, S_IWOTH))
542 mode[8] = 'w';
543 if (ismode (mode_bits, S_IROTH))
544 mode[7] = 'r';
545 if (ismode (mode_bits, S_IXGRP))
546 mode[6] = 'x';
547 if (ismode (mode_bits, S_IWGRP))
548 mode[5] = 'w';
549 if (ismode (mode_bits, S_IRGRP))
550 mode[4] = 'r';
551 if (ismode (mode_bits, S_IXUSR))
552 mode[3] = 'x';
553 if (ismode (mode_bits, S_IWUSR))
554 mode[2] = 'w';
555 if (ismode (mode_bits, S_IRUSR))
556 mode[1] = 'r';
557 #ifdef S_ISUID
558 if (ismode (mode_bits, S_ISUID))
559 mode[3] = (mode[3] == 'x') ? 's' : 'S';
560 #endif /* S_ISUID */
561 #ifdef S_ISGID
562 if (ismode (mode_bits, S_ISGID))
563 mode[6] = (mode[6] == 'x') ? 's' : 'S';
564 #endif /* S_ISGID */
565 #ifdef S_ISVTX
566 if (ismode (mode_bits, S_ISVTX))
567 mode[9] = (mode[9] == 'x') ? 't' : 'T';
568 #endif /* S_ISVTX */
569 return mode;
572 /* --------------------------------------------------------------------------------------------- */
574 const char *
575 extension (const char *filename)
577 const char *d = strrchr (filename, '.');
578 return (d != NULL) ? d + 1 : "";
581 /* --------------------------------------------------------------------------------------------- */
583 char *
584 load_mc_home_file (const char *from, const char *filename, char **allocated_filename)
586 char *hintfile_base, *hintfile;
587 char *lang;
588 char *data;
590 hintfile_base = g_build_filename (from, filename, (char *) NULL);
591 lang = guess_message_value ();
593 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
594 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
596 /* Fall back to the two-letter language code */
597 if (lang[0] != '\0' && lang[1] != '\0')
598 lang[2] = '\0';
599 g_free (hintfile);
600 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
601 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
603 g_free (hintfile);
604 hintfile = hintfile_base;
605 g_file_get_contents (hintfile_base, &data, NULL, NULL);
609 g_free (lang);
611 if (hintfile != hintfile_base)
612 g_free (hintfile_base);
614 if (allocated_filename != NULL)
615 *allocated_filename = hintfile;
616 else
617 g_free (hintfile);
619 return data;
622 /* --------------------------------------------------------------------------------------------- */
624 const char *
625 extract_line (const char *s, const char *top)
627 static char tmp_line[BUF_MEDIUM];
628 char *t = tmp_line;
630 while (*s && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line) - 1 && s < top)
631 *t++ = *s++;
632 *t = 0;
633 return tmp_line;
636 /* --------------------------------------------------------------------------------------------- */
638 * The basename routine
641 const char *
642 x_basename (const char *s)
644 const char *url_delim, *path_sep;
646 url_delim = g_strrstr (s, VFS_PATH_URL_DELIMITER);
647 path_sep = strrchr (s, PATH_SEP);
649 if (url_delim == NULL
650 || url_delim < path_sep - strlen (VFS_PATH_URL_DELIMITER)
651 || url_delim - s + strlen (VFS_PATH_URL_DELIMITER) < strlen (s))
653 /* avoid trailing PATH_SEP, if present */
654 if (s[strlen (s) - 1] == PATH_SEP)
656 while (--path_sep > s && *path_sep != PATH_SEP);
657 return (path_sep != s) ? path_sep + 1 : s;
659 else
660 return (path_sep != NULL) ? path_sep + 1 : s;
663 while (--url_delim > s && *url_delim != PATH_SEP);
664 while (--url_delim > s && *url_delim != PATH_SEP);
666 return (url_delim == s) ? s : url_delim + 1;
669 /* --------------------------------------------------------------------------------------------- */
671 const char *
672 unix_error_string (int error_num)
674 static char buffer[BUF_LARGE];
675 gchar *strerror_currentlocale;
677 strerror_currentlocale = g_locale_from_utf8 (g_strerror (error_num), -1, NULL, NULL, NULL);
678 g_snprintf (buffer, sizeof (buffer), "%s (%d)", strerror_currentlocale, error_num);
679 g_free (strerror_currentlocale);
681 return buffer;
684 /* --------------------------------------------------------------------------------------------- */
686 const char *
687 skip_separators (const char *s)
689 const char *su = s;
691 for (; *su; str_cnext_char (&su))
692 if (*su != ' ' && *su != '\t' && *su != ',')
693 break;
695 return su;
698 /* --------------------------------------------------------------------------------------------- */
700 const char *
701 skip_numbers (const char *s)
703 const char *su = s;
705 for (; *su; str_cnext_char (&su))
706 if (!str_isdigit (su))
707 break;
709 return su;
712 /* --------------------------------------------------------------------------------------------- */
714 * Remove all control sequences from the argument string. We define
715 * "control sequence", in a sort of pidgin BNF, as follows:
717 * control-seq = Esc non-'['
718 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
720 * This scheme works for all the terminals described in my termcap /
721 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
722 * terminals. If I hear from a single person who uses such a terminal
723 * with MC, I'll be glad to add support for it. (Dugan)
724 * Non-printable characters are also removed.
727 char *
728 strip_ctrl_codes (char *s)
730 char *w; /* Current position where the stripped data is written */
731 char *r; /* Current position where the original data is read */
733 if (s == NULL)
734 return NULL;
736 for (w = s, r = s; *r != '\0';)
738 if (*r == ESC_CHAR)
740 /* Skip the control sequence's arguments */ ;
741 /* '(' need to avoid strange 'B' letter in *Suse (if mc runs under root user) */
742 if (*(++r) == '[' || *r == '(')
744 /* strchr() matches trailing binary 0 */
745 while (*(++r) != '\0' && strchr ("0123456789;?", *r) != NULL)
748 else if (*r == ']')
751 * Skip xterm's OSC (Operating System Command)
752 * http://www.xfree86.org/current/ctlseqs.html
753 * OSC P s ; P t ST
754 * OSC P s ; P t BEL
756 char *new_r = r;
758 for (; *new_r != '\0'; ++new_r)
760 switch (*new_r)
762 /* BEL */
763 case '\a':
764 r = new_r;
765 goto osc_out;
766 case ESC_CHAR:
767 /* ST */
768 if (*(new_r + 1) == '\\')
770 r = new_r + 1;
771 goto osc_out;
775 osc_out:
780 * Now we are at the last character of the sequence.
781 * Skip it unless it's binary 0.
783 if (*r != '\0')
784 r++;
786 else
788 char *n;
790 n = str_get_next_char (r);
791 if (str_isprint (r))
793 memmove (w, r, n - r);
794 w += n - r;
796 r = n;
800 *w = '\0';
801 return s;
804 /* --------------------------------------------------------------------------------------------- */
806 enum compression_type
807 get_compression_type (int fd, const char *name)
809 unsigned char magic[16];
810 size_t str_len;
812 /* Read the magic signature */
813 if (mc_read (fd, (char *) magic, 4) != 4)
814 return COMPRESSION_NONE;
816 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
817 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236))
819 return COMPRESSION_GZIP;
822 /* PKZIP_MAGIC */
823 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003 && magic[3] == 004)
825 /* Read compression type */
826 mc_lseek (fd, 8, SEEK_SET);
827 if (mc_read (fd, (char *) magic, 2) != 2)
828 return COMPRESSION_NONE;
830 /* Gzip can handle only deflated (8) or stored (0) files */
831 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
832 return COMPRESSION_NONE;
834 /* Compatible with gzip */
835 return COMPRESSION_GZIP;
838 /* PACK_MAGIC and LZH_MAGIC and compress magic */
839 if (magic[0] == 037 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235))
841 /* Compatible with gzip */
842 return COMPRESSION_GZIP;
845 /* BZIP and BZIP2 files */
846 if ((magic[0] == 'B') && (magic[1] == 'Z') && (magic[3] >= '1') && (magic[3] <= '9'))
848 switch (magic[2])
850 case '0':
851 return COMPRESSION_BZIP;
852 case 'h':
853 return COMPRESSION_BZIP2;
857 /* Support for LZMA (only utils format with magic in header).
858 * This is the default format of LZMA utils 4.32.1 and later. */
860 if (mc_read (fd, (char *) magic + 4, 2) != 2)
861 return COMPRESSION_NONE;
863 /* LZMA utils format */
864 if (magic[0] == 0xFF
865 && magic[1] == 'L'
866 && magic[2] == 'Z' && magic[3] == 'M' && magic[4] == 'A' && magic[5] == 0x00)
867 return COMPRESSION_LZMA;
869 /* XZ compression magic */
870 if (magic[0] == 0xFD
871 && magic[1] == 0x37
872 && magic[2] == 0x7A && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00)
873 return COMPRESSION_XZ;
875 str_len = strlen (name);
876 /* HACK: we must belive to extension of LZMA file :) ... */
877 if ((str_len > 5 && strcmp (&name[str_len - 5], ".lzma") == 0) ||
878 (str_len > 4 && strcmp (&name[str_len - 4], ".tlz") == 0))
879 return COMPRESSION_LZMA;
881 return COMPRESSION_NONE;
884 /* --------------------------------------------------------------------------------------------- */
886 const char *
887 decompress_extension (int type)
889 switch (type)
891 case COMPRESSION_GZIP:
892 return "/ugz" VFS_PATH_URL_DELIMITER;
893 case COMPRESSION_BZIP:
894 return "/ubz" VFS_PATH_URL_DELIMITER;
895 case COMPRESSION_BZIP2:
896 return "/ubz2" VFS_PATH_URL_DELIMITER;
897 case COMPRESSION_LZMA:
898 return "/ulzma" VFS_PATH_URL_DELIMITER;
899 case COMPRESSION_XZ:
900 return "/uxz" VFS_PATH_URL_DELIMITER;
902 /* Should never reach this place */
903 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
904 return 0;
907 /* --------------------------------------------------------------------------------------------- */
909 void
910 wipe_password (char *passwd)
912 char *p = passwd;
914 if (!p)
915 return;
916 for (; *p; p++)
917 *p = 0;
918 g_free (passwd);
921 /* --------------------------------------------------------------------------------------------- */
923 * Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key
925 * @param p pointer to string
927 * @return newly allocated string
930 char *
931 convert_controls (const char *p)
933 char *valcopy = g_strdup (p);
934 char *q;
936 /* Parse the escape special character */
937 for (q = valcopy; *p;)
939 if (*p == '\\')
941 p++;
942 if ((*p == 'e') || (*p == 'E'))
944 p++;
945 *q++ = ESC_CHAR;
948 else
950 if (*p == '^')
952 p++;
953 if (*p == '^')
954 *q++ = *p++;
955 else
957 char c = (*p | 0x20);
958 if (c >= 'a' && c <= 'z')
960 *q++ = c - 'a' + 1;
961 p++;
963 else if (*p)
964 p++;
967 else
968 *q++ = *p++;
971 *q = 0;
972 return valcopy;
975 /* --------------------------------------------------------------------------------------------- */
977 * Finds out a relative path from first to second, i.e. goes as many ..
978 * as needed up in first and then goes down using second
981 char *
982 diff_two_paths (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
984 char *p, *q, *r, *s, *buf = NULL;
985 int i, j, prevlen = -1, currlen;
986 char *my_first = NULL, *my_second = NULL;
988 my_first = resolve_symlinks (vpath1);
989 if (my_first == NULL)
990 goto ret;
992 my_second = resolve_symlinks (vpath2);
993 if (my_second == NULL)
994 goto ret;
996 for (j = 0; j < 2; j++)
998 p = my_first;
999 q = my_second;
1000 while (TRUE)
1002 r = strchr (p, PATH_SEP);
1003 s = strchr (q, PATH_SEP);
1004 if (r == NULL || s == NULL)
1005 break;
1006 *r = '\0';
1007 *s = '\0';
1008 if (strcmp (p, q) != 0)
1010 *r = PATH_SEP;
1011 *s = PATH_SEP;
1012 break;
1015 *r = PATH_SEP;
1016 *s = PATH_SEP;
1018 p = r + 1;
1019 q = s + 1;
1021 p--;
1022 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++)
1024 currlen = (i + 1) * 3 + strlen (q) + 1;
1025 if (j != 0)
1027 if (currlen < prevlen)
1028 g_free (buf);
1029 else
1030 goto ret;
1032 p = buf = g_malloc (currlen);
1033 prevlen = currlen;
1034 for (; i >= 0; i--, p += 3)
1035 strcpy (p, "../");
1036 strcpy (p, q);
1039 ret:
1040 g_free (my_first);
1041 g_free (my_second);
1042 return buf;
1045 /* --------------------------------------------------------------------------------------------- */
1047 * Append text to GList, remove all entries with the same text
1050 GList *
1051 list_append_unique (GList * list, char *text)
1053 GList *lc_link;
1056 * Go to the last position and traverse the list backwards
1057 * starting from the second last entry to make sure that we
1058 * are not removing the current link.
1060 list = g_list_append (list, text);
1061 list = g_list_last (list);
1062 lc_link = g_list_previous (list);
1064 while (lc_link != NULL)
1066 GList *newlink;
1068 newlink = g_list_previous (lc_link);
1069 if (strcmp ((char *) lc_link->data, text) == 0)
1071 GList *tmp;
1073 g_free (lc_link->data);
1074 tmp = g_list_remove_link (list, lc_link);
1075 (void) tmp;
1076 g_list_free_1 (lc_link);
1078 lc_link = newlink;
1081 return list;
1084 /* --------------------------------------------------------------------------------------------- */
1086 * Read and restore position for the given filename.
1087 * If there is no stored data, return line 1 and col 0.
1090 void
1091 load_file_position (const vfs_path_t * filename_vpath, long *line, long *column, off_t * offset,
1092 GArray ** bookmarks)
1094 char *fn;
1095 FILE *f;
1096 char buf[MC_MAXPATHLEN + 100];
1097 const size_t len = vfs_path_len (filename_vpath);
1099 /* defaults */
1100 *line = 1;
1101 *column = 0;
1102 *offset = 0;
1104 /* open file with positions */
1105 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1106 f = fopen (fn, "r");
1107 g_free (fn);
1108 if (f == NULL)
1109 return;
1111 /* prepare array for serialized bookmarks */
1112 if (bookmarks != NULL)
1113 *bookmarks = g_array_sized_new (FALSE, FALSE, sizeof (size_t), MAX_SAVED_BOOKMARKS);
1115 while (fgets (buf, sizeof (buf), f) != NULL)
1117 const char *p;
1118 gchar **pos_tokens;
1120 /* check if the filename matches the beginning of string */
1121 if (strncmp (buf, vfs_path_as_str (filename_vpath), len) != 0)
1122 continue;
1124 /* followed by single space */
1125 if (buf[len] != ' ')
1126 continue;
1128 /* and string without spaces */
1129 p = &buf[len + 1];
1130 if (strchr (p, ' ') != NULL)
1131 continue;
1133 pos_tokens = g_strsplit (p, ";", 3 + MAX_SAVED_BOOKMARKS);
1134 if (pos_tokens[0] == NULL)
1136 *line = 1;
1137 *column = 0;
1138 *offset = 0;
1140 else
1142 *line = strtol (pos_tokens[0], NULL, 10);
1143 if (pos_tokens[1] == NULL)
1145 *column = 0;
1146 *offset = 0;
1148 else
1150 *column = strtol (pos_tokens[1], NULL, 10);
1151 if (pos_tokens[2] == NULL)
1152 *offset = 0;
1153 else if (bookmarks != NULL)
1155 size_t i;
1157 *offset = (off_t) g_ascii_strtoll (pos_tokens[2], NULL, 10);
1159 for (i = 0; i < MAX_SAVED_BOOKMARKS && pos_tokens[3 + i] != NULL; i++)
1161 size_t val;
1163 val = strtoul (pos_tokens[3 + i], NULL, 10);
1164 g_array_append_val (*bookmarks, val);
1170 g_strfreev (pos_tokens);
1173 fclose (f);
1176 /* --------------------------------------------------------------------------------------------- */
1178 * Save position for the given file
1181 void
1182 save_file_position (const vfs_path_t * filename_vpath, long line, long column, off_t offset,
1183 GArray * bookmarks)
1185 static size_t filepos_max_saved_entries = 0;
1186 char *fn, *tmp_fn;
1187 FILE *f, *tmp_f;
1188 char buf[MC_MAXPATHLEN + 100];
1189 size_t i;
1190 const size_t len = vfs_path_len (filename_vpath);
1191 gboolean src_error = FALSE;
1193 if (filepos_max_saved_entries == 0)
1194 filepos_max_saved_entries = mc_config_get_int (mc_main_config, CONFIG_APP_SECTION,
1195 "filepos_max_saved_entries", 1024);
1197 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1198 if (fn == NULL)
1199 goto early_error;
1201 mc_util_make_backup_if_possible (fn, TMP_SUFFIX);
1203 /* open file */
1204 f = fopen (fn, "w");
1205 if (f == NULL)
1206 goto open_target_error;
1208 tmp_fn = g_strdup_printf ("%s" TMP_SUFFIX, fn);
1209 tmp_f = fopen (tmp_fn, "r");
1210 if (tmp_f == NULL)
1212 src_error = TRUE;
1213 goto open_source_error;
1216 /* put the new record */
1217 if (line != 1 || column != 0 || bookmarks != NULL)
1219 if (fprintf
1220 (f, "%s %ld;%ld;%" PRIuMAX, vfs_path_as_str (filename_vpath), line, column,
1221 (uintmax_t) offset) < 0)
1222 goto write_position_error;
1223 if (bookmarks != NULL)
1224 for (i = 0; i < bookmarks->len && i < MAX_SAVED_BOOKMARKS; i++)
1225 if (fprintf (f, ";%zu", g_array_index (bookmarks, size_t, i)) < 0)
1226 goto write_position_error;
1228 if (fprintf (f, "\n") < 0)
1229 goto write_position_error;
1232 i = 1;
1233 while (fgets (buf, sizeof (buf), tmp_f) != NULL)
1235 if (buf[len] == ' ' && strncmp (buf, vfs_path_as_str (filename_vpath), len) == 0
1236 && strchr (&buf[len + 1], ' ') == NULL)
1237 continue;
1239 fprintf (f, "%s", buf);
1240 if (++i > filepos_max_saved_entries)
1241 break;
1244 write_position_error:
1245 fclose (tmp_f);
1246 open_source_error:
1247 g_free (tmp_fn);
1248 fclose (f);
1249 if (src_error)
1250 mc_util_restore_from_backup_if_possible (fn, TMP_SUFFIX);
1251 else
1252 mc_util_unlink_backup_if_possible (fn, TMP_SUFFIX);
1253 open_target_error:
1254 g_free (fn);
1255 early_error:
1256 if (bookmarks != NULL)
1257 g_array_free (bookmarks, TRUE);
1260 /* --------------------------------------------------------------------------------------------- */
1262 extern int
1263 ascii_alpha_to_cntrl (int ch)
1265 if ((ch >= ASCII_A && ch <= ASCII_Z) || (ch >= ASCII_a && ch <= ASCII_z))
1267 ch &= 0x1f;
1269 return ch;
1272 /* --------------------------------------------------------------------------------------------- */
1274 const char *
1275 Q_ (const char *s)
1277 const char *result, *sep;
1279 result = _(s);
1280 sep = strchr (result, '|');
1281 return (sep != NULL) ? sep + 1 : result;
1284 /* --------------------------------------------------------------------------------------------- */
1286 gboolean
1287 mc_util_make_backup_if_possible (const char *file_name, const char *backup_suffix)
1289 struct stat stat_buf;
1290 char *backup_path;
1291 gboolean ret;
1292 if (!exist_file (file_name))
1293 return FALSE;
1295 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1297 if (backup_path == NULL)
1298 return FALSE;
1300 ret = mc_util_write_backup_content (file_name, backup_path);
1302 if (ret)
1304 /* Backup file will have same ownership with main file. */
1305 if (stat (file_name, &stat_buf) == 0)
1306 chmod (backup_path, stat_buf.st_mode);
1307 else
1308 chmod (backup_path, S_IRUSR | S_IWUSR);
1311 g_free (backup_path);
1313 return ret;
1316 /* --------------------------------------------------------------------------------------------- */
1318 gboolean
1319 mc_util_restore_from_backup_if_possible (const char *file_name, const char *backup_suffix)
1321 gboolean ret;
1322 char *backup_path;
1324 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1325 if (backup_path == NULL)
1326 return FALSE;
1328 ret = mc_util_write_backup_content (backup_path, file_name);
1329 g_free (backup_path);
1331 return ret;
1334 /* --------------------------------------------------------------------------------------------- */
1336 gboolean
1337 mc_util_unlink_backup_if_possible (const char *file_name, const char *backup_suffix)
1339 char *backup_path;
1341 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1342 if (backup_path == NULL)
1343 return FALSE;
1345 if (exist_file (backup_path))
1347 vfs_path_t *vpath;
1349 vpath = vfs_path_from_str (backup_path);
1350 mc_unlink (vpath);
1351 vfs_path_free (vpath);
1354 g_free (backup_path);
1355 return TRUE;
1358 /* --------------------------------------------------------------------------------------------- */
1360 * partly taken from dcigettext.c, returns "" for default locale
1361 * value should be freed by calling function g_free()
1364 char *
1365 guess_message_value (void)
1367 static const char *const var[] = {
1368 /* Setting of LC_ALL overwrites all other. */
1369 /* Do not use LANGUAGE for check user locale and drowing hints */
1370 "LC_ALL",
1371 /* Next comes the name of the desired category. */
1372 "LC_MESSAGES",
1373 /* Last possibility is the LANG environment variable. */
1374 "LANG",
1375 /* NULL exit loops */
1376 NULL
1379 unsigned i = 0;
1380 const char *locale = NULL;
1382 while (var[i] != NULL)
1384 locale = getenv (var[i]);
1385 if (locale != NULL && locale[0] != '\0')
1386 break;
1387 i++;
1390 if (locale == NULL)
1391 locale = "";
1393 return g_strdup (locale);
1396 /* --------------------------------------------------------------------------------------------- */