Ticket #2598: u7z: Improve handling of missing p7zip binaries.
[midnight-commander.git] / lib / util.c
blob9e50bf49c1e3588079b0e6e4c3bcc2e61252d0e8
1 /* Various utilities
2 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
3 2004, 2005, 2007, 2009 Free Software Foundation, Inc.
4 Written 1994, 1995, 1996 by:
5 Miguel de Icaza, Janne Kukonlehto, Dugan Porter,
6 Jakub Jelinek, Mauricio Plaza.
8 The file_date routine is mostly from GNU's fileutils package,
9 written by Richard Stallman and David MacKenzie.
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
25 /** \file
26 * \brief Source: various utilities
29 #include <config.h>
31 #include <ctype.h>
32 #include <limits.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <fcntl.h>
38 #include <sys/time.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
43 #include "lib/global.h"
44 #include "lib/mcconfig.h"
45 #include "lib/fileloc.h"
46 #include "lib/vfs/vfs.h"
47 #include "lib/strutil.h"
48 #include "lib/util.h"
50 /*** global variables ****************************************************************************/
52 /*** file scope macro definitions ****************************************************************/
54 #define ismode(n,m) ((n & m) == m)
56 /* Number of attempts to create a temporary file */
57 #ifndef TMP_MAX
58 #define TMP_MAX 16384
59 #endif /* !TMP_MAX */
61 #define TMP_SUFFIX ".tmp"
63 #define ASCII_A (0x40 + 1)
64 #define ASCII_Z (0x40 + 26)
65 #define ASCII_a (0x60 + 1)
66 #define ASCII_z (0x60 + 26)
68 /*** file scope type declarations ****************************************************************/
70 /*** file scope variables ************************************************************************/
72 /*** file scope functions ************************************************************************/
73 /* --------------------------------------------------------------------------------------------- */
75 static inline int
76 is_7bit_printable (unsigned char c)
78 return (c > 31 && c < 127);
81 /* --------------------------------------------------------------------------------------------- */
83 static inline int
84 is_iso_printable (unsigned char c)
86 return ((c > 31 && c < 127) || c >= 160);
89 /* --------------------------------------------------------------------------------------------- */
91 static inline int
92 is_8bit_printable (unsigned char c)
94 /* "Full 8 bits output" doesn't work on xterm */
95 if (mc_global.tty.xterm_flag)
96 return is_iso_printable (c);
98 return (c > 31 && c != 127 && c != 155);
101 /* --------------------------------------------------------------------------------------------- */
103 static char *
104 resolve_symlinks (const char *path)
106 char *buf, *buf2, *q, *r, c;
107 int len;
108 struct stat mybuf;
109 const char *p;
111 if (*path != PATH_SEP)
112 return NULL;
113 r = buf = g_malloc (MC_MAXPATHLEN);
114 buf2 = g_malloc (MC_MAXPATHLEN);
115 *r++ = PATH_SEP;
116 *r = 0;
117 p = path;
118 for (;;)
120 q = strchr (p + 1, PATH_SEP);
121 if (!q)
123 q = strchr (p + 1, 0);
124 if (q == p + 1)
125 break;
127 c = *q;
128 *q = 0;
129 if (mc_lstat (path, &mybuf) < 0)
131 g_free (buf);
132 g_free (buf2);
133 *q = c;
134 return NULL;
136 if (!S_ISLNK (mybuf.st_mode))
137 strcpy (r, p + 1);
138 else
140 len = mc_readlink (path, buf2, MC_MAXPATHLEN - 1);
141 if (len < 0)
143 g_free (buf);
144 g_free (buf2);
145 *q = c;
146 return NULL;
148 buf2[len] = 0;
149 if (*buf2 == PATH_SEP)
150 strcpy (buf, buf2);
151 else
152 strcpy (r, buf2);
154 canonicalize_pathname (buf);
155 r = strchr (buf, 0);
156 if (!*r || *(r - 1) != PATH_SEP)
158 *r++ = PATH_SEP;
159 *r = 0;
161 *q = c;
162 p = q;
163 if (!c)
164 break;
166 if (!*buf)
167 strcpy (buf, PATH_SEP_STR);
168 else if (*(r - 1) == PATH_SEP && r != buf + 1)
169 *(r - 1) = 0;
170 g_free (buf2);
171 return buf;
174 /* --------------------------------------------------------------------------------------------- */
176 static gboolean
177 mc_util_write_backup_content (const char *from_file_name, const char *to_file_name)
179 FILE *backup_fd;
180 char *contents;
181 gsize length;
182 gboolean ret1 = TRUE;
184 if (!g_file_get_contents (from_file_name, &contents, &length, NULL))
185 return FALSE;
187 backup_fd = fopen (to_file_name, "w");
188 if (backup_fd == NULL)
190 g_free (contents);
191 return FALSE;
194 if (fwrite ((const void *) contents, 1, length, backup_fd) != length)
195 ret1 = FALSE;
197 int ret2;
198 ret2 = fflush (backup_fd);
199 ret2 = fclose (backup_fd);
201 g_free (contents);
202 return ret1;
205 /* --------------------------------------------------------------------------------------------- */
206 /*** public functions ****************************************************************************/
207 /* --------------------------------------------------------------------------------------------- */
210 is_printable (int c)
212 c &= 0xff;
214 #ifdef HAVE_CHARSET
215 /* "Display bits" is ignored, since the user controls the output
216 by setting the output codepage */
217 return is_8bit_printable (c);
218 #else
219 if (!mc_global.eight_bit_clean)
220 return is_7bit_printable (c);
222 if (mc_global.full_eight_bits)
224 return is_8bit_printable (c);
226 else
227 return is_iso_printable (c);
228 #endif /* !HAVE_CHARSET */
231 /* --------------------------------------------------------------------------------------------- */
233 * Quote the filename for the purpose of inserting it into the command
234 * line. If quote_percent is 1, replace "%" with "%%" - the percent is
235 * processed by the mc command line.
237 char *
238 name_quote (const char *s, int quote_percent)
240 char *ret, *d;
242 d = ret = g_malloc (strlen (s) * 2 + 2 + 1);
243 if (*s == '-')
245 *d++ = '.';
246 *d++ = '/';
249 for (; *s; s++, d++)
251 switch (*s)
253 case '%':
254 if (quote_percent)
255 *d++ = '%';
256 break;
257 case '\'':
258 case '\\':
259 case '\r':
260 case '\n':
261 case '\t':
262 case '"':
263 case ';':
264 case ' ':
265 case '?':
266 case '|':
267 case '[':
268 case ']':
269 case '{':
270 case '}':
271 case '<':
272 case '>':
273 case '`':
274 case '!':
275 case '$':
276 case '&':
277 case '*':
278 case '(':
279 case ')':
280 *d++ = '\\';
281 break;
282 case '~':
283 case '#':
284 if (d == ret)
285 *d++ = '\\';
286 break;
288 *d = *s;
290 *d = '\0';
291 return ret;
294 /* --------------------------------------------------------------------------------------------- */
296 char *
297 fake_name_quote (const char *s, int quote_percent)
299 (void) quote_percent;
300 return g_strdup (s);
303 /* --------------------------------------------------------------------------------------------- */
305 * path_trunc() is the same as str_trunc() but
306 * it deletes possible password from path for security
307 * reasons.
310 const char *
311 path_trunc (const char *path, size_t trunc_len)
313 char *secure_path = strip_password (g_strdup (path), 1);
315 const char *ret = str_trunc (secure_path, trunc_len);
316 g_free (secure_path);
318 return ret;
321 /* --------------------------------------------------------------------------------------------- */
323 const char *
324 size_trunc (uintmax_t size, gboolean use_si)
326 static char x[BUF_TINY];
327 uintmax_t divisor = 1;
328 const char *xtra = "";
330 if (size > 999999999UL)
332 divisor = use_si ? 1000 : 1024;
333 xtra = use_si ? "k" : "K";
335 if (size / divisor > 999999999UL)
337 divisor = use_si ? (1000 * 1000) : (1024 * 1024);
338 xtra = use_si ? "m" : "M";
340 if (size / divisor > 999999999UL)
342 divisor = use_si ? (1000 * 1000 * 1000) : (1024 * 1024 * 1024);
343 xtra = use_si ? "g" : "G";
347 g_snprintf (x, sizeof (x), "%.0f%s", 1.0 * size / divisor, xtra);
348 return x;
351 /* --------------------------------------------------------------------------------------------- */
353 const char *
354 size_trunc_sep (uintmax_t size, gboolean use_si)
356 static char x[60];
357 int count;
358 const char *p, *y;
359 char *d;
361 p = y = size_trunc (size, use_si);
362 p += strlen (p) - 1;
363 d = x + sizeof (x) - 1;
364 *d-- = '\0';
365 while (p >= y && isalpha ((unsigned char) *p))
366 *d-- = *p--;
367 for (count = 0; p >= y; count++)
369 if (count == 3)
371 *d-- = ',';
372 count = 0;
374 *d-- = *p--;
376 d++;
377 if (*d == ',')
378 d++;
379 return d;
382 /* --------------------------------------------------------------------------------------------- */
384 * Print file SIZE to BUFFER, but don't exceed LEN characters,
385 * not including trailing 0. BUFFER should be at least LEN+1 long.
386 * This function is called for every file on panels, so avoid
387 * floating point by any means.
389 * Units: size units (filesystem sizes are 1K blocks)
390 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
393 void
394 size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gboolean use_si)
396 /* Avoid taking power for every file. */
397 /* *INDENT-OFF* */
398 static const uintmax_t power10[] = {
399 /* we hope that size of uintmax_t is 4 bytes at least */
400 1ULL,
401 10ULL,
402 100ULL,
403 1000ULL,
404 10000ULL,
405 100000ULL,
406 1000000ULL,
407 10000000ULL,
408 100000000ULL,
409 1000000000ULL
410 /* maximmum value of uintmax_t (in case of 4 bytes) is
411 4294967295
413 #if SIZEOF_UINTMAX_T == 8
415 10000000000ULL,
416 100000000000ULL,
417 1000000000000ULL,
418 10000000000000ULL,
419 100000000000000ULL,
420 1000000000000000ULL,
421 10000000000000000ULL,
422 100000000000000000ULL,
423 1000000000000000000ULL,
424 10000000000000000000ULL
425 /* maximmum value of uintmax_t (in case of 8 bytes) is
426 18447644073710439615
428 #endif
430 /* *INDENT-ON* */
431 static const char *const suffix[] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL };
432 static const char *const suffix_lc[] = { "", "k", "m", "g", "t", "p", "e", "z", "y", NULL };
433 int j = 0;
434 int size_remain;
436 if (len == 0)
437 len = 9;
438 #if SIZEOF_UINTMAX_T == 8
439 /* 20 decimal digits are required to represent 8 bytes */
440 else if (len > 19)
441 len = 19;
442 #else
443 /* 10 decimal digits are required to represent 4 bytes */
444 else if (len > 9)
445 len = 9;
446 #endif
449 * recalculate from 1024 base to 1000 base if units>0
450 * We can't just multiply by 1024 - that might cause overflow
451 * if off_t type is too small
453 if (use_si)
454 for (j = 0; j < units; j++)
456 size_remain = ((size % 125) * 1024) / 1000; /* size mod 125, recalculated */
457 size = size / 125; /* 128/125 = 1024/1000 */
458 size = size * 128; /* This will convert size from multiple of 1024 to multiple of 1000 */
459 size += size_remain; /* Re-add remainder lost by division/multiplication */
462 for (j = units; suffix[j] != NULL; j++)
464 if (size == 0)
466 if (j == units)
468 /* Empty files will print "0" even with minimal width. */
469 g_snprintf (buffer, len + 1, "0");
470 break;
473 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
474 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s",
475 (j > 1) ? (use_si ? suffix_lc[j - 1] : suffix[j - 1]) : "B");
476 break;
479 if (size < power10[len - (j > 0 ? 1 : 0)])
481 g_snprintf (buffer, len + 1, "%" PRIuMAX "%s", size, use_si ? suffix_lc[j] : suffix[j]);
482 break;
485 /* Powers of 1000 or 1024, with rounding. */
486 if (use_si)
487 size = (size + 500) / 1000;
488 else
489 size = (size + 512) >> 10;
493 /* --------------------------------------------------------------------------------------------- */
495 const char *
496 string_perm (mode_t mode_bits)
498 static char mode[11];
500 strcpy (mode, "----------");
501 if (S_ISDIR (mode_bits))
502 mode[0] = 'd';
503 if (S_ISCHR (mode_bits))
504 mode[0] = 'c';
505 if (S_ISBLK (mode_bits))
506 mode[0] = 'b';
507 if (S_ISLNK (mode_bits))
508 mode[0] = 'l';
509 if (S_ISFIFO (mode_bits))
510 mode[0] = 'p';
511 if (S_ISNAM (mode_bits))
512 mode[0] = 'n';
513 if (S_ISSOCK (mode_bits))
514 mode[0] = 's';
515 if (S_ISDOOR (mode_bits))
516 mode[0] = 'D';
517 if (ismode (mode_bits, S_IXOTH))
518 mode[9] = 'x';
519 if (ismode (mode_bits, S_IWOTH))
520 mode[8] = 'w';
521 if (ismode (mode_bits, S_IROTH))
522 mode[7] = 'r';
523 if (ismode (mode_bits, S_IXGRP))
524 mode[6] = 'x';
525 if (ismode (mode_bits, S_IWGRP))
526 mode[5] = 'w';
527 if (ismode (mode_bits, S_IRGRP))
528 mode[4] = 'r';
529 if (ismode (mode_bits, S_IXUSR))
530 mode[3] = 'x';
531 if (ismode (mode_bits, S_IWUSR))
532 mode[2] = 'w';
533 if (ismode (mode_bits, S_IRUSR))
534 mode[1] = 'r';
535 #ifdef S_ISUID
536 if (ismode (mode_bits, S_ISUID))
537 mode[3] = (mode[3] == 'x') ? 's' : 'S';
538 #endif /* S_ISUID */
539 #ifdef S_ISGID
540 if (ismode (mode_bits, S_ISGID))
541 mode[6] = (mode[6] == 'x') ? 's' : 'S';
542 #endif /* S_ISGID */
543 #ifdef S_ISVTX
544 if (ismode (mode_bits, S_ISVTX))
545 mode[9] = (mode[9] == 'x') ? 't' : 'T';
546 #endif /* S_ISVTX */
547 return mode;
550 /* --------------------------------------------------------------------------------------------- */
552 * p: string which might contain an url with a password (this parameter is
553 * modified in place).
554 * has_prefix = 0: The first parameter is an url without a prefix
555 * (user[:pass]@]machine[:port][remote-dir). Delete
556 * the password.
557 * has_prefix = 1: Search p for known url prefixes. If found delete
558 * the password from the url.
559 * Caveat: only the first url is found
562 char *
563 strip_password (char *p, int has_prefix)
565 static const struct
567 const char *name;
568 size_t len;
569 } prefixes[] =
571 /* *INDENT-OFF* */
572 { "/#ftp:", 6 },
573 { "ftp://", 6 },
574 { "/#smb:", 6 },
575 { "smb://", 6 },
576 { "/#sh:", 5 },
577 { "sh://", 5 },
578 { "ssh://", 6 }
579 /* *INDENT-ON* */
582 char *at, *inner_colon, *dir;
583 size_t i;
584 char *result = p;
586 for (i = 0; i < sizeof (prefixes) / sizeof (prefixes[0]); i++)
588 char *q;
590 if (has_prefix)
592 q = strstr (p, prefixes[i].name);
593 if (q == NULL)
594 continue;
595 else
596 p = q + prefixes[i].len;
599 dir = strchr (p, PATH_SEP);
600 if (dir != NULL)
601 *dir = '\0';
603 /* search for any possible user */
604 at = strrchr (p, '@');
606 if (dir)
607 *dir = PATH_SEP;
609 /* We have a username */
610 if (at)
612 inner_colon = memchr (p, ':', at - p);
613 if (inner_colon)
614 memmove (inner_colon, at, strlen (at) + 1);
616 break;
618 return (result);
621 /* --------------------------------------------------------------------------------------------- */
623 const char *
624 strip_home_and_password (const char *dir)
626 size_t len;
627 static char newdir[MC_MAXPATHLEN];
629 len = strlen (mc_config_get_home_dir ());
630 if (mc_config_get_home_dir () != NULL && strncmp (dir, mc_config_get_home_dir (), len) == 0 &&
631 (dir[len] == PATH_SEP || dir[len] == '\0'))
633 newdir[0] = '~';
634 g_strlcpy (&newdir[1], &dir[len], sizeof (newdir) - 1);
635 return newdir;
638 /* We do not strip homes in /#ftp tree, I do not like ~'s there
639 (see ftpfs.c why) */
640 g_strlcpy (newdir, dir, sizeof (newdir));
641 strip_password (newdir, 1);
642 return newdir;
645 /* --------------------------------------------------------------------------------------------- */
647 const char *
648 extension (const char *filename)
650 const char *d = strrchr (filename, '.');
651 return (d != NULL) ? d + 1 : "";
654 /* --------------------------------------------------------------------------------------------- */
656 char *
657 load_mc_home_file (const char *from, const char *filename, char **allocated_filename)
659 char *hintfile_base, *hintfile;
660 char *lang;
661 char *data;
663 hintfile_base = g_build_filename (from, filename, (char *) NULL);
664 lang = guess_message_value ();
666 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
667 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
669 /* Fall back to the two-letter language code */
670 if (lang[0] != '\0' && lang[1] != '\0')
671 lang[2] = '\0';
672 g_free (hintfile);
673 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
674 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
676 g_free (hintfile);
677 hintfile = hintfile_base;
678 g_file_get_contents (hintfile_base, &data, NULL, NULL);
682 g_free (lang);
684 if (hintfile != hintfile_base)
685 g_free (hintfile_base);
687 if (allocated_filename != NULL)
688 *allocated_filename = hintfile;
689 else
690 g_free (hintfile);
692 return data;
695 /* --------------------------------------------------------------------------------------------- */
697 const char *
698 extract_line (const char *s, const char *top)
700 static char tmp_line[BUF_MEDIUM];
701 char *t = tmp_line;
703 while (*s && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line) - 1 && s < top)
704 *t++ = *s++;
705 *t = 0;
706 return tmp_line;
709 /* --------------------------------------------------------------------------------------------- */
711 * The basename routine
714 const char *
715 x_basename (const char *s)
717 const char *url_delim, *path_sep;
719 url_delim = g_strrstr (s, VFS_PATH_URL_DELIMITER);
720 path_sep = strrchr (s, PATH_SEP);
722 if (url_delim == NULL
723 || url_delim < path_sep - strlen (VFS_PATH_URL_DELIMITER)
724 || url_delim - s + strlen (VFS_PATH_URL_DELIMITER) < strlen (s))
726 /* avoid trailing PATH_SEP, if present */
727 if (s[strlen (s) - 1] == PATH_SEP)
729 while (--path_sep > s && *path_sep != PATH_SEP);
730 return (path_sep != s) ? path_sep + 1 : s;
732 else
733 return (path_sep != NULL) ? path_sep + 1 : s;
736 while (--url_delim > s && *url_delim != PATH_SEP);
737 while (--url_delim > s && *url_delim != PATH_SEP);
739 return (url_delim == s) ? s : url_delim + 1;
742 /* --------------------------------------------------------------------------------------------- */
744 const char *
745 unix_error_string (int error_num)
747 static char buffer[BUF_LARGE];
748 gchar *strerror_currentlocale;
750 strerror_currentlocale = g_locale_from_utf8 (g_strerror (error_num), -1, NULL, NULL, NULL);
751 g_snprintf (buffer, sizeof (buffer), "%s (%d)", strerror_currentlocale, error_num);
752 g_free (strerror_currentlocale);
754 return buffer;
757 /* --------------------------------------------------------------------------------------------- */
759 const char *
760 skip_separators (const char *s)
762 const char *su = s;
764 for (; *su; str_cnext_char (&su))
765 if (*su != ' ' && *su != '\t' && *su != ',')
766 break;
768 return su;
771 /* --------------------------------------------------------------------------------------------- */
773 const char *
774 skip_numbers (const char *s)
776 const char *su = s;
778 for (; *su; str_cnext_char (&su))
779 if (!str_isdigit (su))
780 break;
782 return su;
785 /* --------------------------------------------------------------------------------------------- */
787 * Remove all control sequences from the argument string. We define
788 * "control sequence", in a sort of pidgin BNF, as follows:
790 * control-seq = Esc non-'['
791 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
793 * This scheme works for all the terminals described in my termcap /
794 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
795 * terminals. If I hear from a single person who uses such a terminal
796 * with MC, I'll be glad to add support for it. (Dugan)
797 * Non-printable characters are also removed.
800 char *
801 strip_ctrl_codes (char *s)
803 char *w; /* Current position where the stripped data is written */
804 char *r; /* Current position where the original data is read */
805 char *n;
807 if (!s)
808 return 0;
810 for (w = s, r = s; *r;)
812 if (*r == ESC_CHAR)
814 /* Skip the control sequence's arguments */ ;
815 /* '(' need to avoid strange 'B' letter in *Suse (if mc runs under root user) */
816 if (*(++r) == '[' || *r == '(')
818 /* strchr() matches trailing binary 0 */
819 while (*(++r) && strchr ("0123456789;?", *r));
821 else if (*r == ']')
824 * Skip xterm's OSC (Operating System Command)
825 * http://www.xfree86.org/current/ctlseqs.html
826 * OSC P s ; P t ST
827 * OSC P s ; P t BEL
829 char *new_r = r;
831 for (; *new_r; ++new_r)
833 switch (*new_r)
835 /* BEL */
836 case '\a':
837 r = new_r;
838 goto osc_out;
839 case ESC_CHAR:
840 /* ST */
841 if (*(new_r + 1) == '\\')
843 r = new_r + 1;
844 goto osc_out;
848 osc_out:;
852 * Now we are at the last character of the sequence.
853 * Skip it unless it's binary 0.
855 if (*r)
856 r++;
857 continue;
860 n = str_get_next_char (r);
861 if (str_isprint (r))
863 memmove (w, r, n - r);
864 w += n - r;
866 r = n;
868 *w = 0;
869 return s;
872 /* --------------------------------------------------------------------------------------------- */
874 enum compression_type
875 get_compression_type (int fd, const char *name)
877 unsigned char magic[16];
878 size_t str_len;
880 /* Read the magic signature */
881 if (mc_read (fd, (char *) magic, 4) != 4)
882 return COMPRESSION_NONE;
884 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
885 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236))
887 return COMPRESSION_GZIP;
890 /* PKZIP_MAGIC */
891 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003 && magic[3] == 004)
893 /* Read compression type */
894 mc_lseek (fd, 8, SEEK_SET);
895 if (mc_read (fd, (char *) magic, 2) != 2)
896 return COMPRESSION_NONE;
898 /* Gzip can handle only deflated (8) or stored (0) files */
899 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
900 return COMPRESSION_NONE;
902 /* Compatible with gzip */
903 return COMPRESSION_GZIP;
906 /* PACK_MAGIC and LZH_MAGIC and compress magic */
907 if (magic[0] == 037 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235))
909 /* Compatible with gzip */
910 return COMPRESSION_GZIP;
913 /* BZIP and BZIP2 files */
914 if ((magic[0] == 'B') && (magic[1] == 'Z') && (magic[3] >= '1') && (magic[3] <= '9'))
916 switch (magic[2])
918 case '0':
919 return COMPRESSION_BZIP;
920 case 'h':
921 return COMPRESSION_BZIP2;
925 /* Support for LZMA (only utils format with magic in header).
926 * This is the default format of LZMA utils 4.32.1 and later. */
928 if (mc_read (fd, (char *) magic + 4, 2) != 2)
929 return COMPRESSION_NONE;
931 /* LZMA utils format */
932 if (magic[0] == 0xFF
933 && magic[1] == 'L'
934 && magic[2] == 'Z' && magic[3] == 'M' && magic[4] == 'A' && magic[5] == 0x00)
935 return COMPRESSION_LZMA;
937 /* XZ compression magic */
938 if (magic[0] == 0xFD
939 && magic[1] == 0x37
940 && magic[2] == 0x7A && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00)
941 return COMPRESSION_XZ;
943 str_len = strlen (name);
944 /* HACK: we must belive to extention of LZMA file :) ... */
945 if ((str_len > 5 && strcmp (&name[str_len - 5], ".lzma") == 0) ||
946 (str_len > 4 && strcmp (&name[str_len - 4], ".tlz") == 0))
947 return COMPRESSION_LZMA;
949 return COMPRESSION_NONE;
952 /* --------------------------------------------------------------------------------------------- */
954 const char *
955 decompress_extension (int type)
957 switch (type)
959 case COMPRESSION_GZIP:
960 return "/ugz" VFS_PATH_URL_DELIMITER;
961 case COMPRESSION_BZIP:
962 return "/ubz" VFS_PATH_URL_DELIMITER;
963 case COMPRESSION_BZIP2:
964 return "/ubz2" VFS_PATH_URL_DELIMITER;
965 case COMPRESSION_LZMA:
966 return "/ulzma" VFS_PATH_URL_DELIMITER;
967 case COMPRESSION_XZ:
968 return "/uxz" VFS_PATH_URL_DELIMITER;
970 /* Should never reach this place */
971 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
972 return 0;
975 /* --------------------------------------------------------------------------------------------- */
977 void
978 wipe_password (char *passwd)
980 char *p = passwd;
982 if (!p)
983 return;
984 for (; *p; p++)
985 *p = 0;
986 g_free (passwd);
989 /* --------------------------------------------------------------------------------------------- */
991 * Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key
992 * @returns a newly allocated string
995 char *
996 convert_controls (const char *p)
998 char *valcopy = g_strdup (p);
999 char *q;
1001 /* Parse the escape special character */
1002 for (q = valcopy; *p;)
1004 if (*p == '\\')
1006 p++;
1007 if ((*p == 'e') || (*p == 'E'))
1009 p++;
1010 *q++ = ESC_CHAR;
1013 else
1015 if (*p == '^')
1017 p++;
1018 if (*p == '^')
1019 *q++ = *p++;
1020 else
1022 char c = (*p | 0x20);
1023 if (c >= 'a' && c <= 'z')
1025 *q++ = c - 'a' + 1;
1026 p++;
1028 else if (*p)
1029 p++;
1032 else
1033 *q++ = *p++;
1036 *q = 0;
1037 return valcopy;
1040 /* --------------------------------------------------------------------------------------------- */
1042 * Finds out a relative path from first to second, i.e. goes as many ..
1043 * as needed up in first and then goes down using second
1046 char *
1047 diff_two_paths (const char *first, const char *second)
1049 char *p, *q, *r, *s, *buf = NULL;
1050 int i, j, prevlen = -1, currlen;
1051 char *my_first = NULL, *my_second = NULL;
1053 my_first = resolve_symlinks (first);
1054 if (my_first == NULL)
1055 return NULL;
1056 my_second = resolve_symlinks (second);
1057 if (my_second == NULL)
1059 g_free (my_first);
1060 return NULL;
1062 for (j = 0; j < 2; j++)
1064 p = my_first;
1065 q = my_second;
1066 for (;;)
1068 r = strchr (p, PATH_SEP);
1069 s = strchr (q, PATH_SEP);
1070 if (!r || !s)
1071 break;
1072 *r = 0;
1073 *s = 0;
1074 if (strcmp (p, q))
1076 *r = PATH_SEP;
1077 *s = PATH_SEP;
1078 break;
1080 else
1082 *r = PATH_SEP;
1083 *s = PATH_SEP;
1085 p = r + 1;
1086 q = s + 1;
1088 p--;
1089 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++);
1090 currlen = (i + 1) * 3 + strlen (q) + 1;
1091 if (j)
1093 if (currlen < prevlen)
1094 g_free (buf);
1095 else
1097 g_free (my_first);
1098 g_free (my_second);
1099 return buf;
1102 p = buf = g_malloc (currlen);
1103 prevlen = currlen;
1104 for (; i >= 0; i--, p += 3)
1105 strcpy (p, "../");
1106 strcpy (p, q);
1108 g_free (my_first);
1109 g_free (my_second);
1110 return buf;
1113 /* --------------------------------------------------------------------------------------------- */
1115 * If filename is NULL, then we just append PATH_SEP to the dir
1118 char *
1119 concat_dir_and_file (const char *dir, const char *file)
1121 int i = strlen (dir);
1123 if (dir[i - 1] == PATH_SEP)
1124 return g_strconcat (dir, file, (char *) NULL);
1125 else
1126 return g_strconcat (dir, PATH_SEP_STR, file, (char *) NULL);
1129 /* --------------------------------------------------------------------------------------------- */
1131 * Append text to GList, remove all entries with the same text
1134 GList *
1135 list_append_unique (GList * list, char *text)
1137 GList *lc_link;
1140 * Go to the last position and traverse the list backwards
1141 * starting from the second last entry to make sure that we
1142 * are not removing the current link.
1144 list = g_list_append (list, text);
1145 list = g_list_last (list);
1146 lc_link = g_list_previous (list);
1148 while (lc_link != NULL)
1150 GList *newlink;
1152 newlink = g_list_previous (lc_link);
1153 if (strcmp ((char *) lc_link->data, text) == 0)
1155 GList *tmp;
1157 g_free (lc_link->data);
1158 tmp = g_list_remove_link (list, lc_link);
1159 g_list_free_1 (lc_link);
1161 lc_link = newlink;
1164 return list;
1167 /* --------------------------------------------------------------------------------------------- */
1168 /* Following code heavily borrows from libiberty, mkstemps.c */
1170 * Arguments:
1171 * pname (output) - pointer to the name of the temp file (needs g_free).
1172 * NULL if the function fails.
1173 * prefix - part of the filename before the random part.
1174 * Prepend $TMPDIR or /tmp if there are no path separators.
1175 * suffix - if not NULL, part of the filename after the random part.
1177 * Result:
1178 * handle of the open file or -1 if couldn't open any.
1182 mc_mkstemps (char **pname, const char *prefix, const char *suffix)
1184 static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1185 static unsigned long value;
1186 struct timeval tv;
1187 char *tmpbase;
1188 char *tmpname;
1189 char *XXXXXX;
1190 int count;
1192 if (strchr (prefix, PATH_SEP) == NULL)
1194 /* Add prefix first to find the position of XXXXXX */
1195 tmpbase = concat_dir_and_file (mc_tmpdir (), prefix);
1197 else
1199 tmpbase = g_strdup (prefix);
1202 tmpname = g_strconcat (tmpbase, "XXXXXX", suffix, (char *) NULL);
1203 *pname = tmpname;
1204 XXXXXX = &tmpname[strlen (tmpbase)];
1205 g_free (tmpbase);
1207 /* Get some more or less random data. */
1208 gettimeofday (&tv, NULL);
1209 value += (tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
1211 for (count = 0; count < TMP_MAX; ++count)
1213 unsigned long v = value;
1214 int fd;
1216 /* Fill in the random bits. */
1217 XXXXXX[0] = letters[v % 62];
1218 v /= 62;
1219 XXXXXX[1] = letters[v % 62];
1220 v /= 62;
1221 XXXXXX[2] = letters[v % 62];
1222 v /= 62;
1223 XXXXXX[3] = letters[v % 62];
1224 v /= 62;
1225 XXXXXX[4] = letters[v % 62];
1226 v /= 62;
1227 XXXXXX[5] = letters[v % 62];
1229 fd = open (tmpname, O_RDWR | O_CREAT | O_TRUNC | O_EXCL, S_IRUSR | S_IWUSR);
1230 if (fd >= 0)
1232 /* Successfully created. */
1233 return fd;
1236 /* This is a random value. It is only necessary that the next
1237 TMP_MAX values generated by adding 7777 to VALUE are different
1238 with (module 2^32). */
1239 value += 7777;
1242 /* Unsuccessful. Free the filename. */
1243 g_free (tmpname);
1244 *pname = NULL;
1246 return -1;
1249 /* --------------------------------------------------------------------------------------------- */
1251 * Read and restore position for the given filename.
1252 * If there is no stored data, return line 1 and col 0.
1255 void
1256 load_file_position (const char *filename, long *line, long *column, off_t * offset,
1257 GArray ** bookmarks)
1259 char *fn;
1260 FILE *f;
1261 char buf[MC_MAXPATHLEN + 100];
1262 const size_t len = strlen (filename);
1264 /* defaults */
1265 *line = 1;
1266 *column = 0;
1267 *offset = 0;
1269 /* open file with positions */
1270 fn = g_build_filename (mc_config_get_cache_path (), MC_FILEPOS_FILE, NULL);
1271 f = fopen (fn, "r");
1272 g_free (fn);
1273 if (f == NULL)
1274 return;
1276 /* prepare array for serialized bookmarks */
1277 *bookmarks = g_array_sized_new (FALSE, FALSE, sizeof (size_t), MAX_SAVED_BOOKMARKS);
1279 while (fgets (buf, sizeof (buf), f) != NULL)
1281 const char *p;
1282 gchar **pos_tokens;
1284 /* check if the filename matches the beginning of string */
1285 if (strncmp (buf, filename, len) != 0)
1286 continue;
1288 /* followed by single space */
1289 if (buf[len] != ' ')
1290 continue;
1292 /* and string without spaces */
1293 p = &buf[len + 1];
1294 if (strchr (p, ' ') != NULL)
1295 continue;
1297 pos_tokens = g_strsplit (p, ";", 3 + MAX_SAVED_BOOKMARKS);
1298 if (pos_tokens[0] == NULL)
1300 *line = 1;
1301 *column = 0;
1302 *offset = 0;
1304 else
1306 *line = strtol (pos_tokens[0], NULL, 10);
1307 if (pos_tokens[1] == NULL)
1309 *column = 0;
1310 *offset = 0;
1312 else
1314 *column = strtol (pos_tokens[1], NULL, 10);
1315 if (pos_tokens[2] == NULL)
1316 *offset = 0;
1317 else
1319 size_t i;
1321 *offset = strtoll (pos_tokens[2], NULL, 10);
1323 for (i = 0; i < MAX_SAVED_BOOKMARKS && pos_tokens[3 + i] != NULL; i++)
1325 size_t val;
1327 val = strtoul (pos_tokens[3 + i], NULL, 10);
1328 g_array_append_val (*bookmarks, val);
1334 g_strfreev (pos_tokens);
1337 fclose (f);
1340 /* --------------------------------------------------------------------------------------------- */
1342 * Save position for the given file
1345 void
1346 save_file_position (const char *filename, long line, long column, off_t offset, GArray * bookmarks)
1348 static size_t filepos_max_saved_entries = 0;
1349 char *fn, *tmp_fn;
1350 FILE *f, *tmp_f;
1351 char buf[MC_MAXPATHLEN + 100];
1352 size_t i;
1353 const size_t len = strlen (filename);
1354 gboolean src_error = FALSE;
1356 if (filepos_max_saved_entries == 0)
1357 filepos_max_saved_entries = mc_config_get_int (mc_main_config, CONFIG_APP_SECTION,
1358 "filepos_max_saved_entries", 1024);
1360 fn = g_build_filename (mc_config_get_cache_path (), MC_FILEPOS_FILE, NULL);
1361 if (fn == NULL)
1362 goto early_error;
1364 mc_util_make_backup_if_possible (fn, TMP_SUFFIX);
1366 /* open file */
1367 f = fopen (fn, "w");
1368 if (f == NULL)
1369 goto open_target_error;
1371 tmp_fn = g_strdup_printf ("%s" TMP_SUFFIX, fn);
1372 tmp_f = fopen (tmp_fn, "r");
1373 if (tmp_f == NULL)
1375 src_error = TRUE;
1376 goto open_source_error;
1379 /* put the new record */
1380 if (line != 1 || column != 0 || bookmarks != NULL)
1382 if (fprintf (f, "%s %ld;%ld;%" PRIuMAX, filename, line, column, (uintmax_t) offset) < 0)
1383 goto write_position_error;
1384 if (bookmarks != NULL)
1385 for (i = 0; i < bookmarks->len && i < MAX_SAVED_BOOKMARKS; i++)
1386 if (fprintf (f, ";%zu", g_array_index (bookmarks, size_t, i)) < 0)
1387 goto write_position_error;
1389 if (fprintf (f, "\n") < 0)
1390 goto write_position_error;
1393 i = 1;
1394 while (fgets (buf, sizeof (buf), tmp_f) != NULL)
1396 if (buf[len] == ' ' && strncmp (buf, filename, len) == 0
1397 && strchr (&buf[len + 1], ' ') == NULL)
1398 continue;
1400 fprintf (f, "%s", buf);
1401 if (++i > filepos_max_saved_entries)
1402 break;
1405 write_position_error:
1406 fclose (tmp_f);
1407 open_source_error:
1408 g_free (tmp_fn);
1409 fclose (f);
1410 if (src_error)
1411 mc_util_restore_from_backup_if_possible (fn, TMP_SUFFIX);
1412 else
1413 mc_util_unlink_backup_if_possible (fn, TMP_SUFFIX);
1414 open_target_error:
1415 g_free (fn);
1416 early_error:
1417 if (bookmarks != NULL)
1418 g_array_free (bookmarks, TRUE);
1421 /* --------------------------------------------------------------------------------------------- */
1423 extern int
1424 ascii_alpha_to_cntrl (int ch)
1426 if ((ch >= ASCII_A && ch <= ASCII_Z) || (ch >= ASCII_a && ch <= ASCII_z))
1428 ch &= 0x1f;
1430 return ch;
1433 /* --------------------------------------------------------------------------------------------- */
1435 const char *
1436 Q_ (const char *s)
1438 const char *result, *sep;
1440 result = _(s);
1441 sep = strchr (result, '|');
1442 return (sep != NULL) ? sep + 1 : result;
1445 /* --------------------------------------------------------------------------------------------- */
1447 gboolean
1448 mc_util_make_backup_if_possible (const char *file_name, const char *backup_suffix)
1450 struct stat stat_buf;
1451 char *backup_path;
1452 gboolean ret;
1453 if (!exist_file (file_name))
1454 return FALSE;
1456 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1458 if (backup_path == NULL)
1459 return FALSE;
1461 ret = mc_util_write_backup_content (file_name, backup_path);
1463 if (ret)
1465 /* Backup file will have same ownership with main file. */
1466 if (stat (file_name, &stat_buf) == 0)
1467 chmod (backup_path, stat_buf.st_mode);
1468 else
1469 chmod (backup_path, S_IRUSR | S_IWUSR);
1472 g_free (backup_path);
1474 return ret;
1477 /* --------------------------------------------------------------------------------------------- */
1479 gboolean
1480 mc_util_restore_from_backup_if_possible (const char *file_name, const char *backup_suffix)
1482 gboolean ret;
1483 char *backup_path;
1485 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1486 if (backup_path == NULL)
1487 return FALSE;
1489 ret = mc_util_write_backup_content (backup_path, file_name);
1490 g_free (backup_path);
1492 return ret;
1495 /* --------------------------------------------------------------------------------------------- */
1497 gboolean
1498 mc_util_unlink_backup_if_possible (const char *file_name, const char *backup_suffix)
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 if (exist_file (backup_path))
1507 mc_unlink (backup_path);
1509 g_free (backup_path);
1510 return TRUE;
1513 /* --------------------------------------------------------------------------------------------- */
1515 * partly taken from dcigettext.c, returns "" for default locale
1516 * value should be freed by calling function g_free()
1519 char *
1520 guess_message_value (void)
1522 static const char *const var[] = {
1523 /* Setting of LC_ALL overwrites all other. */
1524 /* Do not use LANGUAGE for check user locale and drowing hints */
1525 "LC_ALL",
1526 /* Next comes the name of the desired category. */
1527 "LC_MESSAGES",
1528 /* Last possibility is the LANG environment variable. */
1529 "LANG",
1530 /* NULL exit loops */
1531 NULL
1534 unsigned i = 0;
1535 const char *locale = NULL;
1537 while (var[i] != NULL)
1539 locale = getenv (var[i]);
1540 if (locale != NULL && locale[0] != '\0')
1541 break;
1542 i++;
1545 if (locale == NULL)
1546 locale = "";
1548 return g_strdup (locale);
1551 /* --------------------------------------------------------------------------------------------- */