Changes to handle vfs_path_t object:
[midnight-commander.git] / lib / util.c
blob70320d1dc5e1399009f5bd4299d40645fda02e73
1 /*
2 Various utilities
4 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2007, 2009, 2011
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
15 The file_date routine is mostly from GNU's fileutils package,
16 written by Richard Stallman and David MacKenzie.
18 This file is part of the Midnight Commander.
20 The Midnight Commander is free software: you can redistribute it
21 and/or modify it under the terms of the GNU General Public License as
22 published by the Free Software Foundation, either version 3 of the License,
23 or (at your option) any later version.
25 The Midnight Commander is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
30 You should have received a copy of the GNU General Public License
31 along with this program. If not, see <http://www.gnu.org/licenses/>.
34 /** \file
35 * \brief Source: various utilities
38 #include <config.h>
40 #include <ctype.h>
41 #include <limits.h>
42 #include <stdarg.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <fcntl.h>
47 #include <sys/time.h>
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <unistd.h>
52 #include "lib/global.h"
53 #include "lib/mcconfig.h"
54 #include "lib/fileloc.h"
55 #include "lib/vfs/vfs.h"
56 #include "lib/strutil.h"
57 #include "lib/util.h"
59 /*** global variables ****************************************************************************/
61 /*** file scope macro definitions ****************************************************************/
63 #define ismode(n,m) ((n & m) == m)
65 /* Number of attempts to create a temporary file */
66 #ifndef TMP_MAX
67 #define TMP_MAX 16384
68 #endif /* !TMP_MAX */
70 #define TMP_SUFFIX ".tmp"
72 #define ASCII_A (0x40 + 1)
73 #define ASCII_Z (0x40 + 26)
74 #define ASCII_a (0x60 + 1)
75 #define ASCII_z (0x60 + 26)
77 /*** file scope type declarations ****************************************************************/
79 /*** file scope variables ************************************************************************/
81 /*** file scope functions ************************************************************************/
82 /* --------------------------------------------------------------------------------------------- */
84 static inline int
85 is_7bit_printable (unsigned char c)
87 return (c > 31 && c < 127);
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 char *path)
115 char *buf, *buf2, *q, *r, c;
116 int len;
117 struct stat mybuf;
118 const char *p;
119 vfs_path_t *vpath;
121 if (*path != PATH_SEP)
122 return NULL;
124 vpath = vfs_path_from_str (path);
125 r = buf = g_malloc (MC_MAXPATHLEN);
126 buf2 = g_malloc (MC_MAXPATHLEN);
127 *r++ = PATH_SEP;
128 *r = 0;
129 p = path;
131 for (;;)
133 q = strchr (p + 1, PATH_SEP);
134 if (!q)
136 q = strchr (p + 1, 0);
137 if (q == p + 1)
138 break;
140 c = *q;
141 *q = 0;
142 if (mc_lstat (vpath, &mybuf) < 0)
144 g_free (buf);
145 buf = NULL;
146 *q = c;
147 goto ret;
149 if (!S_ISLNK (mybuf.st_mode))
150 strcpy (r, p + 1);
151 else
153 len = mc_readlink (vpath, buf2, MC_MAXPATHLEN - 1);
154 if (len < 0)
156 g_free (buf);
157 buf = NULL;
158 *q = c;
159 goto ret;
161 buf2[len] = 0;
162 if (*buf2 == PATH_SEP)
163 strcpy (buf, buf2);
164 else
165 strcpy (r, buf2);
167 canonicalize_pathname (buf);
168 r = strchr (buf, 0);
169 if (!*r || *(r - 1) != PATH_SEP)
171 *r++ = PATH_SEP;
172 *r = 0;
174 *q = c;
175 p = q;
176 if (!c)
177 break;
179 if (!*buf)
180 strcpy (buf, PATH_SEP_STR);
181 else if (*(r - 1) == PATH_SEP && r != buf + 1)
182 *(r - 1) = 0;
184 ret:
185 g_free (buf2);
186 vfs_path_free (vpath);
187 return buf;
190 /* --------------------------------------------------------------------------------------------- */
192 static gboolean
193 mc_util_write_backup_content (const char *from_file_name, const char *to_file_name)
195 FILE *backup_fd;
196 char *contents;
197 gsize length;
198 gboolean ret1 = TRUE;
200 if (!g_file_get_contents (from_file_name, &contents, &length, NULL))
201 return FALSE;
203 backup_fd = fopen (to_file_name, "w");
204 if (backup_fd == NULL)
206 g_free (contents);
207 return FALSE;
210 if (fwrite ((const void *) contents, 1, length, backup_fd) != length)
211 ret1 = FALSE;
213 int ret2;
214 ret2 = fflush (backup_fd);
215 ret2 = fclose (backup_fd);
217 g_free (contents);
218 return ret1;
221 /* --------------------------------------------------------------------------------------------- */
222 /*** public functions ****************************************************************************/
223 /* --------------------------------------------------------------------------------------------- */
226 is_printable (int c)
228 c &= 0xff;
230 #ifdef HAVE_CHARSET
231 /* "Display bits" is ignored, since the user controls the output
232 by setting the output codepage */
233 return is_8bit_printable (c);
234 #else
235 if (!mc_global.eight_bit_clean)
236 return is_7bit_printable (c);
238 if (mc_global.full_eight_bits)
240 return is_8bit_printable (c);
242 else
243 return is_iso_printable (c);
244 #endif /* !HAVE_CHARSET */
247 /* --------------------------------------------------------------------------------------------- */
249 * Quote the filename for the purpose of inserting it into the command
250 * line. If quote_percent is 1, replace "%" with "%%" - the percent is
251 * processed by the mc command line.
253 char *
254 name_quote (const char *s, int quote_percent)
256 char *ret, *d;
258 d = ret = g_malloc (strlen (s) * 2 + 2 + 1);
259 if (*s == '-')
261 *d++ = '.';
262 *d++ = '/';
265 for (; *s; s++, d++)
267 switch (*s)
269 case '%':
270 if (quote_percent)
271 *d++ = '%';
272 break;
273 case '\'':
274 case '\\':
275 case '\r':
276 case '\n':
277 case '\t':
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 case '(':
295 case ')':
296 *d++ = '\\';
297 break;
298 case '~':
299 case '#':
300 if (d == ret)
301 *d++ = '\\';
302 break;
304 *d = *s;
306 *d = '\0';
307 return ret;
310 /* --------------------------------------------------------------------------------------------- */
312 char *
313 fake_name_quote (const char *s, int 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 = "";
352 if (size > 999999999UL)
354 divisor = use_si ? 1000 : 1024;
355 xtra = use_si ? "k" : "K";
357 if (size / divisor > 999999999UL)
359 divisor = use_si ? (1000 * 1000) : (1024 * 1024);
360 xtra = use_si ? "m" : "M";
362 if (size / divisor > 999999999UL)
364 divisor = use_si ? (1000 * 1000 * 1000) : (1024 * 1024 * 1024);
365 xtra = use_si ? "g" : "G";
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 while (p >= y && isalpha ((unsigned char) *p))
388 *d-- = *p--;
389 for (count = 0; p >= y; count++)
391 if (count == 3)
393 *d-- = ',';
394 count = 0;
396 *d-- = *p--;
398 d++;
399 if (*d == ',')
400 d++;
401 return d;
404 /* --------------------------------------------------------------------------------------------- */
406 * Print file SIZE to BUFFER, but don't exceed LEN characters,
407 * not including trailing 0. BUFFER should be at least LEN+1 long.
408 * This function is called for every file on panels, so avoid
409 * floating point by any means.
411 * Units: size units (filesystem sizes are 1K blocks)
412 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
415 void
416 size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gboolean use_si)
418 /* Avoid taking power for every file. */
419 /* *INDENT-OFF* */
420 static const uintmax_t power10[] = {
421 /* we hope that size of uintmax_t is 4 bytes at least */
422 1ULL,
423 10ULL,
424 100ULL,
425 1000ULL,
426 10000ULL,
427 100000ULL,
428 1000000ULL,
429 10000000ULL,
430 100000000ULL,
431 1000000000ULL
432 /* maximmum value of uintmax_t (in case of 4 bytes) is
433 4294967295
435 #if SIZEOF_UINTMAX_T == 8
437 10000000000ULL,
438 100000000000ULL,
439 1000000000000ULL,
440 10000000000000ULL,
441 100000000000000ULL,
442 1000000000000000ULL,
443 10000000000000000ULL,
444 100000000000000000ULL,
445 1000000000000000000ULL,
446 10000000000000000000ULL
447 /* maximmum value of uintmax_t (in case of 8 bytes) is
448 18447644073710439615
450 #endif
452 /* *INDENT-ON* */
453 static const char *const suffix[] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL };
454 static const char *const suffix_lc[] = { "", "k", "m", "g", "t", "p", "e", "z", "y", NULL };
455 int j = 0;
457 if (len == 0)
458 len = 9;
459 #if SIZEOF_UINTMAX_T == 8
460 /* 20 decimal digits are required to represent 8 bytes */
461 else if (len > 19)
462 len = 19;
463 #else
464 /* 10 decimal digits are required to represent 4 bytes */
465 else if (len > 9)
466 len = 9;
467 #endif
470 * recalculate from 1024 base to 1000 base if units>0
471 * We can't just multiply by 1024 - that might cause overflow
472 * if off_t type is too small
474 if (use_si)
475 for (j = 0; j < units; j++)
477 uintmax_t size_remain;
479 size_remain = ((size % 125) * 1024) / 1000; /* size mod 125, recalculated */
480 size = size / 125; /* 128/125 = 1024/1000 */
481 size = size * 128; /* This will convert size from multiple of 1024 to multiple of 1000 */
482 size += size_remain; /* Re-add remainder lost by division/multiplication */
485 for (j = units; suffix[j] != NULL; j++)
487 if (size == 0)
489 if (j == units)
491 /* Empty files will print "0" even with minimal width. */
492 g_snprintf (buffer, len + 1, "0");
493 break;
496 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
497 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s",
498 (j > 1) ? (use_si ? suffix_lc[j - 1] : suffix[j - 1]) : "B");
499 break;
502 if (size < power10[len - (j > 0 ? 1 : 0)])
504 g_snprintf (buffer, len + 1, "%" PRIuMAX "%s", size, use_si ? suffix_lc[j] : suffix[j]);
505 break;
508 /* Powers of 1000 or 1024, with rounding. */
509 if (use_si)
510 size = (size + 500) / 1000;
511 else
512 size = (size + 512) >> 10;
516 /* --------------------------------------------------------------------------------------------- */
518 const char *
519 string_perm (mode_t mode_bits)
521 static char mode[11];
523 strcpy (mode, "----------");
524 if (S_ISDIR (mode_bits))
525 mode[0] = 'd';
526 if (S_ISCHR (mode_bits))
527 mode[0] = 'c';
528 if (S_ISBLK (mode_bits))
529 mode[0] = 'b';
530 if (S_ISLNK (mode_bits))
531 mode[0] = 'l';
532 if (S_ISFIFO (mode_bits))
533 mode[0] = 'p';
534 if (S_ISNAM (mode_bits))
535 mode[0] = 'n';
536 if (S_ISSOCK (mode_bits))
537 mode[0] = 's';
538 if (S_ISDOOR (mode_bits))
539 mode[0] = 'D';
540 if (ismode (mode_bits, S_IXOTH))
541 mode[9] = 'x';
542 if (ismode (mode_bits, S_IWOTH))
543 mode[8] = 'w';
544 if (ismode (mode_bits, S_IROTH))
545 mode[7] = 'r';
546 if (ismode (mode_bits, S_IXGRP))
547 mode[6] = 'x';
548 if (ismode (mode_bits, S_IWGRP))
549 mode[5] = 'w';
550 if (ismode (mode_bits, S_IRGRP))
551 mode[4] = 'r';
552 if (ismode (mode_bits, S_IXUSR))
553 mode[3] = 'x';
554 if (ismode (mode_bits, S_IWUSR))
555 mode[2] = 'w';
556 if (ismode (mode_bits, S_IRUSR))
557 mode[1] = 'r';
558 #ifdef S_ISUID
559 if (ismode (mode_bits, S_ISUID))
560 mode[3] = (mode[3] == 'x') ? 's' : 'S';
561 #endif /* S_ISUID */
562 #ifdef S_ISGID
563 if (ismode (mode_bits, S_ISGID))
564 mode[6] = (mode[6] == 'x') ? 's' : 'S';
565 #endif /* S_ISGID */
566 #ifdef S_ISVTX
567 if (ismode (mode_bits, S_ISVTX))
568 mode[9] = (mode[9] == 'x') ? 't' : 'T';
569 #endif /* S_ISVTX */
570 return mode;
573 /* --------------------------------------------------------------------------------------------- */
575 const char *
576 extension (const char *filename)
578 const char *d = strrchr (filename, '.');
579 return (d != NULL) ? d + 1 : "";
582 /* --------------------------------------------------------------------------------------------- */
584 char *
585 load_mc_home_file (const char *from, const char *filename, char **allocated_filename)
587 char *hintfile_base, *hintfile;
588 char *lang;
589 char *data;
591 hintfile_base = g_build_filename (from, filename, (char *) NULL);
592 lang = guess_message_value ();
594 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
595 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
597 /* Fall back to the two-letter language code */
598 if (lang[0] != '\0' && lang[1] != '\0')
599 lang[2] = '\0';
600 g_free (hintfile);
601 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
602 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
604 g_free (hintfile);
605 hintfile = hintfile_base;
606 g_file_get_contents (hintfile_base, &data, NULL, NULL);
610 g_free (lang);
612 if (hintfile != hintfile_base)
613 g_free (hintfile_base);
615 if (allocated_filename != NULL)
616 *allocated_filename = hintfile;
617 else
618 g_free (hintfile);
620 return data;
623 /* --------------------------------------------------------------------------------------------- */
625 const char *
626 extract_line (const char *s, const char *top)
628 static char tmp_line[BUF_MEDIUM];
629 char *t = tmp_line;
631 while (*s && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line) - 1 && s < top)
632 *t++ = *s++;
633 *t = 0;
634 return tmp_line;
637 /* --------------------------------------------------------------------------------------------- */
639 * The basename routine
642 const char *
643 x_basename (const char *s)
645 const char *url_delim, *path_sep;
647 url_delim = g_strrstr (s, VFS_PATH_URL_DELIMITER);
648 path_sep = strrchr (s, PATH_SEP);
650 if (url_delim == NULL
651 || url_delim < path_sep - strlen (VFS_PATH_URL_DELIMITER)
652 || url_delim - s + strlen (VFS_PATH_URL_DELIMITER) < strlen (s))
654 /* avoid trailing PATH_SEP, if present */
655 if (s[strlen (s) - 1] == PATH_SEP)
657 while (--path_sep > s && *path_sep != PATH_SEP);
658 return (path_sep != s) ? path_sep + 1 : s;
660 else
661 return (path_sep != NULL) ? path_sep + 1 : s;
664 while (--url_delim > s && *url_delim != PATH_SEP);
665 while (--url_delim > s && *url_delim != PATH_SEP);
667 return (url_delim == s) ? s : url_delim + 1;
670 /* --------------------------------------------------------------------------------------------- */
672 const char *
673 unix_error_string (int error_num)
675 static char buffer[BUF_LARGE];
676 gchar *strerror_currentlocale;
678 strerror_currentlocale = g_locale_from_utf8 (g_strerror (error_num), -1, NULL, NULL, NULL);
679 g_snprintf (buffer, sizeof (buffer), "%s (%d)", strerror_currentlocale, error_num);
680 g_free (strerror_currentlocale);
682 return buffer;
685 /* --------------------------------------------------------------------------------------------- */
687 const char *
688 skip_separators (const char *s)
690 const char *su = s;
692 for (; *su; str_cnext_char (&su))
693 if (*su != ' ' && *su != '\t' && *su != ',')
694 break;
696 return su;
699 /* --------------------------------------------------------------------------------------------- */
701 const char *
702 skip_numbers (const char *s)
704 const char *su = s;
706 for (; *su; str_cnext_char (&su))
707 if (!str_isdigit (su))
708 break;
710 return su;
713 /* --------------------------------------------------------------------------------------------- */
715 * Remove all control sequences from the argument string. We define
716 * "control sequence", in a sort of pidgin BNF, as follows:
718 * control-seq = Esc non-'['
719 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
721 * This scheme works for all the terminals described in my termcap /
722 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
723 * terminals. If I hear from a single person who uses such a terminal
724 * with MC, I'll be glad to add support for it. (Dugan)
725 * Non-printable characters are also removed.
728 char *
729 strip_ctrl_codes (char *s)
731 char *w; /* Current position where the stripped data is written */
732 char *r; /* Current position where the original data is read */
733 char *n;
735 if (!s)
736 return 0;
738 for (w = s, r = s; *r;)
740 if (*r == ESC_CHAR)
742 /* Skip the control sequence's arguments */ ;
743 /* '(' need to avoid strange 'B' letter in *Suse (if mc runs under root user) */
744 if (*(++r) == '[' || *r == '(')
746 /* strchr() matches trailing binary 0 */
747 while (*(++r) && strchr ("0123456789;?", *r));
749 else if (*r == ']')
752 * Skip xterm's OSC (Operating System Command)
753 * http://www.xfree86.org/current/ctlseqs.html
754 * OSC P s ; P t ST
755 * OSC P s ; P t BEL
757 char *new_r = r;
759 for (; *new_r; ++new_r)
761 switch (*new_r)
763 /* BEL */
764 case '\a':
765 r = new_r;
766 goto osc_out;
767 case ESC_CHAR:
768 /* ST */
769 if (*(new_r + 1) == '\\')
771 r = new_r + 1;
772 goto osc_out;
776 osc_out:;
780 * Now we are at the last character of the sequence.
781 * Skip it unless it's binary 0.
783 if (*r)
784 r++;
785 continue;
788 n = str_get_next_char (r);
789 if (str_isprint (r))
791 memmove (w, r, n - r);
792 w += n - r;
794 r = n;
796 *w = 0;
797 return s;
800 /* --------------------------------------------------------------------------------------------- */
802 enum compression_type
803 get_compression_type (int fd, const char *name)
805 unsigned char magic[16];
806 size_t str_len;
808 /* Read the magic signature */
809 if (mc_read (fd, (char *) magic, 4) != 4)
810 return COMPRESSION_NONE;
812 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
813 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236))
815 return COMPRESSION_GZIP;
818 /* PKZIP_MAGIC */
819 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003 && magic[3] == 004)
821 /* Read compression type */
822 mc_lseek (fd, 8, SEEK_SET);
823 if (mc_read (fd, (char *) magic, 2) != 2)
824 return COMPRESSION_NONE;
826 /* Gzip can handle only deflated (8) or stored (0) files */
827 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
828 return COMPRESSION_NONE;
830 /* Compatible with gzip */
831 return COMPRESSION_GZIP;
834 /* PACK_MAGIC and LZH_MAGIC and compress magic */
835 if (magic[0] == 037 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235))
837 /* Compatible with gzip */
838 return COMPRESSION_GZIP;
841 /* BZIP and BZIP2 files */
842 if ((magic[0] == 'B') && (magic[1] == 'Z') && (magic[3] >= '1') && (magic[3] <= '9'))
844 switch (magic[2])
846 case '0':
847 return COMPRESSION_BZIP;
848 case 'h':
849 return COMPRESSION_BZIP2;
853 /* Support for LZMA (only utils format with magic in header).
854 * This is the default format of LZMA utils 4.32.1 and later. */
856 if (mc_read (fd, (char *) magic + 4, 2) != 2)
857 return COMPRESSION_NONE;
859 /* LZMA utils format */
860 if (magic[0] == 0xFF
861 && magic[1] == 'L'
862 && magic[2] == 'Z' && magic[3] == 'M' && magic[4] == 'A' && magic[5] == 0x00)
863 return COMPRESSION_LZMA;
865 /* XZ compression magic */
866 if (magic[0] == 0xFD
867 && magic[1] == 0x37
868 && magic[2] == 0x7A && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00)
869 return COMPRESSION_XZ;
871 str_len = strlen (name);
872 /* HACK: we must belive to extention of LZMA file :) ... */
873 if ((str_len > 5 && strcmp (&name[str_len - 5], ".lzma") == 0) ||
874 (str_len > 4 && strcmp (&name[str_len - 4], ".tlz") == 0))
875 return COMPRESSION_LZMA;
877 return COMPRESSION_NONE;
880 /* --------------------------------------------------------------------------------------------- */
882 const char *
883 decompress_extension (int type)
885 switch (type)
887 case COMPRESSION_GZIP:
888 return "/ugz" VFS_PATH_URL_DELIMITER;
889 case COMPRESSION_BZIP:
890 return "/ubz" VFS_PATH_URL_DELIMITER;
891 case COMPRESSION_BZIP2:
892 return "/ubz2" VFS_PATH_URL_DELIMITER;
893 case COMPRESSION_LZMA:
894 return "/ulzma" VFS_PATH_URL_DELIMITER;
895 case COMPRESSION_XZ:
896 return "/uxz" VFS_PATH_URL_DELIMITER;
898 /* Should never reach this place */
899 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
900 return 0;
903 /* --------------------------------------------------------------------------------------------- */
905 void
906 wipe_password (char *passwd)
908 char *p = passwd;
910 if (!p)
911 return;
912 for (; *p; p++)
913 *p = 0;
914 g_free (passwd);
917 /* --------------------------------------------------------------------------------------------- */
919 * Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key
920 * @returns a newly allocated string
923 char *
924 convert_controls (const char *p)
926 char *valcopy = g_strdup (p);
927 char *q;
929 /* Parse the escape special character */
930 for (q = valcopy; *p;)
932 if (*p == '\\')
934 p++;
935 if ((*p == 'e') || (*p == 'E'))
937 p++;
938 *q++ = ESC_CHAR;
941 else
943 if (*p == '^')
945 p++;
946 if (*p == '^')
947 *q++ = *p++;
948 else
950 char c = (*p | 0x20);
951 if (c >= 'a' && c <= 'z')
953 *q++ = c - 'a' + 1;
954 p++;
956 else if (*p)
957 p++;
960 else
961 *q++ = *p++;
964 *q = 0;
965 return valcopy;
968 /* --------------------------------------------------------------------------------------------- */
970 * Finds out a relative path from first to second, i.e. goes as many ..
971 * as needed up in first and then goes down using second
974 char *
975 diff_two_paths (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
977 char *p, *q, *r, *s, *buf = NULL;
978 int i, j, prevlen = -1, currlen;
979 char *my_first = NULL, *my_second = NULL;
980 char *path_str;
982 path_str = vfs_path_to_str (vpath1);
983 my_first = resolve_symlinks (path_str);
984 g_free (path_str);
985 if (my_first == NULL)
986 return NULL;
988 path_str = vfs_path_to_str (vpath2);
989 my_second = resolve_symlinks (path_str);
990 g_free (path_str);
991 if (my_second == NULL)
993 g_free (my_first);
994 return NULL;
997 for (j = 0; j < 2; j++)
999 p = my_first;
1000 q = my_second;
1001 for (;;)
1003 r = strchr (p, PATH_SEP);
1004 s = strchr (q, PATH_SEP);
1005 if (!r || !s)
1006 break;
1007 *r = 0;
1008 *s = 0;
1009 if (strcmp (p, q))
1011 *r = PATH_SEP;
1012 *s = PATH_SEP;
1013 break;
1015 else
1017 *r = PATH_SEP;
1018 *s = PATH_SEP;
1020 p = r + 1;
1021 q = s + 1;
1023 p--;
1024 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++);
1025 currlen = (i + 1) * 3 + strlen (q) + 1;
1026 if (j)
1028 if (currlen < prevlen)
1029 g_free (buf);
1030 else
1032 g_free (my_first);
1033 g_free (my_second);
1034 return buf;
1037 p = buf = g_malloc (currlen);
1038 prevlen = currlen;
1039 for (; i >= 0; i--, p += 3)
1040 strcpy (p, "../");
1041 strcpy (p, q);
1043 g_free (my_first);
1044 g_free (my_second);
1045 return buf;
1048 /* --------------------------------------------------------------------------------------------- */
1050 * Append text to GList, remove all entries with the same text
1053 GList *
1054 list_append_unique (GList * list, char *text)
1056 GList *lc_link;
1059 * Go to the last position and traverse the list backwards
1060 * starting from the second last entry to make sure that we
1061 * are not removing the current link.
1063 list = g_list_append (list, text);
1064 list = g_list_last (list);
1065 lc_link = g_list_previous (list);
1067 while (lc_link != NULL)
1069 GList *newlink;
1071 newlink = g_list_previous (lc_link);
1072 if (strcmp ((char *) lc_link->data, text) == 0)
1074 GList *tmp;
1076 g_free (lc_link->data);
1077 tmp = g_list_remove_link (list, lc_link);
1078 g_list_free_1 (lc_link);
1080 lc_link = newlink;
1083 return list;
1086 /* --------------------------------------------------------------------------------------------- */
1088 * Read and restore position for the given filename.
1089 * If there is no stored data, return line 1 and col 0.
1092 void
1093 load_file_position (const vfs_path_t * filename_vpath, long *line, long *column, off_t * offset,
1094 GArray ** bookmarks)
1096 char *fn;
1097 FILE *f;
1098 char buf[MC_MAXPATHLEN + 100];
1099 const size_t len = vfs_path_len (filename_vpath);
1100 char *filename;
1102 /* defaults */
1103 *line = 1;
1104 *column = 0;
1105 *offset = 0;
1107 /* open file with positions */
1108 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1109 f = fopen (fn, "r");
1110 g_free (fn);
1111 if (f == NULL)
1112 return;
1114 /* prepare array for serialized bookmarks */
1115 *bookmarks = g_array_sized_new (FALSE, FALSE, sizeof (size_t), MAX_SAVED_BOOKMARKS);
1116 filename = vfs_path_to_str (filename_vpath);
1118 while (fgets (buf, sizeof (buf), f) != NULL)
1120 const char *p;
1121 gchar **pos_tokens;
1123 /* check if the filename matches the beginning of string */
1124 if (strncmp (buf, filename, len) != 0)
1125 continue;
1127 /* followed by single space */
1128 if (buf[len] != ' ')
1129 continue;
1131 /* and string without spaces */
1132 p = &buf[len + 1];
1133 if (strchr (p, ' ') != NULL)
1134 continue;
1136 pos_tokens = g_strsplit (p, ";", 3 + MAX_SAVED_BOOKMARKS);
1137 if (pos_tokens[0] == NULL)
1139 *line = 1;
1140 *column = 0;
1141 *offset = 0;
1143 else
1145 *line = strtol (pos_tokens[0], NULL, 10);
1146 if (pos_tokens[1] == NULL)
1148 *column = 0;
1149 *offset = 0;
1151 else
1153 *column = strtol (pos_tokens[1], NULL, 10);
1154 if (pos_tokens[2] == NULL)
1155 *offset = 0;
1156 else
1158 size_t i;
1160 *offset = strtoll (pos_tokens[2], NULL, 10);
1162 for (i = 0; i < MAX_SAVED_BOOKMARKS && pos_tokens[3 + i] != NULL; i++)
1164 size_t val;
1166 val = strtoul (pos_tokens[3 + i], NULL, 10);
1167 g_array_append_val (*bookmarks, val);
1173 g_strfreev (pos_tokens);
1176 g_free (filename);
1177 fclose (f);
1180 /* --------------------------------------------------------------------------------------------- */
1182 * Save position for the given file
1185 void
1186 save_file_position (const vfs_path_t * filename_vpath, long line, long column, off_t offset,
1187 GArray * bookmarks)
1189 static size_t filepos_max_saved_entries = 0;
1190 char *fn, *tmp_fn;
1191 FILE *f, *tmp_f;
1192 char buf[MC_MAXPATHLEN + 100];
1193 size_t i;
1194 const size_t len = vfs_path_len (filename_vpath);
1195 gboolean src_error = FALSE;
1196 char *filename;
1198 if (filepos_max_saved_entries == 0)
1199 filepos_max_saved_entries = mc_config_get_int (mc_main_config, CONFIG_APP_SECTION,
1200 "filepos_max_saved_entries", 1024);
1202 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1203 if (fn == NULL)
1204 goto early_error;
1206 mc_util_make_backup_if_possible (fn, TMP_SUFFIX);
1208 /* open file */
1209 f = fopen (fn, "w");
1210 if (f == NULL)
1211 goto open_target_error;
1213 tmp_fn = g_strdup_printf ("%s" TMP_SUFFIX, fn);
1214 tmp_f = fopen (tmp_fn, "r");
1215 if (tmp_f == NULL)
1217 src_error = TRUE;
1218 goto open_source_error;
1221 filename = vfs_path_to_str (filename_vpath);
1222 /* put the new record */
1223 if (line != 1 || column != 0 || bookmarks != NULL)
1225 if (fprintf (f, "%s %ld;%ld;%" PRIuMAX, filename, line, column, (uintmax_t) offset) < 0)
1226 goto write_position_error;
1227 if (bookmarks != NULL)
1228 for (i = 0; i < bookmarks->len && i < MAX_SAVED_BOOKMARKS; i++)
1229 if (fprintf (f, ";%zu", g_array_index (bookmarks, size_t, i)) < 0)
1230 goto write_position_error;
1232 if (fprintf (f, "\n") < 0)
1233 goto write_position_error;
1236 i = 1;
1237 while (fgets (buf, sizeof (buf), tmp_f) != NULL)
1239 if (buf[len] == ' ' && strncmp (buf, filename, len) == 0
1240 && strchr (&buf[len + 1], ' ') == NULL)
1241 continue;
1243 fprintf (f, "%s", buf);
1244 if (++i > filepos_max_saved_entries)
1245 break;
1248 write_position_error:
1249 g_free (filename);
1250 fclose (tmp_f);
1251 open_source_error:
1252 g_free (tmp_fn);
1253 fclose (f);
1254 if (src_error)
1255 mc_util_restore_from_backup_if_possible (fn, TMP_SUFFIX);
1256 else
1257 mc_util_unlink_backup_if_possible (fn, TMP_SUFFIX);
1258 open_target_error:
1259 g_free (fn);
1260 early_error:
1261 if (bookmarks != NULL)
1262 g_array_free (bookmarks, TRUE);
1265 /* --------------------------------------------------------------------------------------------- */
1267 extern int
1268 ascii_alpha_to_cntrl (int ch)
1270 if ((ch >= ASCII_A && ch <= ASCII_Z) || (ch >= ASCII_a && ch <= ASCII_z))
1272 ch &= 0x1f;
1274 return ch;
1277 /* --------------------------------------------------------------------------------------------- */
1279 const char *
1280 Q_ (const char *s)
1282 const char *result, *sep;
1284 result = _(s);
1285 sep = strchr (result, '|');
1286 return (sep != NULL) ? sep + 1 : result;
1289 /* --------------------------------------------------------------------------------------------- */
1291 gboolean
1292 mc_util_make_backup_if_possible (const char *file_name, const char *backup_suffix)
1294 struct stat stat_buf;
1295 char *backup_path;
1296 gboolean ret;
1297 if (!exist_file (file_name))
1298 return FALSE;
1300 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1302 if (backup_path == NULL)
1303 return FALSE;
1305 ret = mc_util_write_backup_content (file_name, backup_path);
1307 if (ret)
1309 /* Backup file will have same ownership with main file. */
1310 if (stat (file_name, &stat_buf) == 0)
1311 chmod (backup_path, stat_buf.st_mode);
1312 else
1313 chmod (backup_path, S_IRUSR | S_IWUSR);
1316 g_free (backup_path);
1318 return ret;
1321 /* --------------------------------------------------------------------------------------------- */
1323 gboolean
1324 mc_util_restore_from_backup_if_possible (const char *file_name, const char *backup_suffix)
1326 gboolean ret;
1327 char *backup_path;
1329 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 (backup_path, file_name);
1334 g_free (backup_path);
1336 return ret;
1339 /* --------------------------------------------------------------------------------------------- */
1341 gboolean
1342 mc_util_unlink_backup_if_possible (const char *file_name, const char *backup_suffix)
1344 char *backup_path;
1346 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1347 if (backup_path == NULL)
1348 return FALSE;
1350 if (exist_file (backup_path))
1352 vfs_path_t *vpath;
1354 vpath = vfs_path_from_str (backup_path);
1355 mc_unlink (vpath);
1356 vfs_path_free (vpath);
1359 g_free (backup_path);
1360 return TRUE;
1363 /* --------------------------------------------------------------------------------------------- */
1365 * partly taken from dcigettext.c, returns "" for default locale
1366 * value should be freed by calling function g_free()
1369 char *
1370 guess_message_value (void)
1372 static const char *const var[] = {
1373 /* Setting of LC_ALL overwrites all other. */
1374 /* Do not use LANGUAGE for check user locale and drowing hints */
1375 "LC_ALL",
1376 /* Next comes the name of the desired category. */
1377 "LC_MESSAGES",
1378 /* Last possibility is the LANG environment variable. */
1379 "LANG",
1380 /* NULL exit loops */
1381 NULL
1384 unsigned i = 0;
1385 const char *locale = NULL;
1387 while (var[i] != NULL)
1389 locale = getenv (var[i]);
1390 if (locale != NULL && locale[0] != '\0')
1391 break;
1392 i++;
1395 if (locale == NULL)
1396 locale = "";
1398 return g_strdup (locale);
1401 /* --------------------------------------------------------------------------------------------- */