FISH: added external script 'utime'
[midnight-commander.git] / lib / util.c
blobcb6835fee7e3ba495e64a145101e0dd973a8808b
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;
120 if (*path != PATH_SEP)
121 return NULL;
122 r = buf = g_malloc (MC_MAXPATHLEN);
123 buf2 = g_malloc (MC_MAXPATHLEN);
124 *r++ = PATH_SEP;
125 *r = 0;
126 p = path;
127 for (;;)
129 q = strchr (p + 1, PATH_SEP);
130 if (!q)
132 q = strchr (p + 1, 0);
133 if (q == p + 1)
134 break;
136 c = *q;
137 *q = 0;
138 if (mc_lstat (path, &mybuf) < 0)
140 g_free (buf);
141 g_free (buf2);
142 *q = c;
143 return NULL;
145 if (!S_ISLNK (mybuf.st_mode))
146 strcpy (r, p + 1);
147 else
149 len = mc_readlink (path, buf2, MC_MAXPATHLEN - 1);
150 if (len < 0)
152 g_free (buf);
153 g_free (buf2);
154 *q = c;
155 return NULL;
157 buf2[len] = 0;
158 if (*buf2 == PATH_SEP)
159 strcpy (buf, buf2);
160 else
161 strcpy (r, buf2);
163 canonicalize_pathname (buf);
164 r = strchr (buf, 0);
165 if (!*r || *(r - 1) != PATH_SEP)
167 *r++ = PATH_SEP;
168 *r = 0;
170 *q = c;
171 p = q;
172 if (!c)
173 break;
175 if (!*buf)
176 strcpy (buf, PATH_SEP_STR);
177 else if (*(r - 1) == PATH_SEP && r != buf + 1)
178 *(r - 1) = 0;
179 g_free (buf2);
180 return buf;
183 /* --------------------------------------------------------------------------------------------- */
185 static gboolean
186 mc_util_write_backup_content (const char *from_file_name, const char *to_file_name)
188 FILE *backup_fd;
189 char *contents;
190 gsize length;
191 gboolean ret1 = TRUE;
193 if (!g_file_get_contents (from_file_name, &contents, &length, NULL))
194 return FALSE;
196 backup_fd = fopen (to_file_name, "w");
197 if (backup_fd == NULL)
199 g_free (contents);
200 return FALSE;
203 if (fwrite ((const void *) contents, 1, length, backup_fd) != length)
204 ret1 = FALSE;
206 int ret2;
207 ret2 = fflush (backup_fd);
208 ret2 = fclose (backup_fd);
210 g_free (contents);
211 return ret1;
214 /* --------------------------------------------------------------------------------------------- */
215 /*** public functions ****************************************************************************/
216 /* --------------------------------------------------------------------------------------------- */
219 is_printable (int c)
221 c &= 0xff;
223 #ifdef HAVE_CHARSET
224 /* "Display bits" is ignored, since the user controls the output
225 by setting the output codepage */
226 return is_8bit_printable (c);
227 #else
228 if (!mc_global.eight_bit_clean)
229 return is_7bit_printable (c);
231 if (mc_global.full_eight_bits)
233 return is_8bit_printable (c);
235 else
236 return is_iso_printable (c);
237 #endif /* !HAVE_CHARSET */
240 /* --------------------------------------------------------------------------------------------- */
242 * Quote the filename for the purpose of inserting it into the command
243 * line. If quote_percent is 1, replace "%" with "%%" - the percent is
244 * processed by the mc command line.
246 char *
247 name_quote (const char *s, int quote_percent)
249 char *ret, *d;
251 d = ret = g_malloc (strlen (s) * 2 + 2 + 1);
252 if (*s == '-')
254 *d++ = '.';
255 *d++ = '/';
258 for (; *s; s++, d++)
260 switch (*s)
262 case '%':
263 if (quote_percent)
264 *d++ = '%';
265 break;
266 case '\'':
267 case '\\':
268 case '\r':
269 case '\n':
270 case '\t':
271 case '"':
272 case ';':
273 case ' ':
274 case '?':
275 case '|':
276 case '[':
277 case ']':
278 case '{':
279 case '}':
280 case '<':
281 case '>':
282 case '`':
283 case '!':
284 case '$':
285 case '&':
286 case '*':
287 case '(':
288 case ')':
289 *d++ = '\\';
290 break;
291 case '~':
292 case '#':
293 if (d == ret)
294 *d++ = '\\';
295 break;
297 *d = *s;
299 *d = '\0';
300 return ret;
303 /* --------------------------------------------------------------------------------------------- */
305 char *
306 fake_name_quote (const char *s, int quote_percent)
308 (void) quote_percent;
309 return g_strdup (s);
312 /* --------------------------------------------------------------------------------------------- */
314 * path_trunc() is the same as str_trunc() but
315 * it deletes possible password from path for security
316 * reasons.
319 const char *
320 path_trunc (const char *path, size_t trunc_len)
322 char *secure_path = strip_password (g_strdup (path), 1);
324 const char *ret = str_trunc (secure_path, trunc_len);
325 g_free (secure_path);
327 return ret;
330 /* --------------------------------------------------------------------------------------------- */
332 const char *
333 size_trunc (uintmax_t size, gboolean use_si)
335 static char x[BUF_TINY];
336 uintmax_t divisor = 1;
337 const char *xtra = "";
339 if (size > 999999999UL)
341 divisor = use_si ? 1000 : 1024;
342 xtra = use_si ? "k" : "K";
344 if (size / divisor > 999999999UL)
346 divisor = use_si ? (1000 * 1000) : (1024 * 1024);
347 xtra = use_si ? "m" : "M";
349 if (size / divisor > 999999999UL)
351 divisor = use_si ? (1000 * 1000 * 1000) : (1024 * 1024 * 1024);
352 xtra = use_si ? "g" : "G";
356 g_snprintf (x, sizeof (x), "%.0f%s", 1.0 * size / divisor, xtra);
357 return x;
360 /* --------------------------------------------------------------------------------------------- */
362 const char *
363 size_trunc_sep (uintmax_t size, gboolean use_si)
365 static char x[60];
366 int count;
367 const char *p, *y;
368 char *d;
370 p = y = size_trunc (size, use_si);
371 p += strlen (p) - 1;
372 d = x + sizeof (x) - 1;
373 *d-- = '\0';
374 while (p >= y && isalpha ((unsigned char) *p))
375 *d-- = *p--;
376 for (count = 0; p >= y; count++)
378 if (count == 3)
380 *d-- = ',';
381 count = 0;
383 *d-- = *p--;
385 d++;
386 if (*d == ',')
387 d++;
388 return d;
391 /* --------------------------------------------------------------------------------------------- */
393 * Print file SIZE to BUFFER, but don't exceed LEN characters,
394 * not including trailing 0. BUFFER should be at least LEN+1 long.
395 * This function is called for every file on panels, so avoid
396 * floating point by any means.
398 * Units: size units (filesystem sizes are 1K blocks)
399 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
402 void
403 size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gboolean use_si)
405 /* Avoid taking power for every file. */
406 /* *INDENT-OFF* */
407 static const uintmax_t power10[] = {
408 /* we hope that size of uintmax_t is 4 bytes at least */
409 1ULL,
410 10ULL,
411 100ULL,
412 1000ULL,
413 10000ULL,
414 100000ULL,
415 1000000ULL,
416 10000000ULL,
417 100000000ULL,
418 1000000000ULL
419 /* maximmum value of uintmax_t (in case of 4 bytes) is
420 4294967295
422 #if SIZEOF_UINTMAX_T == 8
424 10000000000ULL,
425 100000000000ULL,
426 1000000000000ULL,
427 10000000000000ULL,
428 100000000000000ULL,
429 1000000000000000ULL,
430 10000000000000000ULL,
431 100000000000000000ULL,
432 1000000000000000000ULL,
433 10000000000000000000ULL
434 /* maximmum value of uintmax_t (in case of 8 bytes) is
435 18447644073710439615
437 #endif
439 /* *INDENT-ON* */
440 static const char *const suffix[] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL };
441 static const char *const suffix_lc[] = { "", "k", "m", "g", "t", "p", "e", "z", "y", NULL };
442 int j = 0;
444 if (len == 0)
445 len = 9;
446 #if SIZEOF_UINTMAX_T == 8
447 /* 20 decimal digits are required to represent 8 bytes */
448 else if (len > 19)
449 len = 19;
450 #else
451 /* 10 decimal digits are required to represent 4 bytes */
452 else if (len > 9)
453 len = 9;
454 #endif
457 * recalculate from 1024 base to 1000 base if units>0
458 * We can't just multiply by 1024 - that might cause overflow
459 * if off_t type is too small
461 if (use_si)
462 for (j = 0; j < units; j++)
464 uintmax_t size_remain;
466 size_remain = ((size % 125) * 1024) / 1000; /* size mod 125, recalculated */
467 size = size / 125; /* 128/125 = 1024/1000 */
468 size = size * 128; /* This will convert size from multiple of 1024 to multiple of 1000 */
469 size += size_remain; /* Re-add remainder lost by division/multiplication */
472 for (j = units; suffix[j] != NULL; j++)
474 if (size == 0)
476 if (j == units)
478 /* Empty files will print "0" even with minimal width. */
479 g_snprintf (buffer, len + 1, "0");
480 break;
483 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
484 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s",
485 (j > 1) ? (use_si ? suffix_lc[j - 1] : suffix[j - 1]) : "B");
486 break;
489 if (size < power10[len - (j > 0 ? 1 : 0)])
491 g_snprintf (buffer, len + 1, "%" PRIuMAX "%s", size, use_si ? suffix_lc[j] : suffix[j]);
492 break;
495 /* Powers of 1000 or 1024, with rounding. */
496 if (use_si)
497 size = (size + 500) / 1000;
498 else
499 size = (size + 512) >> 10;
503 /* --------------------------------------------------------------------------------------------- */
505 const char *
506 string_perm (mode_t mode_bits)
508 static char mode[11];
510 strcpy (mode, "----------");
511 if (S_ISDIR (mode_bits))
512 mode[0] = 'd';
513 if (S_ISCHR (mode_bits))
514 mode[0] = 'c';
515 if (S_ISBLK (mode_bits))
516 mode[0] = 'b';
517 if (S_ISLNK (mode_bits))
518 mode[0] = 'l';
519 if (S_ISFIFO (mode_bits))
520 mode[0] = 'p';
521 if (S_ISNAM (mode_bits))
522 mode[0] = 'n';
523 if (S_ISSOCK (mode_bits))
524 mode[0] = 's';
525 if (S_ISDOOR (mode_bits))
526 mode[0] = 'D';
527 if (ismode (mode_bits, S_IXOTH))
528 mode[9] = 'x';
529 if (ismode (mode_bits, S_IWOTH))
530 mode[8] = 'w';
531 if (ismode (mode_bits, S_IROTH))
532 mode[7] = 'r';
533 if (ismode (mode_bits, S_IXGRP))
534 mode[6] = 'x';
535 if (ismode (mode_bits, S_IWGRP))
536 mode[5] = 'w';
537 if (ismode (mode_bits, S_IRGRP))
538 mode[4] = 'r';
539 if (ismode (mode_bits, S_IXUSR))
540 mode[3] = 'x';
541 if (ismode (mode_bits, S_IWUSR))
542 mode[2] = 'w';
543 if (ismode (mode_bits, S_IRUSR))
544 mode[1] = 'r';
545 #ifdef S_ISUID
546 if (ismode (mode_bits, S_ISUID))
547 mode[3] = (mode[3] == 'x') ? 's' : 'S';
548 #endif /* S_ISUID */
549 #ifdef S_ISGID
550 if (ismode (mode_bits, S_ISGID))
551 mode[6] = (mode[6] == 'x') ? 's' : 'S';
552 #endif /* S_ISGID */
553 #ifdef S_ISVTX
554 if (ismode (mode_bits, S_ISVTX))
555 mode[9] = (mode[9] == 'x') ? 't' : 'T';
556 #endif /* S_ISVTX */
557 return mode;
560 /* --------------------------------------------------------------------------------------------- */
562 * p: string which might contain an url with a password (this parameter is
563 * modified in place).
564 * has_prefix = 0: The first parameter is an url without a prefix
565 * (user[:pass]@]machine[:port][remote-dir). Delete
566 * the password.
567 * has_prefix = 1: Search p for known url prefixes. If found delete
568 * the password from the url.
569 * Caveat: only the first url is found
572 char *
573 strip_password (char *p, int has_prefix)
575 static const struct
577 const char *name;
578 size_t len;
579 } prefixes[] =
581 /* *INDENT-OFF* */
582 { "/#ftp:", 6 },
583 { "ftp://", 6 },
584 { "/#smb:", 6 },
585 { "smb://", 6 },
586 { "/#sh:", 5 },
587 { "sh://", 5 },
588 { "ssh://", 6 }
589 /* *INDENT-ON* */
592 char *at, *inner_colon, *dir;
593 size_t i;
594 char *result = p;
596 for (i = 0; i < sizeof (prefixes) / sizeof (prefixes[0]); i++)
598 char *q;
600 if (has_prefix)
602 q = strstr (p, prefixes[i].name);
603 if (q == NULL)
604 continue;
605 else
606 p = q + prefixes[i].len;
609 dir = strchr (p, PATH_SEP);
610 if (dir != NULL)
611 *dir = '\0';
613 /* search for any possible user */
614 at = strrchr (p, '@');
616 if (dir)
617 *dir = PATH_SEP;
619 /* We have a username */
620 if (at)
622 inner_colon = memchr (p, ':', at - p);
623 if (inner_colon)
624 memmove (inner_colon, at, strlen (at) + 1);
626 break;
628 return (result);
631 /* --------------------------------------------------------------------------------------------- */
633 const char *
634 strip_home_and_password (const char *dir)
636 size_t len;
637 static char newdir[MC_MAXPATHLEN];
639 len = strlen (mc_config_get_home_dir ());
640 if (mc_config_get_home_dir () != NULL && strncmp (dir, mc_config_get_home_dir (), len) == 0 &&
641 (dir[len] == PATH_SEP || dir[len] == '\0'))
643 newdir[0] = '~';
644 g_strlcpy (&newdir[1], &dir[len], sizeof (newdir) - 1);
645 return newdir;
648 /* We do not strip homes in /#ftp tree, I do not like ~'s there
649 (see ftpfs.c why) */
650 g_strlcpy (newdir, dir, sizeof (newdir));
651 strip_password (newdir, 1);
652 return newdir;
655 /* --------------------------------------------------------------------------------------------- */
657 const char *
658 extension (const char *filename)
660 const char *d = strrchr (filename, '.');
661 return (d != NULL) ? d + 1 : "";
664 /* --------------------------------------------------------------------------------------------- */
666 char *
667 load_mc_home_file (const char *from, const char *filename, char **allocated_filename)
669 char *hintfile_base, *hintfile;
670 char *lang;
671 char *data;
673 hintfile_base = g_build_filename (from, filename, (char *) NULL);
674 lang = guess_message_value ();
676 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
677 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
679 /* Fall back to the two-letter language code */
680 if (lang[0] != '\0' && lang[1] != '\0')
681 lang[2] = '\0';
682 g_free (hintfile);
683 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
684 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
686 g_free (hintfile);
687 hintfile = hintfile_base;
688 g_file_get_contents (hintfile_base, &data, NULL, NULL);
692 g_free (lang);
694 if (hintfile != hintfile_base)
695 g_free (hintfile_base);
697 if (allocated_filename != NULL)
698 *allocated_filename = hintfile;
699 else
700 g_free (hintfile);
702 return data;
705 /* --------------------------------------------------------------------------------------------- */
707 const char *
708 extract_line (const char *s, const char *top)
710 static char tmp_line[BUF_MEDIUM];
711 char *t = tmp_line;
713 while (*s && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line) - 1 && s < top)
714 *t++ = *s++;
715 *t = 0;
716 return tmp_line;
719 /* --------------------------------------------------------------------------------------------- */
721 * The basename routine
724 const char *
725 x_basename (const char *s)
727 const char *url_delim, *path_sep;
729 url_delim = g_strrstr (s, VFS_PATH_URL_DELIMITER);
730 path_sep = strrchr (s, PATH_SEP);
732 if (url_delim == NULL
733 || url_delim < path_sep - strlen (VFS_PATH_URL_DELIMITER)
734 || url_delim - s + strlen (VFS_PATH_URL_DELIMITER) < strlen (s))
736 /* avoid trailing PATH_SEP, if present */
737 if (s[strlen (s) - 1] == PATH_SEP)
739 while (--path_sep > s && *path_sep != PATH_SEP);
740 return (path_sep != s) ? path_sep + 1 : s;
742 else
743 return (path_sep != NULL) ? path_sep + 1 : s;
746 while (--url_delim > s && *url_delim != PATH_SEP);
747 while (--url_delim > s && *url_delim != PATH_SEP);
749 return (url_delim == s) ? s : url_delim + 1;
752 /* --------------------------------------------------------------------------------------------- */
754 const char *
755 unix_error_string (int error_num)
757 static char buffer[BUF_LARGE];
758 gchar *strerror_currentlocale;
760 strerror_currentlocale = g_locale_from_utf8 (g_strerror (error_num), -1, NULL, NULL, NULL);
761 g_snprintf (buffer, sizeof (buffer), "%s (%d)", strerror_currentlocale, error_num);
762 g_free (strerror_currentlocale);
764 return buffer;
767 /* --------------------------------------------------------------------------------------------- */
769 const char *
770 skip_separators (const char *s)
772 const char *su = s;
774 for (; *su; str_cnext_char (&su))
775 if (*su != ' ' && *su != '\t' && *su != ',')
776 break;
778 return su;
781 /* --------------------------------------------------------------------------------------------- */
783 const char *
784 skip_numbers (const char *s)
786 const char *su = s;
788 for (; *su; str_cnext_char (&su))
789 if (!str_isdigit (su))
790 break;
792 return su;
795 /* --------------------------------------------------------------------------------------------- */
797 * Remove all control sequences from the argument string. We define
798 * "control sequence", in a sort of pidgin BNF, as follows:
800 * control-seq = Esc non-'['
801 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
803 * This scheme works for all the terminals described in my termcap /
804 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
805 * terminals. If I hear from a single person who uses such a terminal
806 * with MC, I'll be glad to add support for it. (Dugan)
807 * Non-printable characters are also removed.
810 char *
811 strip_ctrl_codes (char *s)
813 char *w; /* Current position where the stripped data is written */
814 char *r; /* Current position where the original data is read */
815 char *n;
817 if (!s)
818 return 0;
820 for (w = s, r = s; *r;)
822 if (*r == ESC_CHAR)
824 /* Skip the control sequence's arguments */ ;
825 /* '(' need to avoid strange 'B' letter in *Suse (if mc runs under root user) */
826 if (*(++r) == '[' || *r == '(')
828 /* strchr() matches trailing binary 0 */
829 while (*(++r) && strchr ("0123456789;?", *r));
831 else if (*r == ']')
834 * Skip xterm's OSC (Operating System Command)
835 * http://www.xfree86.org/current/ctlseqs.html
836 * OSC P s ; P t ST
837 * OSC P s ; P t BEL
839 char *new_r = r;
841 for (; *new_r; ++new_r)
843 switch (*new_r)
845 /* BEL */
846 case '\a':
847 r = new_r;
848 goto osc_out;
849 case ESC_CHAR:
850 /* ST */
851 if (*(new_r + 1) == '\\')
853 r = new_r + 1;
854 goto osc_out;
858 osc_out:;
862 * Now we are at the last character of the sequence.
863 * Skip it unless it's binary 0.
865 if (*r)
866 r++;
867 continue;
870 n = str_get_next_char (r);
871 if (str_isprint (r))
873 memmove (w, r, n - r);
874 w += n - r;
876 r = n;
878 *w = 0;
879 return s;
882 /* --------------------------------------------------------------------------------------------- */
884 enum compression_type
885 get_compression_type (int fd, const char *name)
887 unsigned char magic[16];
888 size_t str_len;
890 /* Read the magic signature */
891 if (mc_read (fd, (char *) magic, 4) != 4)
892 return COMPRESSION_NONE;
894 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
895 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236))
897 return COMPRESSION_GZIP;
900 /* PKZIP_MAGIC */
901 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003 && magic[3] == 004)
903 /* Read compression type */
904 mc_lseek (fd, 8, SEEK_SET);
905 if (mc_read (fd, (char *) magic, 2) != 2)
906 return COMPRESSION_NONE;
908 /* Gzip can handle only deflated (8) or stored (0) files */
909 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
910 return COMPRESSION_NONE;
912 /* Compatible with gzip */
913 return COMPRESSION_GZIP;
916 /* PACK_MAGIC and LZH_MAGIC and compress magic */
917 if (magic[0] == 037 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235))
919 /* Compatible with gzip */
920 return COMPRESSION_GZIP;
923 /* BZIP and BZIP2 files */
924 if ((magic[0] == 'B') && (magic[1] == 'Z') && (magic[3] >= '1') && (magic[3] <= '9'))
926 switch (magic[2])
928 case '0':
929 return COMPRESSION_BZIP;
930 case 'h':
931 return COMPRESSION_BZIP2;
935 /* Support for LZMA (only utils format with magic in header).
936 * This is the default format of LZMA utils 4.32.1 and later. */
938 if (mc_read (fd, (char *) magic + 4, 2) != 2)
939 return COMPRESSION_NONE;
941 /* LZMA utils format */
942 if (magic[0] == 0xFF
943 && magic[1] == 'L'
944 && magic[2] == 'Z' && magic[3] == 'M' && magic[4] == 'A' && magic[5] == 0x00)
945 return COMPRESSION_LZMA;
947 /* XZ compression magic */
948 if (magic[0] == 0xFD
949 && magic[1] == 0x37
950 && magic[2] == 0x7A && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00)
951 return COMPRESSION_XZ;
953 str_len = strlen (name);
954 /* HACK: we must belive to extention of LZMA file :) ... */
955 if ((str_len > 5 && strcmp (&name[str_len - 5], ".lzma") == 0) ||
956 (str_len > 4 && strcmp (&name[str_len - 4], ".tlz") == 0))
957 return COMPRESSION_LZMA;
959 return COMPRESSION_NONE;
962 /* --------------------------------------------------------------------------------------------- */
964 const char *
965 decompress_extension (int type)
967 switch (type)
969 case COMPRESSION_GZIP:
970 return "/ugz" VFS_PATH_URL_DELIMITER;
971 case COMPRESSION_BZIP:
972 return "/ubz" VFS_PATH_URL_DELIMITER;
973 case COMPRESSION_BZIP2:
974 return "/ubz2" VFS_PATH_URL_DELIMITER;
975 case COMPRESSION_LZMA:
976 return "/ulzma" VFS_PATH_URL_DELIMITER;
977 case COMPRESSION_XZ:
978 return "/uxz" VFS_PATH_URL_DELIMITER;
980 /* Should never reach this place */
981 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
982 return 0;
985 /* --------------------------------------------------------------------------------------------- */
987 void
988 wipe_password (char *passwd)
990 char *p = passwd;
992 if (!p)
993 return;
994 for (; *p; p++)
995 *p = 0;
996 g_free (passwd);
999 /* --------------------------------------------------------------------------------------------- */
1001 * Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key
1002 * @returns a newly allocated string
1005 char *
1006 convert_controls (const char *p)
1008 char *valcopy = g_strdup (p);
1009 char *q;
1011 /* Parse the escape special character */
1012 for (q = valcopy; *p;)
1014 if (*p == '\\')
1016 p++;
1017 if ((*p == 'e') || (*p == 'E'))
1019 p++;
1020 *q++ = ESC_CHAR;
1023 else
1025 if (*p == '^')
1027 p++;
1028 if (*p == '^')
1029 *q++ = *p++;
1030 else
1032 char c = (*p | 0x20);
1033 if (c >= 'a' && c <= 'z')
1035 *q++ = c - 'a' + 1;
1036 p++;
1038 else if (*p)
1039 p++;
1042 else
1043 *q++ = *p++;
1046 *q = 0;
1047 return valcopy;
1050 /* --------------------------------------------------------------------------------------------- */
1052 * Finds out a relative path from first to second, i.e. goes as many ..
1053 * as needed up in first and then goes down using second
1056 char *
1057 diff_two_paths (const char *first, const char *second)
1059 char *p, *q, *r, *s, *buf = NULL;
1060 int i, j, prevlen = -1, currlen;
1061 char *my_first = NULL, *my_second = NULL;
1063 my_first = resolve_symlinks (first);
1064 if (my_first == NULL)
1065 return NULL;
1066 my_second = resolve_symlinks (second);
1067 if (my_second == NULL)
1069 g_free (my_first);
1070 return NULL;
1072 for (j = 0; j < 2; j++)
1074 p = my_first;
1075 q = my_second;
1076 for (;;)
1078 r = strchr (p, PATH_SEP);
1079 s = strchr (q, PATH_SEP);
1080 if (!r || !s)
1081 break;
1082 *r = 0;
1083 *s = 0;
1084 if (strcmp (p, q))
1086 *r = PATH_SEP;
1087 *s = PATH_SEP;
1088 break;
1090 else
1092 *r = PATH_SEP;
1093 *s = PATH_SEP;
1095 p = r + 1;
1096 q = s + 1;
1098 p--;
1099 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++);
1100 currlen = (i + 1) * 3 + strlen (q) + 1;
1101 if (j)
1103 if (currlen < prevlen)
1104 g_free (buf);
1105 else
1107 g_free (my_first);
1108 g_free (my_second);
1109 return buf;
1112 p = buf = g_malloc (currlen);
1113 prevlen = currlen;
1114 for (; i >= 0; i--, p += 3)
1115 strcpy (p, "../");
1116 strcpy (p, q);
1118 g_free (my_first);
1119 g_free (my_second);
1120 return buf;
1123 /* --------------------------------------------------------------------------------------------- */
1125 * If filename is NULL, then we just append PATH_SEP to the dir
1128 char *
1129 concat_dir_and_file (const char *dir, const char *file)
1131 int i = strlen (dir);
1133 if (dir[i - 1] == PATH_SEP)
1134 return g_strconcat (dir, file, (char *) NULL);
1135 else
1136 return g_strconcat (dir, PATH_SEP_STR, file, (char *) NULL);
1139 /* --------------------------------------------------------------------------------------------- */
1141 * Append text to GList, remove all entries with the same text
1144 GList *
1145 list_append_unique (GList * list, char *text)
1147 GList *lc_link;
1150 * Go to the last position and traverse the list backwards
1151 * starting from the second last entry to make sure that we
1152 * are not removing the current link.
1154 list = g_list_append (list, text);
1155 list = g_list_last (list);
1156 lc_link = g_list_previous (list);
1158 while (lc_link != NULL)
1160 GList *newlink;
1162 newlink = g_list_previous (lc_link);
1163 if (strcmp ((char *) lc_link->data, text) == 0)
1165 GList *tmp;
1167 g_free (lc_link->data);
1168 tmp = g_list_remove_link (list, lc_link);
1169 g_list_free_1 (lc_link);
1171 lc_link = newlink;
1174 return list;
1177 /* --------------------------------------------------------------------------------------------- */
1178 /* Following code heavily borrows from libiberty, mkstemps.c */
1180 * Arguments:
1181 * pname (output) - pointer to the name of the temp file (needs g_free).
1182 * NULL if the function fails.
1183 * prefix - part of the filename before the random part.
1184 * Prepend $TMPDIR or /tmp if there are no path separators.
1185 * suffix - if not NULL, part of the filename after the random part.
1187 * Result:
1188 * handle of the open file or -1 if couldn't open any.
1192 mc_mkstemps (char **pname, const char *prefix, const char *suffix)
1194 static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1195 static unsigned long value;
1196 struct timeval tv;
1197 char *tmpbase;
1198 char *tmpname;
1199 char *XXXXXX;
1200 int count;
1202 if (strchr (prefix, PATH_SEP) == NULL)
1204 /* Add prefix first to find the position of XXXXXX */
1205 tmpbase = concat_dir_and_file (mc_tmpdir (), prefix);
1207 else
1209 tmpbase = g_strdup (prefix);
1212 tmpname = g_strconcat (tmpbase, "XXXXXX", suffix, (char *) NULL);
1213 *pname = tmpname;
1214 XXXXXX = &tmpname[strlen (tmpbase)];
1215 g_free (tmpbase);
1217 /* Get some more or less random data. */
1218 gettimeofday (&tv, NULL);
1219 value += (tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
1221 for (count = 0; count < TMP_MAX; ++count)
1223 unsigned long v = value;
1224 int fd;
1226 /* Fill in the random bits. */
1227 XXXXXX[0] = letters[v % 62];
1228 v /= 62;
1229 XXXXXX[1] = letters[v % 62];
1230 v /= 62;
1231 XXXXXX[2] = letters[v % 62];
1232 v /= 62;
1233 XXXXXX[3] = letters[v % 62];
1234 v /= 62;
1235 XXXXXX[4] = letters[v % 62];
1236 v /= 62;
1237 XXXXXX[5] = letters[v % 62];
1239 fd = open (tmpname, O_RDWR | O_CREAT | O_TRUNC | O_EXCL, S_IRUSR | S_IWUSR);
1240 if (fd >= 0)
1242 /* Successfully created. */
1243 return fd;
1246 /* This is a random value. It is only necessary that the next
1247 TMP_MAX values generated by adding 7777 to VALUE are different
1248 with (module 2^32). */
1249 value += 7777;
1252 /* Unsuccessful. Free the filename. */
1253 g_free (tmpname);
1254 *pname = NULL;
1256 return -1;
1259 /* --------------------------------------------------------------------------------------------- */
1261 * Read and restore position for the given filename.
1262 * If there is no stored data, return line 1 and col 0.
1265 void
1266 load_file_position (const char *filename, long *line, long *column, off_t * offset,
1267 GArray ** bookmarks)
1269 char *fn;
1270 FILE *f;
1271 char buf[MC_MAXPATHLEN + 100];
1272 const size_t len = strlen (filename);
1274 /* defaults */
1275 *line = 1;
1276 *column = 0;
1277 *offset = 0;
1279 /* open file with positions */
1280 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1281 f = fopen (fn, "r");
1282 g_free (fn);
1283 if (f == NULL)
1284 return;
1286 /* prepare array for serialized bookmarks */
1287 *bookmarks = g_array_sized_new (FALSE, FALSE, sizeof (size_t), MAX_SAVED_BOOKMARKS);
1289 while (fgets (buf, sizeof (buf), f) != NULL)
1291 const char *p;
1292 gchar **pos_tokens;
1294 /* check if the filename matches the beginning of string */
1295 if (strncmp (buf, filename, len) != 0)
1296 continue;
1298 /* followed by single space */
1299 if (buf[len] != ' ')
1300 continue;
1302 /* and string without spaces */
1303 p = &buf[len + 1];
1304 if (strchr (p, ' ') != NULL)
1305 continue;
1307 pos_tokens = g_strsplit (p, ";", 3 + MAX_SAVED_BOOKMARKS);
1308 if (pos_tokens[0] == NULL)
1310 *line = 1;
1311 *column = 0;
1312 *offset = 0;
1314 else
1316 *line = strtol (pos_tokens[0], NULL, 10);
1317 if (pos_tokens[1] == NULL)
1319 *column = 0;
1320 *offset = 0;
1322 else
1324 *column = strtol (pos_tokens[1], NULL, 10);
1325 if (pos_tokens[2] == NULL)
1326 *offset = 0;
1327 else
1329 size_t i;
1331 *offset = strtoll (pos_tokens[2], NULL, 10);
1333 for (i = 0; i < MAX_SAVED_BOOKMARKS && pos_tokens[3 + i] != NULL; i++)
1335 size_t val;
1337 val = strtoul (pos_tokens[3 + i], NULL, 10);
1338 g_array_append_val (*bookmarks, val);
1344 g_strfreev (pos_tokens);
1347 fclose (f);
1350 /* --------------------------------------------------------------------------------------------- */
1352 * Save position for the given file
1355 void
1356 save_file_position (const char *filename, long line, long column, off_t offset, GArray * bookmarks)
1358 static size_t filepos_max_saved_entries = 0;
1359 char *fn, *tmp_fn;
1360 FILE *f, *tmp_f;
1361 char buf[MC_MAXPATHLEN + 100];
1362 size_t i;
1363 const size_t len = strlen (filename);
1364 gboolean src_error = FALSE;
1366 if (filepos_max_saved_entries == 0)
1367 filepos_max_saved_entries = mc_config_get_int (mc_main_config, CONFIG_APP_SECTION,
1368 "filepos_max_saved_entries", 1024);
1370 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1371 if (fn == NULL)
1372 goto early_error;
1374 mc_util_make_backup_if_possible (fn, TMP_SUFFIX);
1376 /* open file */
1377 f = fopen (fn, "w");
1378 if (f == NULL)
1379 goto open_target_error;
1381 tmp_fn = g_strdup_printf ("%s" TMP_SUFFIX, fn);
1382 tmp_f = fopen (tmp_fn, "r");
1383 if (tmp_f == NULL)
1385 src_error = TRUE;
1386 goto open_source_error;
1389 /* put the new record */
1390 if (line != 1 || column != 0 || bookmarks != NULL)
1392 if (fprintf (f, "%s %ld;%ld;%" PRIuMAX, filename, line, column, (uintmax_t) offset) < 0)
1393 goto write_position_error;
1394 if (bookmarks != NULL)
1395 for (i = 0; i < bookmarks->len && i < MAX_SAVED_BOOKMARKS; i++)
1396 if (fprintf (f, ";%zu", g_array_index (bookmarks, size_t, i)) < 0)
1397 goto write_position_error;
1399 if (fprintf (f, "\n") < 0)
1400 goto write_position_error;
1403 i = 1;
1404 while (fgets (buf, sizeof (buf), tmp_f) != NULL)
1406 if (buf[len] == ' ' && strncmp (buf, filename, len) == 0
1407 && strchr (&buf[len + 1], ' ') == NULL)
1408 continue;
1410 fprintf (f, "%s", buf);
1411 if (++i > filepos_max_saved_entries)
1412 break;
1415 write_position_error:
1416 fclose (tmp_f);
1417 open_source_error:
1418 g_free (tmp_fn);
1419 fclose (f);
1420 if (src_error)
1421 mc_util_restore_from_backup_if_possible (fn, TMP_SUFFIX);
1422 else
1423 mc_util_unlink_backup_if_possible (fn, TMP_SUFFIX);
1424 open_target_error:
1425 g_free (fn);
1426 early_error:
1427 if (bookmarks != NULL)
1428 g_array_free (bookmarks, TRUE);
1431 /* --------------------------------------------------------------------------------------------- */
1433 extern int
1434 ascii_alpha_to_cntrl (int ch)
1436 if ((ch >= ASCII_A && ch <= ASCII_Z) || (ch >= ASCII_a && ch <= ASCII_z))
1438 ch &= 0x1f;
1440 return ch;
1443 /* --------------------------------------------------------------------------------------------- */
1445 const char *
1446 Q_ (const char *s)
1448 const char *result, *sep;
1450 result = _(s);
1451 sep = strchr (result, '|');
1452 return (sep != NULL) ? sep + 1 : result;
1455 /* --------------------------------------------------------------------------------------------- */
1457 gboolean
1458 mc_util_make_backup_if_possible (const char *file_name, const char *backup_suffix)
1460 struct stat stat_buf;
1461 char *backup_path;
1462 gboolean ret;
1463 if (!exist_file (file_name))
1464 return FALSE;
1466 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1468 if (backup_path == NULL)
1469 return FALSE;
1471 ret = mc_util_write_backup_content (file_name, backup_path);
1473 if (ret)
1475 /* Backup file will have same ownership with main file. */
1476 if (stat (file_name, &stat_buf) == 0)
1477 chmod (backup_path, stat_buf.st_mode);
1478 else
1479 chmod (backup_path, S_IRUSR | S_IWUSR);
1482 g_free (backup_path);
1484 return ret;
1487 /* --------------------------------------------------------------------------------------------- */
1489 gboolean
1490 mc_util_restore_from_backup_if_possible (const char *file_name, const char *backup_suffix)
1492 gboolean ret;
1493 char *backup_path;
1495 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1496 if (backup_path == NULL)
1497 return FALSE;
1499 ret = mc_util_write_backup_content (backup_path, file_name);
1500 g_free (backup_path);
1502 return ret;
1505 /* --------------------------------------------------------------------------------------------- */
1507 gboolean
1508 mc_util_unlink_backup_if_possible (const char *file_name, const char *backup_suffix)
1510 char *backup_path;
1512 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1513 if (backup_path == NULL)
1514 return FALSE;
1516 if (exist_file (backup_path))
1517 mc_unlink (backup_path);
1519 g_free (backup_path);
1520 return TRUE;
1523 /* --------------------------------------------------------------------------------------------- */
1525 * partly taken from dcigettext.c, returns "" for default locale
1526 * value should be freed by calling function g_free()
1529 char *
1530 guess_message_value (void)
1532 static const char *const var[] = {
1533 /* Setting of LC_ALL overwrites all other. */
1534 /* Do not use LANGUAGE for check user locale and drowing hints */
1535 "LC_ALL",
1536 /* Next comes the name of the desired category. */
1537 "LC_MESSAGES",
1538 /* Last possibility is the LANG environment variable. */
1539 "LANG",
1540 /* NULL exit loops */
1541 NULL
1544 unsigned i = 0;
1545 const char *locale = NULL;
1547 while (var[i] != NULL)
1549 locale = getenv (var[i]);
1550 if (locale != NULL && locale[0] != '\0')
1551 break;
1552 i++;
1555 if (locale == NULL)
1556 locale = "";
1558 return g_strdup (locale);
1561 /* --------------------------------------------------------------------------------------------- */