changed interface of function mc_setctl() for handle vfs_path_t object as parameter
[midnight-commander.git] / lib / util.c
blobf6bedfc5d4a31fc17e22d2337086d71b9e4a264a
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 (path, 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 char *secure_path = strip_password (g_strdup (path), 1);
331 const char *ret = str_trunc (secure_path, trunc_len);
332 g_free (secure_path);
334 return ret;
337 /* --------------------------------------------------------------------------------------------- */
339 const char *
340 size_trunc (uintmax_t size, gboolean use_si)
342 static char x[BUF_TINY];
343 uintmax_t divisor = 1;
344 const char *xtra = "";
346 if (size > 999999999UL)
348 divisor = use_si ? 1000 : 1024;
349 xtra = use_si ? "k" : "K";
351 if (size / divisor > 999999999UL)
353 divisor = use_si ? (1000 * 1000) : (1024 * 1024);
354 xtra = use_si ? "m" : "M";
356 if (size / divisor > 999999999UL)
358 divisor = use_si ? (1000 * 1000 * 1000) : (1024 * 1024 * 1024);
359 xtra = use_si ? "g" : "G";
363 g_snprintf (x, sizeof (x), "%.0f%s", 1.0 * size / divisor, xtra);
364 return x;
367 /* --------------------------------------------------------------------------------------------- */
369 const char *
370 size_trunc_sep (uintmax_t size, gboolean use_si)
372 static char x[60];
373 int count;
374 const char *p, *y;
375 char *d;
377 p = y = size_trunc (size, use_si);
378 p += strlen (p) - 1;
379 d = x + sizeof (x) - 1;
380 *d-- = '\0';
381 while (p >= y && isalpha ((unsigned char) *p))
382 *d-- = *p--;
383 for (count = 0; p >= y; count++)
385 if (count == 3)
387 *d-- = ',';
388 count = 0;
390 *d-- = *p--;
392 d++;
393 if (*d == ',')
394 d++;
395 return d;
398 /* --------------------------------------------------------------------------------------------- */
400 * Print file SIZE to BUFFER, but don't exceed LEN characters,
401 * not including trailing 0. BUFFER should be at least LEN+1 long.
402 * This function is called for every file on panels, so avoid
403 * floating point by any means.
405 * Units: size units (filesystem sizes are 1K blocks)
406 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
409 void
410 size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gboolean use_si)
412 /* Avoid taking power for every file. */
413 /* *INDENT-OFF* */
414 static const uintmax_t power10[] = {
415 /* we hope that size of uintmax_t is 4 bytes at least */
416 1ULL,
417 10ULL,
418 100ULL,
419 1000ULL,
420 10000ULL,
421 100000ULL,
422 1000000ULL,
423 10000000ULL,
424 100000000ULL,
425 1000000000ULL
426 /* maximmum value of uintmax_t (in case of 4 bytes) is
427 4294967295
429 #if SIZEOF_UINTMAX_T == 8
431 10000000000ULL,
432 100000000000ULL,
433 1000000000000ULL,
434 10000000000000ULL,
435 100000000000000ULL,
436 1000000000000000ULL,
437 10000000000000000ULL,
438 100000000000000000ULL,
439 1000000000000000000ULL,
440 10000000000000000000ULL
441 /* maximmum value of uintmax_t (in case of 8 bytes) is
442 18447644073710439615
444 #endif
446 /* *INDENT-ON* */
447 static const char *const suffix[] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL };
448 static const char *const suffix_lc[] = { "", "k", "m", "g", "t", "p", "e", "z", "y", NULL };
449 int j = 0;
451 if (len == 0)
452 len = 9;
453 #if SIZEOF_UINTMAX_T == 8
454 /* 20 decimal digits are required to represent 8 bytes */
455 else if (len > 19)
456 len = 19;
457 #else
458 /* 10 decimal digits are required to represent 4 bytes */
459 else if (len > 9)
460 len = 9;
461 #endif
464 * recalculate from 1024 base to 1000 base if units>0
465 * We can't just multiply by 1024 - that might cause overflow
466 * if off_t type is too small
468 if (use_si)
469 for (j = 0; j < units; j++)
471 uintmax_t size_remain;
473 size_remain = ((size % 125) * 1024) / 1000; /* size mod 125, recalculated */
474 size = size / 125; /* 128/125 = 1024/1000 */
475 size = size * 128; /* This will convert size from multiple of 1024 to multiple of 1000 */
476 size += size_remain; /* Re-add remainder lost by division/multiplication */
479 for (j = units; suffix[j] != NULL; j++)
481 if (size == 0)
483 if (j == units)
485 /* Empty files will print "0" even with minimal width. */
486 g_snprintf (buffer, len + 1, "0");
487 break;
490 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
491 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s",
492 (j > 1) ? (use_si ? suffix_lc[j - 1] : suffix[j - 1]) : "B");
493 break;
496 if (size < power10[len - (j > 0 ? 1 : 0)])
498 g_snprintf (buffer, len + 1, "%" PRIuMAX "%s", size, use_si ? suffix_lc[j] : suffix[j]);
499 break;
502 /* Powers of 1000 or 1024, with rounding. */
503 if (use_si)
504 size = (size + 500) / 1000;
505 else
506 size = (size + 512) >> 10;
510 /* --------------------------------------------------------------------------------------------- */
512 const char *
513 string_perm (mode_t mode_bits)
515 static char mode[11];
517 strcpy (mode, "----------");
518 if (S_ISDIR (mode_bits))
519 mode[0] = 'd';
520 if (S_ISCHR (mode_bits))
521 mode[0] = 'c';
522 if (S_ISBLK (mode_bits))
523 mode[0] = 'b';
524 if (S_ISLNK (mode_bits))
525 mode[0] = 'l';
526 if (S_ISFIFO (mode_bits))
527 mode[0] = 'p';
528 if (S_ISNAM (mode_bits))
529 mode[0] = 'n';
530 if (S_ISSOCK (mode_bits))
531 mode[0] = 's';
532 if (S_ISDOOR (mode_bits))
533 mode[0] = 'D';
534 if (ismode (mode_bits, S_IXOTH))
535 mode[9] = 'x';
536 if (ismode (mode_bits, S_IWOTH))
537 mode[8] = 'w';
538 if (ismode (mode_bits, S_IROTH))
539 mode[7] = 'r';
540 if (ismode (mode_bits, S_IXGRP))
541 mode[6] = 'x';
542 if (ismode (mode_bits, S_IWGRP))
543 mode[5] = 'w';
544 if (ismode (mode_bits, S_IRGRP))
545 mode[4] = 'r';
546 if (ismode (mode_bits, S_IXUSR))
547 mode[3] = 'x';
548 if (ismode (mode_bits, S_IWUSR))
549 mode[2] = 'w';
550 if (ismode (mode_bits, S_IRUSR))
551 mode[1] = 'r';
552 #ifdef S_ISUID
553 if (ismode (mode_bits, S_ISUID))
554 mode[3] = (mode[3] == 'x') ? 's' : 'S';
555 #endif /* S_ISUID */
556 #ifdef S_ISGID
557 if (ismode (mode_bits, S_ISGID))
558 mode[6] = (mode[6] == 'x') ? 's' : 'S';
559 #endif /* S_ISGID */
560 #ifdef S_ISVTX
561 if (ismode (mode_bits, S_ISVTX))
562 mode[9] = (mode[9] == 'x') ? 't' : 'T';
563 #endif /* S_ISVTX */
564 return mode;
567 /* --------------------------------------------------------------------------------------------- */
569 * p: string which might contain an url with a password (this parameter is
570 * modified in place).
571 * has_prefix = 0: The first parameter is an url without a prefix
572 * (user[:pass]@]machine[:port][remote-dir). Delete
573 * the password.
574 * has_prefix = 1: Search p for known url prefixes. If found delete
575 * the password from the url.
576 * Caveat: only the first url is found
579 char *
580 strip_password (char *p, int has_prefix)
582 static const struct
584 const char *name;
585 size_t len;
586 } prefixes[] =
588 /* *INDENT-OFF* */
589 { "/#ftp:", 6 },
590 { "ftp://", 6 },
591 { "/#smb:", 6 },
592 { "smb://", 6 },
593 { "/#sh:", 5 },
594 { "sh://", 5 },
595 { "ssh://", 6 }
596 /* *INDENT-ON* */
599 char *at, *inner_colon, *dir;
600 size_t i;
601 char *result = p;
603 for (i = 0; i < sizeof (prefixes) / sizeof (prefixes[0]); i++)
605 char *q;
607 if (has_prefix)
609 q = strstr (p, prefixes[i].name);
610 if (q == NULL)
611 continue;
612 else
613 p = q + prefixes[i].len;
616 dir = strchr (p, PATH_SEP);
617 if (dir != NULL)
618 *dir = '\0';
620 /* search for any possible user */
621 at = strrchr (p, '@');
623 if (dir)
624 *dir = PATH_SEP;
626 /* We have a username */
627 if (at)
629 inner_colon = memchr (p, ':', at - p);
630 if (inner_colon)
631 memmove (inner_colon, at, strlen (at) + 1);
633 break;
635 return (result);
638 /* --------------------------------------------------------------------------------------------- */
640 const char *
641 strip_home_and_password (const char *dir)
643 size_t len;
644 static char newdir[MC_MAXPATHLEN];
646 len = strlen (mc_config_get_home_dir ());
647 if (mc_config_get_home_dir () != NULL && strncmp (dir, mc_config_get_home_dir (), len) == 0 &&
648 (dir[len] == PATH_SEP || dir[len] == '\0'))
650 newdir[0] = '~';
651 g_strlcpy (&newdir[1], &dir[len], sizeof (newdir) - 1);
652 return newdir;
655 /* We do not strip homes in /#ftp tree, I do not like ~'s there
656 (see ftpfs.c why) */
657 g_strlcpy (newdir, dir, sizeof (newdir));
658 strip_password (newdir, 1);
659 return newdir;
662 /* --------------------------------------------------------------------------------------------- */
664 const char *
665 extension (const char *filename)
667 const char *d = strrchr (filename, '.');
668 return (d != NULL) ? d + 1 : "";
671 /* --------------------------------------------------------------------------------------------- */
673 char *
674 load_mc_home_file (const char *from, const char *filename, char **allocated_filename)
676 char *hintfile_base, *hintfile;
677 char *lang;
678 char *data;
680 hintfile_base = g_build_filename (from, filename, (char *) NULL);
681 lang = guess_message_value ();
683 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
684 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
686 /* Fall back to the two-letter language code */
687 if (lang[0] != '\0' && lang[1] != '\0')
688 lang[2] = '\0';
689 g_free (hintfile);
690 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
691 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
693 g_free (hintfile);
694 hintfile = hintfile_base;
695 g_file_get_contents (hintfile_base, &data, NULL, NULL);
699 g_free (lang);
701 if (hintfile != hintfile_base)
702 g_free (hintfile_base);
704 if (allocated_filename != NULL)
705 *allocated_filename = hintfile;
706 else
707 g_free (hintfile);
709 return data;
712 /* --------------------------------------------------------------------------------------------- */
714 const char *
715 extract_line (const char *s, const char *top)
717 static char tmp_line[BUF_MEDIUM];
718 char *t = tmp_line;
720 while (*s && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line) - 1 && s < top)
721 *t++ = *s++;
722 *t = 0;
723 return tmp_line;
726 /* --------------------------------------------------------------------------------------------- */
728 * The basename routine
731 const char *
732 x_basename (const char *s)
734 const char *url_delim, *path_sep;
736 url_delim = g_strrstr (s, VFS_PATH_URL_DELIMITER);
737 path_sep = strrchr (s, PATH_SEP);
739 if (url_delim == NULL
740 || url_delim < path_sep - strlen (VFS_PATH_URL_DELIMITER)
741 || url_delim - s + strlen (VFS_PATH_URL_DELIMITER) < strlen (s))
743 /* avoid trailing PATH_SEP, if present */
744 if (s[strlen (s) - 1] == PATH_SEP)
746 while (--path_sep > s && *path_sep != PATH_SEP);
747 return (path_sep != s) ? path_sep + 1 : s;
749 else
750 return (path_sep != NULL) ? path_sep + 1 : s;
753 while (--url_delim > s && *url_delim != PATH_SEP);
754 while (--url_delim > s && *url_delim != PATH_SEP);
756 return (url_delim == s) ? s : url_delim + 1;
759 /* --------------------------------------------------------------------------------------------- */
761 const char *
762 unix_error_string (int error_num)
764 static char buffer[BUF_LARGE];
765 gchar *strerror_currentlocale;
767 strerror_currentlocale = g_locale_from_utf8 (g_strerror (error_num), -1, NULL, NULL, NULL);
768 g_snprintf (buffer, sizeof (buffer), "%s (%d)", strerror_currentlocale, error_num);
769 g_free (strerror_currentlocale);
771 return buffer;
774 /* --------------------------------------------------------------------------------------------- */
776 const char *
777 skip_separators (const char *s)
779 const char *su = s;
781 for (; *su; str_cnext_char (&su))
782 if (*su != ' ' && *su != '\t' && *su != ',')
783 break;
785 return su;
788 /* --------------------------------------------------------------------------------------------- */
790 const char *
791 skip_numbers (const char *s)
793 const char *su = s;
795 for (; *su; str_cnext_char (&su))
796 if (!str_isdigit (su))
797 break;
799 return su;
802 /* --------------------------------------------------------------------------------------------- */
804 * Remove all control sequences from the argument string. We define
805 * "control sequence", in a sort of pidgin BNF, as follows:
807 * control-seq = Esc non-'['
808 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
810 * This scheme works for all the terminals described in my termcap /
811 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
812 * terminals. If I hear from a single person who uses such a terminal
813 * with MC, I'll be glad to add support for it. (Dugan)
814 * Non-printable characters are also removed.
817 char *
818 strip_ctrl_codes (char *s)
820 char *w; /* Current position where the stripped data is written */
821 char *r; /* Current position where the original data is read */
822 char *n;
824 if (!s)
825 return 0;
827 for (w = s, r = s; *r;)
829 if (*r == ESC_CHAR)
831 /* Skip the control sequence's arguments */ ;
832 /* '(' need to avoid strange 'B' letter in *Suse (if mc runs under root user) */
833 if (*(++r) == '[' || *r == '(')
835 /* strchr() matches trailing binary 0 */
836 while (*(++r) && strchr ("0123456789;?", *r));
838 else if (*r == ']')
841 * Skip xterm's OSC (Operating System Command)
842 * http://www.xfree86.org/current/ctlseqs.html
843 * OSC P s ; P t ST
844 * OSC P s ; P t BEL
846 char *new_r = r;
848 for (; *new_r; ++new_r)
850 switch (*new_r)
852 /* BEL */
853 case '\a':
854 r = new_r;
855 goto osc_out;
856 case ESC_CHAR:
857 /* ST */
858 if (*(new_r + 1) == '\\')
860 r = new_r + 1;
861 goto osc_out;
865 osc_out:;
869 * Now we are at the last character of the sequence.
870 * Skip it unless it's binary 0.
872 if (*r)
873 r++;
874 continue;
877 n = str_get_next_char (r);
878 if (str_isprint (r))
880 memmove (w, r, n - r);
881 w += n - r;
883 r = n;
885 *w = 0;
886 return s;
889 /* --------------------------------------------------------------------------------------------- */
891 enum compression_type
892 get_compression_type (int fd, const char *name)
894 unsigned char magic[16];
895 size_t str_len;
897 /* Read the magic signature */
898 if (mc_read (fd, (char *) magic, 4) != 4)
899 return COMPRESSION_NONE;
901 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
902 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236))
904 return COMPRESSION_GZIP;
907 /* PKZIP_MAGIC */
908 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003 && magic[3] == 004)
910 /* Read compression type */
911 mc_lseek (fd, 8, SEEK_SET);
912 if (mc_read (fd, (char *) magic, 2) != 2)
913 return COMPRESSION_NONE;
915 /* Gzip can handle only deflated (8) or stored (0) files */
916 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
917 return COMPRESSION_NONE;
919 /* Compatible with gzip */
920 return COMPRESSION_GZIP;
923 /* PACK_MAGIC and LZH_MAGIC and compress magic */
924 if (magic[0] == 037 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235))
926 /* Compatible with gzip */
927 return COMPRESSION_GZIP;
930 /* BZIP and BZIP2 files */
931 if ((magic[0] == 'B') && (magic[1] == 'Z') && (magic[3] >= '1') && (magic[3] <= '9'))
933 switch (magic[2])
935 case '0':
936 return COMPRESSION_BZIP;
937 case 'h':
938 return COMPRESSION_BZIP2;
942 /* Support for LZMA (only utils format with magic in header).
943 * This is the default format of LZMA utils 4.32.1 and later. */
945 if (mc_read (fd, (char *) magic + 4, 2) != 2)
946 return COMPRESSION_NONE;
948 /* LZMA utils format */
949 if (magic[0] == 0xFF
950 && magic[1] == 'L'
951 && magic[2] == 'Z' && magic[3] == 'M' && magic[4] == 'A' && magic[5] == 0x00)
952 return COMPRESSION_LZMA;
954 /* XZ compression magic */
955 if (magic[0] == 0xFD
956 && magic[1] == 0x37
957 && magic[2] == 0x7A && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00)
958 return COMPRESSION_XZ;
960 str_len = strlen (name);
961 /* HACK: we must belive to extention of LZMA file :) ... */
962 if ((str_len > 5 && strcmp (&name[str_len - 5], ".lzma") == 0) ||
963 (str_len > 4 && strcmp (&name[str_len - 4], ".tlz") == 0))
964 return COMPRESSION_LZMA;
966 return COMPRESSION_NONE;
969 /* --------------------------------------------------------------------------------------------- */
971 const char *
972 decompress_extension (int type)
974 switch (type)
976 case COMPRESSION_GZIP:
977 return "/ugz" VFS_PATH_URL_DELIMITER;
978 case COMPRESSION_BZIP:
979 return "/ubz" VFS_PATH_URL_DELIMITER;
980 case COMPRESSION_BZIP2:
981 return "/ubz2" VFS_PATH_URL_DELIMITER;
982 case COMPRESSION_LZMA:
983 return "/ulzma" VFS_PATH_URL_DELIMITER;
984 case COMPRESSION_XZ:
985 return "/uxz" VFS_PATH_URL_DELIMITER;
987 /* Should never reach this place */
988 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
989 return 0;
992 /* --------------------------------------------------------------------------------------------- */
994 void
995 wipe_password (char *passwd)
997 char *p = passwd;
999 if (!p)
1000 return;
1001 for (; *p; p++)
1002 *p = 0;
1003 g_free (passwd);
1006 /* --------------------------------------------------------------------------------------------- */
1008 * Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key
1009 * @returns a newly allocated string
1012 char *
1013 convert_controls (const char *p)
1015 char *valcopy = g_strdup (p);
1016 char *q;
1018 /* Parse the escape special character */
1019 for (q = valcopy; *p;)
1021 if (*p == '\\')
1023 p++;
1024 if ((*p == 'e') || (*p == 'E'))
1026 p++;
1027 *q++ = ESC_CHAR;
1030 else
1032 if (*p == '^')
1034 p++;
1035 if (*p == '^')
1036 *q++ = *p++;
1037 else
1039 char c = (*p | 0x20);
1040 if (c >= 'a' && c <= 'z')
1042 *q++ = c - 'a' + 1;
1043 p++;
1045 else if (*p)
1046 p++;
1049 else
1050 *q++ = *p++;
1053 *q = 0;
1054 return valcopy;
1057 /* --------------------------------------------------------------------------------------------- */
1059 * Finds out a relative path from first to second, i.e. goes as many ..
1060 * as needed up in first and then goes down using second
1063 char *
1064 diff_two_paths (const char *first, const char *second)
1066 char *p, *q, *r, *s, *buf = NULL;
1067 int i, j, prevlen = -1, currlen;
1068 char *my_first = NULL, *my_second = NULL;
1070 my_first = resolve_symlinks (first);
1071 if (my_first == NULL)
1072 return NULL;
1073 my_second = resolve_symlinks (second);
1074 if (my_second == NULL)
1076 g_free (my_first);
1077 return NULL;
1079 for (j = 0; j < 2; j++)
1081 p = my_first;
1082 q = my_second;
1083 for (;;)
1085 r = strchr (p, PATH_SEP);
1086 s = strchr (q, PATH_SEP);
1087 if (!r || !s)
1088 break;
1089 *r = 0;
1090 *s = 0;
1091 if (strcmp (p, q))
1093 *r = PATH_SEP;
1094 *s = PATH_SEP;
1095 break;
1097 else
1099 *r = PATH_SEP;
1100 *s = PATH_SEP;
1102 p = r + 1;
1103 q = s + 1;
1105 p--;
1106 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++);
1107 currlen = (i + 1) * 3 + strlen (q) + 1;
1108 if (j)
1110 if (currlen < prevlen)
1111 g_free (buf);
1112 else
1114 g_free (my_first);
1115 g_free (my_second);
1116 return buf;
1119 p = buf = g_malloc (currlen);
1120 prevlen = currlen;
1121 for (; i >= 0; i--, p += 3)
1122 strcpy (p, "../");
1123 strcpy (p, q);
1125 g_free (my_first);
1126 g_free (my_second);
1127 return buf;
1130 /* --------------------------------------------------------------------------------------------- */
1132 * If filename is NULL, then we just append PATH_SEP to the dir
1135 char *
1136 concat_dir_and_file (const char *dir, const char *file)
1138 int i = strlen (dir);
1140 if (dir[i - 1] == PATH_SEP)
1141 return g_strconcat (dir, file, (char *) NULL);
1142 else
1143 return g_strconcat (dir, PATH_SEP_STR, file, (char *) NULL);
1146 /* --------------------------------------------------------------------------------------------- */
1148 * Append text to GList, remove all entries with the same text
1151 GList *
1152 list_append_unique (GList * list, char *text)
1154 GList *lc_link;
1157 * Go to the last position and traverse the list backwards
1158 * starting from the second last entry to make sure that we
1159 * are not removing the current link.
1161 list = g_list_append (list, text);
1162 list = g_list_last (list);
1163 lc_link = g_list_previous (list);
1165 while (lc_link != NULL)
1167 GList *newlink;
1169 newlink = g_list_previous (lc_link);
1170 if (strcmp ((char *) lc_link->data, text) == 0)
1172 GList *tmp;
1174 g_free (lc_link->data);
1175 tmp = g_list_remove_link (list, lc_link);
1176 g_list_free_1 (lc_link);
1178 lc_link = newlink;
1181 return list;
1184 /* --------------------------------------------------------------------------------------------- */
1185 /* Following code heavily borrows from libiberty, mkstemps.c */
1187 * Arguments:
1188 * pname (output) - pointer to the name of the temp file (needs g_free).
1189 * NULL if the function fails.
1190 * prefix - part of the filename before the random part.
1191 * Prepend $TMPDIR or /tmp if there are no path separators.
1192 * suffix - if not NULL, part of the filename after the random part.
1194 * Result:
1195 * handle of the open file or -1 if couldn't open any.
1199 mc_mkstemps (char **pname, const char *prefix, const char *suffix)
1201 static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1202 static unsigned long value;
1203 struct timeval tv;
1204 char *tmpbase;
1205 char *tmpname;
1206 char *XXXXXX;
1207 int count;
1209 if (strchr (prefix, PATH_SEP) == NULL)
1211 /* Add prefix first to find the position of XXXXXX */
1212 tmpbase = concat_dir_and_file (mc_tmpdir (), prefix);
1214 else
1216 tmpbase = g_strdup (prefix);
1219 tmpname = g_strconcat (tmpbase, "XXXXXX", suffix, (char *) NULL);
1220 *pname = tmpname;
1221 XXXXXX = &tmpname[strlen (tmpbase)];
1222 g_free (tmpbase);
1224 /* Get some more or less random data. */
1225 gettimeofday (&tv, NULL);
1226 value += (tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
1228 for (count = 0; count < TMP_MAX; ++count)
1230 unsigned long v = value;
1231 int fd;
1233 /* Fill in the random bits. */
1234 XXXXXX[0] = letters[v % 62];
1235 v /= 62;
1236 XXXXXX[1] = letters[v % 62];
1237 v /= 62;
1238 XXXXXX[2] = letters[v % 62];
1239 v /= 62;
1240 XXXXXX[3] = letters[v % 62];
1241 v /= 62;
1242 XXXXXX[4] = letters[v % 62];
1243 v /= 62;
1244 XXXXXX[5] = letters[v % 62];
1246 fd = open (tmpname, O_RDWR | O_CREAT | O_TRUNC | O_EXCL, S_IRUSR | S_IWUSR);
1247 if (fd >= 0)
1249 /* Successfully created. */
1250 return fd;
1253 /* This is a random value. It is only necessary that the next
1254 TMP_MAX values generated by adding 7777 to VALUE are different
1255 with (module 2^32). */
1256 value += 7777;
1259 /* Unsuccessful. Free the filename. */
1260 g_free (tmpname);
1261 *pname = NULL;
1263 return -1;
1266 /* --------------------------------------------------------------------------------------------- */
1268 * Read and restore position for the given filename.
1269 * If there is no stored data, return line 1 and col 0.
1272 void
1273 load_file_position (const char *filename, long *line, long *column, off_t * offset,
1274 GArray ** bookmarks)
1276 char *fn;
1277 FILE *f;
1278 char buf[MC_MAXPATHLEN + 100];
1279 const size_t len = strlen (filename);
1281 /* defaults */
1282 *line = 1;
1283 *column = 0;
1284 *offset = 0;
1286 /* open file with positions */
1287 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1288 f = fopen (fn, "r");
1289 g_free (fn);
1290 if (f == NULL)
1291 return;
1293 /* prepare array for serialized bookmarks */
1294 *bookmarks = g_array_sized_new (FALSE, FALSE, sizeof (size_t), MAX_SAVED_BOOKMARKS);
1296 while (fgets (buf, sizeof (buf), f) != NULL)
1298 const char *p;
1299 gchar **pos_tokens;
1301 /* check if the filename matches the beginning of string */
1302 if (strncmp (buf, filename, len) != 0)
1303 continue;
1305 /* followed by single space */
1306 if (buf[len] != ' ')
1307 continue;
1309 /* and string without spaces */
1310 p = &buf[len + 1];
1311 if (strchr (p, ' ') != NULL)
1312 continue;
1314 pos_tokens = g_strsplit (p, ";", 3 + MAX_SAVED_BOOKMARKS);
1315 if (pos_tokens[0] == NULL)
1317 *line = 1;
1318 *column = 0;
1319 *offset = 0;
1321 else
1323 *line = strtol (pos_tokens[0], NULL, 10);
1324 if (pos_tokens[1] == NULL)
1326 *column = 0;
1327 *offset = 0;
1329 else
1331 *column = strtol (pos_tokens[1], NULL, 10);
1332 if (pos_tokens[2] == NULL)
1333 *offset = 0;
1334 else
1336 size_t i;
1338 *offset = strtoll (pos_tokens[2], NULL, 10);
1340 for (i = 0; i < MAX_SAVED_BOOKMARKS && pos_tokens[3 + i] != NULL; i++)
1342 size_t val;
1344 val = strtoul (pos_tokens[3 + i], NULL, 10);
1345 g_array_append_val (*bookmarks, val);
1351 g_strfreev (pos_tokens);
1354 fclose (f);
1357 /* --------------------------------------------------------------------------------------------- */
1359 * Save position for the given file
1362 void
1363 save_file_position (const char *filename, long line, long column, off_t offset, GArray * bookmarks)
1365 static size_t filepos_max_saved_entries = 0;
1366 char *fn, *tmp_fn;
1367 FILE *f, *tmp_f;
1368 char buf[MC_MAXPATHLEN + 100];
1369 size_t i;
1370 const size_t len = strlen (filename);
1371 gboolean src_error = FALSE;
1373 if (filepos_max_saved_entries == 0)
1374 filepos_max_saved_entries = mc_config_get_int (mc_main_config, CONFIG_APP_SECTION,
1375 "filepos_max_saved_entries", 1024);
1377 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1378 if (fn == NULL)
1379 goto early_error;
1381 mc_util_make_backup_if_possible (fn, TMP_SUFFIX);
1383 /* open file */
1384 f = fopen (fn, "w");
1385 if (f == NULL)
1386 goto open_target_error;
1388 tmp_fn = g_strdup_printf ("%s" TMP_SUFFIX, fn);
1389 tmp_f = fopen (tmp_fn, "r");
1390 if (tmp_f == NULL)
1392 src_error = TRUE;
1393 goto open_source_error;
1396 /* put the new record */
1397 if (line != 1 || column != 0 || bookmarks != NULL)
1399 if (fprintf (f, "%s %ld;%ld;%" PRIuMAX, filename, line, column, (uintmax_t) offset) < 0)
1400 goto write_position_error;
1401 if (bookmarks != NULL)
1402 for (i = 0; i < bookmarks->len && i < MAX_SAVED_BOOKMARKS; i++)
1403 if (fprintf (f, ";%zu", g_array_index (bookmarks, size_t, i)) < 0)
1404 goto write_position_error;
1406 if (fprintf (f, "\n") < 0)
1407 goto write_position_error;
1410 i = 1;
1411 while (fgets (buf, sizeof (buf), tmp_f) != NULL)
1413 if (buf[len] == ' ' && strncmp (buf, filename, len) == 0
1414 && strchr (&buf[len + 1], ' ') == NULL)
1415 continue;
1417 fprintf (f, "%s", buf);
1418 if (++i > filepos_max_saved_entries)
1419 break;
1422 write_position_error:
1423 fclose (tmp_f);
1424 open_source_error:
1425 g_free (tmp_fn);
1426 fclose (f);
1427 if (src_error)
1428 mc_util_restore_from_backup_if_possible (fn, TMP_SUFFIX);
1429 else
1430 mc_util_unlink_backup_if_possible (fn, TMP_SUFFIX);
1431 open_target_error:
1432 g_free (fn);
1433 early_error:
1434 if (bookmarks != NULL)
1435 g_array_free (bookmarks, TRUE);
1438 /* --------------------------------------------------------------------------------------------- */
1440 extern int
1441 ascii_alpha_to_cntrl (int ch)
1443 if ((ch >= ASCII_A && ch <= ASCII_Z) || (ch >= ASCII_a && ch <= ASCII_z))
1445 ch &= 0x1f;
1447 return ch;
1450 /* --------------------------------------------------------------------------------------------- */
1452 const char *
1453 Q_ (const char *s)
1455 const char *result, *sep;
1457 result = _(s);
1458 sep = strchr (result, '|');
1459 return (sep != NULL) ? sep + 1 : result;
1462 /* --------------------------------------------------------------------------------------------- */
1464 gboolean
1465 mc_util_make_backup_if_possible (const char *file_name, const char *backup_suffix)
1467 struct stat stat_buf;
1468 char *backup_path;
1469 gboolean ret;
1470 if (!exist_file (file_name))
1471 return FALSE;
1473 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1475 if (backup_path == NULL)
1476 return FALSE;
1478 ret = mc_util_write_backup_content (file_name, backup_path);
1480 if (ret)
1482 /* Backup file will have same ownership with main file. */
1483 if (stat (file_name, &stat_buf) == 0)
1484 chmod (backup_path, stat_buf.st_mode);
1485 else
1486 chmod (backup_path, S_IRUSR | S_IWUSR);
1489 g_free (backup_path);
1491 return ret;
1494 /* --------------------------------------------------------------------------------------------- */
1496 gboolean
1497 mc_util_restore_from_backup_if_possible (const char *file_name, const char *backup_suffix)
1499 gboolean ret;
1500 char *backup_path;
1502 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1503 if (backup_path == NULL)
1504 return FALSE;
1506 ret = mc_util_write_backup_content (backup_path, file_name);
1507 g_free (backup_path);
1509 return ret;
1512 /* --------------------------------------------------------------------------------------------- */
1514 gboolean
1515 mc_util_unlink_backup_if_possible (const char *file_name, const char *backup_suffix)
1517 char *backup_path;
1519 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1520 if (backup_path == NULL)
1521 return FALSE;
1523 if (exist_file (backup_path))
1524 mc_unlink (backup_path);
1526 g_free (backup_path);
1527 return TRUE;
1530 /* --------------------------------------------------------------------------------------------- */
1532 * partly taken from dcigettext.c, returns "" for default locale
1533 * value should be freed by calling function g_free()
1536 char *
1537 guess_message_value (void)
1539 static const char *const var[] = {
1540 /* Setting of LC_ALL overwrites all other. */
1541 /* Do not use LANGUAGE for check user locale and drowing hints */
1542 "LC_ALL",
1543 /* Next comes the name of the desired category. */
1544 "LC_MESSAGES",
1545 /* Last possibility is the LANG environment variable. */
1546 "LANG",
1547 /* NULL exit loops */
1548 NULL
1551 unsigned i = 0;
1552 const char *locale = NULL;
1554 while (var[i] != NULL)
1556 locale = getenv (var[i]);
1557 if (locale != NULL && locale[0] != '\0')
1558 break;
1559 i++;
1562 if (locale == NULL)
1563 locale = "";
1565 return g_strdup (locale);
1568 /* --------------------------------------------------------------------------------------------- */