(size_trunc): added ability to show size in [G|g]bytes.
[midnight-commander.git] / lib / util.c
blob58230bfc12c1095d0d4e7814044ae756d043e61b
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/tty/win.h" /* xterm_flag */
45 #include "lib/mcconfig.h"
46 #include "lib/fileloc.h"
47 #include "lib/vfs/vfs.h"
48 #include "lib/strutil.h"
49 #include "lib/util.h"
51 /*** global variables ****************************************************************************/
53 /*** file scope macro definitions ****************************************************************/
55 #define ismode(n,m) ((n & m) == m)
57 /* Number of attempts to create a temporary file */
58 #ifndef TMP_MAX
59 #define TMP_MAX 16384
60 #endif /* !TMP_MAX */
62 #define TMP_SUFFIX ".tmp"
64 #define ASCII_A (0x40 + 1)
65 #define ASCII_Z (0x40 + 26)
66 #define ASCII_a (0x60 + 1)
67 #define ASCII_z (0x60 + 26)
69 /*** file scope type declarations ****************************************************************/
71 /*** file scope variables ************************************************************************/
73 /*** file scope functions ************************************************************************/
74 /* --------------------------------------------------------------------------------------------- */
76 static inline int
77 is_7bit_printable (unsigned char c)
79 return (c > 31 && c < 127);
82 /* --------------------------------------------------------------------------------------------- */
84 static inline int
85 is_iso_printable (unsigned char c)
87 return ((c > 31 && c < 127) || c >= 160);
90 /* --------------------------------------------------------------------------------------------- */
92 static inline int
93 is_8bit_printable (unsigned char c)
95 /* "Full 8 bits output" doesn't work on xterm */
96 if (xterm_flag)
97 return is_iso_printable (c);
99 return (c > 31 && c != 127 && c != 155);
102 /* --------------------------------------------------------------------------------------------- */
104 static char *
105 resolve_symlinks (const char *path)
107 char *buf, *buf2, *q, *r, c;
108 int len;
109 struct stat mybuf;
110 const char *p;
112 if (*path != PATH_SEP)
113 return NULL;
114 r = buf = g_malloc (MC_MAXPATHLEN);
115 buf2 = g_malloc (MC_MAXPATHLEN);
116 *r++ = PATH_SEP;
117 *r = 0;
118 p = path;
119 for (;;)
121 q = strchr (p + 1, PATH_SEP);
122 if (!q)
124 q = strchr (p + 1, 0);
125 if (q == p + 1)
126 break;
128 c = *q;
129 *q = 0;
130 if (mc_lstat (path, &mybuf) < 0)
132 g_free (buf);
133 g_free (buf2);
134 *q = c;
135 return NULL;
137 if (!S_ISLNK (mybuf.st_mode))
138 strcpy (r, p + 1);
139 else
141 len = mc_readlink (path, buf2, MC_MAXPATHLEN - 1);
142 if (len < 0)
144 g_free (buf);
145 g_free (buf2);
146 *q = c;
147 return NULL;
149 buf2[len] = 0;
150 if (*buf2 == PATH_SEP)
151 strcpy (buf, buf2);
152 else
153 strcpy (r, buf2);
155 canonicalize_pathname (buf);
156 r = strchr (buf, 0);
157 if (!*r || *(r - 1) != PATH_SEP)
159 *r++ = PATH_SEP;
160 *r = 0;
162 *q = c;
163 p = q;
164 if (!c)
165 break;
167 if (!*buf)
168 strcpy (buf, PATH_SEP_STR);
169 else if (*(r - 1) == PATH_SEP && r != buf + 1)
170 *(r - 1) = 0;
171 g_free (buf2);
172 return buf;
175 /* --------------------------------------------------------------------------------------------- */
177 static gboolean
178 mc_util_write_backup_content (const char *from_file_name, const char *to_file_name)
180 FILE *backup_fd;
181 char *contents;
182 gsize length;
183 gboolean ret1 = TRUE;
185 if (!g_file_get_contents (from_file_name, &contents, &length, NULL))
186 return FALSE;
188 backup_fd = fopen (to_file_name, "w");
189 if (backup_fd == NULL)
191 g_free (contents);
192 return FALSE;
195 if (fwrite ((const void *) contents, 1, length, backup_fd) != length)
196 ret1 = FALSE;
198 int ret2;
199 ret2 = fflush (backup_fd);
200 ret2 = fclose (backup_fd);
202 g_free (contents);
203 return ret1;
206 /* --------------------------------------------------------------------------------------------- */
207 /*** public functions ****************************************************************************/
208 /* --------------------------------------------------------------------------------------------- */
211 is_printable (int c)
213 c &= 0xff;
215 #ifdef HAVE_CHARSET
216 /* "Display bits" is ignored, since the user controls the output
217 by setting the output codepage */
218 return is_8bit_printable (c);
219 #else
220 if (!mc_global.eight_bit_clean)
221 return is_7bit_printable (c);
223 if (mc_global.full_eight_bits)
225 return is_8bit_printable (c);
227 else
228 return is_iso_printable (c);
229 #endif /* !HAVE_CHARSET */
232 /* --------------------------------------------------------------------------------------------- */
234 * Quote the filename for the purpose of inserting it into the command
235 * line. If quote_percent is 1, replace "%" with "%%" - the percent is
236 * processed by the mc command line.
238 char *
239 name_quote (const char *s, int quote_percent)
241 char *ret, *d;
243 d = ret = g_malloc (strlen (s) * 2 + 2 + 1);
244 if (*s == '-')
246 *d++ = '.';
247 *d++ = '/';
250 for (; *s; s++, d++)
252 switch (*s)
254 case '%':
255 if (quote_percent)
256 *d++ = '%';
257 break;
258 case '\'':
259 case '\\':
260 case '\r':
261 case '\n':
262 case '\t':
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 case ')':
281 *d++ = '\\';
282 break;
283 case '~':
284 case '#':
285 if (d == ret)
286 *d++ = '\\';
287 break;
289 *d = *s;
291 *d = '\0';
292 return ret;
295 /* --------------------------------------------------------------------------------------------- */
297 char *
298 fake_name_quote (const char *s, int quote_percent)
300 (void) quote_percent;
301 return g_strdup (s);
304 /* --------------------------------------------------------------------------------------------- */
306 * path_trunc() is the same as str_trunc() but
307 * it deletes possible password from path for security
308 * reasons.
311 const char *
312 path_trunc (const char *path, size_t trunc_len)
314 char *secure_path = strip_password (g_strdup (path), 1);
316 const char *ret = str_trunc (secure_path, trunc_len);
317 g_free (secure_path);
319 return ret;
322 /* --------------------------------------------------------------------------------------------- */
324 const char *
325 size_trunc (uintmax_t size, gboolean use_si)
327 static char x[BUF_TINY];
328 uintmax_t divisor = 1;
329 const char *xtra = "";
331 if (size > 999999999UL)
333 divisor = use_si ? 1000 : 1024;
334 xtra = use_si ? "k" : "K";
336 if (size / divisor > 999999999UL)
338 divisor = use_si ? (1000 * 1000) : (1024 * 1024);
339 xtra = use_si ? "m" : "M";
341 if (size / divisor > 999999999UL)
343 divisor = use_si ? (1000 * 1000 * 1000) : (1024 * 1024 * 1024);
344 xtra = use_si ? "g" : "G";
348 g_snprintf (x, sizeof (x), "%.0f%s", 1.0 * size / divisor, xtra);
349 return x;
352 /* --------------------------------------------------------------------------------------------- */
354 const char *
355 size_trunc_sep (uintmax_t size, gboolean use_si)
357 static char x[60];
358 int count;
359 const char *p, *y;
360 char *d;
362 p = y = size_trunc (size, use_si);
363 p += strlen (p) - 1;
364 d = x + sizeof (x) - 1;
365 *d-- = '\0';
366 while (p >= y && isalpha ((unsigned char) *p))
367 *d-- = *p--;
368 for (count = 0; p >= y; count++)
370 if (count == 3)
372 *d-- = ',';
373 count = 0;
375 *d-- = *p--;
377 d++;
378 if (*d == ',')
379 d++;
380 return d;
383 /* --------------------------------------------------------------------------------------------- */
385 * Print file SIZE to BUFFER, but don't exceed LEN characters,
386 * not including trailing 0. BUFFER should be at least LEN+1 long.
387 * This function is called for every file on panels, so avoid
388 * floating point by any means.
390 * Units: size units (filesystem sizes are 1K blocks)
391 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
394 void
395 size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gboolean use_si)
397 /* Avoid taking power for every file. */
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 static const char *const suffix[] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL };
431 static const char *const suffix_lc[] = { "", "k", "m", "g", "t", "p", "e", "z", "y", NULL };
432 int j = 0;
433 int size_remain;
435 if (len == 0)
436 len = 9;
437 #if SIZEOF_UINTMAX_T == 8
438 /* 20 decimal digits are required to represent 8 bytes */
439 else if (len > 19)
440 len = 19;
441 #else
442 /* 10 decimal digits are required to represent 4 bytes */
443 else if (len > 9)
444 len = 9;
445 #endif
448 * recalculate from 1024 base to 1000 base if units>0
449 * We can't just multiply by 1024 - that might cause overflow
450 * if off_t type is too small
452 if (use_si)
453 for (j = 0; j < units; j++)
455 size_remain = ((size % 125) * 1024) / 1000; /* size mod 125, recalculated */
456 size = size / 125; /* 128/125 = 1024/1000 */
457 size = size * 128; /* This will convert size from multiple of 1024 to multiple of 1000 */
458 size += size_remain; /* Re-add remainder lost by division/multiplication */
461 for (j = units; suffix[j] != NULL; j++)
463 if (size == 0)
465 if (j == units)
467 /* Empty files will print "0" even with minimal width. */
468 g_snprintf (buffer, len + 1, "0");
469 break;
472 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
473 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s",
474 (j > 1) ? (use_si ? suffix_lc[j - 1] : suffix[j - 1]) : "B");
475 break;
478 if (size < power10[len - (j > 0 ? 1 : 0)])
480 g_snprintf (buffer, len + 1, "%" PRIuMAX "%s", size, use_si ? suffix_lc[j] : suffix[j]);
481 break;
484 /* Powers of 1000 or 1024, with rounding. */
485 if (use_si)
486 size = (size + 500) / 1000;
487 else
488 size = (size + 512) >> 10;
492 /* --------------------------------------------------------------------------------------------- */
494 const char *
495 string_perm (mode_t mode_bits)
497 static char mode[11];
499 strcpy (mode, "----------");
500 if (S_ISDIR (mode_bits))
501 mode[0] = 'd';
502 if (S_ISCHR (mode_bits))
503 mode[0] = 'c';
504 if (S_ISBLK (mode_bits))
505 mode[0] = 'b';
506 if (S_ISLNK (mode_bits))
507 mode[0] = 'l';
508 if (S_ISFIFO (mode_bits))
509 mode[0] = 'p';
510 if (S_ISNAM (mode_bits))
511 mode[0] = 'n';
512 if (S_ISSOCK (mode_bits))
513 mode[0] = 's';
514 if (S_ISDOOR (mode_bits))
515 mode[0] = 'D';
516 if (ismode (mode_bits, S_IXOTH))
517 mode[9] = 'x';
518 if (ismode (mode_bits, S_IWOTH))
519 mode[8] = 'w';
520 if (ismode (mode_bits, S_IROTH))
521 mode[7] = 'r';
522 if (ismode (mode_bits, S_IXGRP))
523 mode[6] = 'x';
524 if (ismode (mode_bits, S_IWGRP))
525 mode[5] = 'w';
526 if (ismode (mode_bits, S_IRGRP))
527 mode[4] = 'r';
528 if (ismode (mode_bits, S_IXUSR))
529 mode[3] = 'x';
530 if (ismode (mode_bits, S_IWUSR))
531 mode[2] = 'w';
532 if (ismode (mode_bits, S_IRUSR))
533 mode[1] = 'r';
534 #ifdef S_ISUID
535 if (ismode (mode_bits, S_ISUID))
536 mode[3] = (mode[3] == 'x') ? 's' : 'S';
537 #endif /* S_ISUID */
538 #ifdef S_ISGID
539 if (ismode (mode_bits, S_ISGID))
540 mode[6] = (mode[6] == 'x') ? 's' : 'S';
541 #endif /* S_ISGID */
542 #ifdef S_ISVTX
543 if (ismode (mode_bits, S_ISVTX))
544 mode[9] = (mode[9] == 'x') ? 't' : 'T';
545 #endif /* S_ISVTX */
546 return mode;
549 /* --------------------------------------------------------------------------------------------- */
551 * p: string which might contain an url with a password (this parameter is
552 * modified in place).
553 * has_prefix = 0: The first parameter is an url without a prefix
554 * (user[:pass]@]machine[:port][remote-dir). Delete
555 * the password.
556 * has_prefix = 1: Search p for known url prefixes. If found delete
557 * the password from the url.
558 * Caveat: only the first url is found
561 char *
562 strip_password (char *p, int has_prefix)
564 static const struct
566 const char *name;
567 size_t len;
568 } prefixes[] =
570 /* *INDENT-OFF* */
571 { "/#ftp:", 6 },
572 { "ftp://", 6 },
573 { "/#smb:", 6 },
574 { "smb://", 6 },
575 { "/#sh:", 5 },
576 { "sh://", 5 },
577 { "ssh://", 6 }
578 /* *INDENT-ON* */
581 char *at, *inner_colon, *dir;
582 size_t i;
583 char *result = p;
585 for (i = 0; i < sizeof (prefixes) / sizeof (prefixes[0]); i++)
587 char *q;
589 if (has_prefix)
591 q = strstr (p, prefixes[i].name);
592 if (q == NULL)
593 continue;
594 else
595 p = q + prefixes[i].len;
598 dir = strchr (p, PATH_SEP);
599 if (dir != NULL)
600 *dir = '\0';
602 /* search for any possible user */
603 at = strrchr (p, '@');
605 if (dir)
606 *dir = PATH_SEP;
608 /* We have a username */
609 if (at)
611 inner_colon = memchr (p, ':', at - p);
612 if (inner_colon)
613 memmove (inner_colon, at, strlen (at) + 1);
615 break;
617 return (result);
620 /* --------------------------------------------------------------------------------------------- */
622 const char *
623 strip_home_and_password (const char *dir)
625 size_t len;
626 static char newdir[MC_MAXPATHLEN];
628 len = strlen (mc_config_get_home_dir ());
629 if (mc_config_get_home_dir () != NULL && strncmp (dir, mc_config_get_home_dir (), len) == 0 &&
630 (dir[len] == PATH_SEP || dir[len] == '\0'))
632 newdir[0] = '~';
633 g_strlcpy (&newdir[1], &dir[len], sizeof (newdir) - 1);
634 return newdir;
637 /* We do not strip homes in /#ftp tree, I do not like ~'s there
638 (see ftpfs.c why) */
639 g_strlcpy (newdir, dir, sizeof (newdir));
640 strip_password (newdir, 1);
641 return newdir;
644 /* --------------------------------------------------------------------------------------------- */
646 const char *
647 extension (const char *filename)
649 const char *d = strrchr (filename, '.');
650 return (d != NULL) ? d + 1 : "";
653 /* --------------------------------------------------------------------------------------------- */
655 char *
656 load_mc_home_file (const char *from, const char *filename, char **allocated_filename)
658 char *hintfile_base, *hintfile;
659 char *lang;
660 char *data;
662 hintfile_base = g_build_filename (from, filename, (char *) NULL);
663 lang = guess_message_value ();
665 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
666 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
668 /* Fall back to the two-letter language code */
669 if (lang[0] != '\0' && lang[1] != '\0')
670 lang[2] = '\0';
671 g_free (hintfile);
672 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
673 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
675 g_free (hintfile);
676 hintfile = hintfile_base;
677 g_file_get_contents (hintfile_base, &data, NULL, NULL);
681 g_free (lang);
683 if (hintfile != hintfile_base)
684 g_free (hintfile_base);
686 if (allocated_filename != NULL)
687 *allocated_filename = hintfile;
688 else
689 g_free (hintfile);
691 return data;
694 /* --------------------------------------------------------------------------------------------- */
696 const char *
697 extract_line (const char *s, const char *top)
699 static char tmp_line[BUF_MEDIUM];
700 char *t = tmp_line;
702 while (*s && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line) - 1 && s < top)
703 *t++ = *s++;
704 *t = 0;
705 return tmp_line;
708 /* --------------------------------------------------------------------------------------------- */
710 * The basename routine
713 const char *
714 x_basename (const char *s)
716 const char *url_delim, *path_sep;
718 url_delim = g_strrstr (s, VFS_PATH_URL_DELIMITER);
719 path_sep = strrchr (s, PATH_SEP);
721 if (url_delim == NULL
722 || url_delim < path_sep - strlen (VFS_PATH_URL_DELIMITER)
723 || url_delim - s + strlen (VFS_PATH_URL_DELIMITER) < strlen (s))
725 /* avoid trailing PATH_SEP, if present */
726 if (s[strlen (s) - 1] == PATH_SEP)
728 while (--path_sep > s && *path_sep != PATH_SEP);
729 return (path_sep != s) ? path_sep + 1 : s;
731 else
732 return (path_sep != NULL) ? path_sep + 1 : s;
735 while (--url_delim > s && *url_delim != PATH_SEP);
736 while (--url_delim > s && *url_delim != PATH_SEP);
738 return (url_delim == s) ? s : url_delim + 1;
741 /* --------------------------------------------------------------------------------------------- */
743 const char *
744 unix_error_string (int error_num)
746 static char buffer[BUF_LARGE];
747 gchar *strerror_currentlocale;
749 strerror_currentlocale = g_locale_from_utf8 (g_strerror (error_num), -1, NULL, NULL, NULL);
750 g_snprintf (buffer, sizeof (buffer), "%s (%d)", strerror_currentlocale, error_num);
751 g_free (strerror_currentlocale);
753 return buffer;
756 /* --------------------------------------------------------------------------------------------- */
758 const char *
759 skip_separators (const char *s)
761 const char *su = s;
763 for (; *su; str_cnext_char (&su))
764 if (*su != ' ' && *su != '\t' && *su != ',')
765 break;
767 return su;
770 /* --------------------------------------------------------------------------------------------- */
772 const char *
773 skip_numbers (const char *s)
775 const char *su = s;
777 for (; *su; str_cnext_char (&su))
778 if (!str_isdigit (su))
779 break;
781 return su;
784 /* --------------------------------------------------------------------------------------------- */
786 * Remove all control sequences from the argument string. We define
787 * "control sequence", in a sort of pidgin BNF, as follows:
789 * control-seq = Esc non-'['
790 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
792 * This scheme works for all the terminals described in my termcap /
793 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
794 * terminals. If I hear from a single person who uses such a terminal
795 * with MC, I'll be glad to add support for it. (Dugan)
796 * Non-printable characters are also removed.
799 char *
800 strip_ctrl_codes (char *s)
802 char *w; /* Current position where the stripped data is written */
803 char *r; /* Current position where the original data is read */
804 char *n;
806 if (!s)
807 return 0;
809 for (w = s, r = s; *r;)
811 if (*r == ESC_CHAR)
813 /* Skip the control sequence's arguments */ ;
814 /* '(' need to avoid strange 'B' letter in *Suse (if mc runs under root user) */
815 if (*(++r) == '[' || *r == '(')
817 /* strchr() matches trailing binary 0 */
818 while (*(++r) && strchr ("0123456789;?", *r));
820 else if (*r == ']')
823 * Skip xterm's OSC (Operating System Command)
824 * http://www.xfree86.org/current/ctlseqs.html
825 * OSC P s ; P t ST
826 * OSC P s ; P t BEL
828 char *new_r = r;
830 for (; *new_r; ++new_r)
832 switch (*new_r)
834 /* BEL */
835 case '\a':
836 r = new_r;
837 goto osc_out;
838 case ESC_CHAR:
839 /* ST */
840 if (*(new_r + 1) == '\\')
842 r = new_r + 1;
843 goto osc_out;
847 osc_out:;
851 * Now we are at the last character of the sequence.
852 * Skip it unless it's binary 0.
854 if (*r)
855 r++;
856 continue;
859 n = str_get_next_char (r);
860 if (str_isprint (r))
862 memmove (w, r, n - r);
863 w += n - r;
865 r = n;
867 *w = 0;
868 return s;
871 /* --------------------------------------------------------------------------------------------- */
873 enum compression_type
874 get_compression_type (int fd, const char *name)
876 unsigned char magic[16];
877 size_t str_len;
879 /* Read the magic signature */
880 if (mc_read (fd, (char *) magic, 4) != 4)
881 return COMPRESSION_NONE;
883 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
884 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236))
886 return COMPRESSION_GZIP;
889 /* PKZIP_MAGIC */
890 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003 && magic[3] == 004)
892 /* Read compression type */
893 mc_lseek (fd, 8, SEEK_SET);
894 if (mc_read (fd, (char *) magic, 2) != 2)
895 return COMPRESSION_NONE;
897 /* Gzip can handle only deflated (8) or stored (0) files */
898 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
899 return COMPRESSION_NONE;
901 /* Compatible with gzip */
902 return COMPRESSION_GZIP;
905 /* PACK_MAGIC and LZH_MAGIC and compress magic */
906 if (magic[0] == 037 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235))
908 /* Compatible with gzip */
909 return COMPRESSION_GZIP;
912 /* BZIP and BZIP2 files */
913 if ((magic[0] == 'B') && (magic[1] == 'Z') && (magic[3] >= '1') && (magic[3] <= '9'))
915 switch (magic[2])
917 case '0':
918 return COMPRESSION_BZIP;
919 case 'h':
920 return COMPRESSION_BZIP2;
924 /* Support for LZMA (only utils format with magic in header).
925 * This is the default format of LZMA utils 4.32.1 and later. */
927 if (mc_read (fd, (char *) magic + 4, 2) != 2)
928 return COMPRESSION_NONE;
930 /* LZMA utils format */
931 if (magic[0] == 0xFF
932 && magic[1] == 'L'
933 && magic[2] == 'Z' && magic[3] == 'M' && magic[4] == 'A' && magic[5] == 0x00)
934 return COMPRESSION_LZMA;
936 /* XZ compression magic */
937 if (magic[0] == 0xFD
938 && magic[1] == 0x37
939 && magic[2] == 0x7A && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00)
940 return COMPRESSION_XZ;
942 str_len = strlen (name);
943 /* HACK: we must belive to extention of LZMA file :) ... */
944 if ((str_len > 5 && strcmp (&name[str_len - 5], ".lzma") == 0) ||
945 (str_len > 4 && strcmp (&name[str_len - 4], ".tlz") == 0))
946 return COMPRESSION_LZMA;
948 return COMPRESSION_NONE;
951 /* --------------------------------------------------------------------------------------------- */
953 const char *
954 decompress_extension (int type)
956 switch (type)
958 case COMPRESSION_GZIP:
959 return "/ugz" VFS_PATH_URL_DELIMITER;
960 case COMPRESSION_BZIP:
961 return "/ubz" VFS_PATH_URL_DELIMITER;
962 case COMPRESSION_BZIP2:
963 return "/ubz2" VFS_PATH_URL_DELIMITER;
964 case COMPRESSION_LZMA:
965 return "/ulzma" VFS_PATH_URL_DELIMITER;
966 case COMPRESSION_XZ:
967 return "/uxz" VFS_PATH_URL_DELIMITER;
969 /* Should never reach this place */
970 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
971 return 0;
974 /* --------------------------------------------------------------------------------------------- */
976 void
977 wipe_password (char *passwd)
979 char *p = passwd;
981 if (!p)
982 return;
983 for (; *p; p++)
984 *p = 0;
985 g_free (passwd);
988 /* --------------------------------------------------------------------------------------------- */
990 * Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key
991 * @returns a newly allocated string
994 char *
995 convert_controls (const char *p)
997 char *valcopy = g_strdup (p);
998 char *q;
1000 /* Parse the escape special character */
1001 for (q = valcopy; *p;)
1003 if (*p == '\\')
1005 p++;
1006 if ((*p == 'e') || (*p == 'E'))
1008 p++;
1009 *q++ = ESC_CHAR;
1012 else
1014 if (*p == '^')
1016 p++;
1017 if (*p == '^')
1018 *q++ = *p++;
1019 else
1021 char c = (*p | 0x20);
1022 if (c >= 'a' && c <= 'z')
1024 *q++ = c - 'a' + 1;
1025 p++;
1027 else if (*p)
1028 p++;
1031 else
1032 *q++ = *p++;
1035 *q = 0;
1036 return valcopy;
1039 /* --------------------------------------------------------------------------------------------- */
1041 * Finds out a relative path from first to second, i.e. goes as many ..
1042 * as needed up in first and then goes down using second
1045 char *
1046 diff_two_paths (const char *first, const char *second)
1048 char *p, *q, *r, *s, *buf = NULL;
1049 int i, j, prevlen = -1, currlen;
1050 char *my_first = NULL, *my_second = NULL;
1052 my_first = resolve_symlinks (first);
1053 if (my_first == NULL)
1054 return NULL;
1055 my_second = resolve_symlinks (second);
1056 if (my_second == NULL)
1058 g_free (my_first);
1059 return NULL;
1061 for (j = 0; j < 2; j++)
1063 p = my_first;
1064 q = my_second;
1065 for (;;)
1067 r = strchr (p, PATH_SEP);
1068 s = strchr (q, PATH_SEP);
1069 if (!r || !s)
1070 break;
1071 *r = 0;
1072 *s = 0;
1073 if (strcmp (p, q))
1075 *r = PATH_SEP;
1076 *s = PATH_SEP;
1077 break;
1079 else
1081 *r = PATH_SEP;
1082 *s = PATH_SEP;
1084 p = r + 1;
1085 q = s + 1;
1087 p--;
1088 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++);
1089 currlen = (i + 1) * 3 + strlen (q) + 1;
1090 if (j)
1092 if (currlen < prevlen)
1093 g_free (buf);
1094 else
1096 g_free (my_first);
1097 g_free (my_second);
1098 return buf;
1101 p = buf = g_malloc (currlen);
1102 prevlen = currlen;
1103 for (; i >= 0; i--, p += 3)
1104 strcpy (p, "../");
1105 strcpy (p, q);
1107 g_free (my_first);
1108 g_free (my_second);
1109 return buf;
1112 /* --------------------------------------------------------------------------------------------- */
1114 * If filename is NULL, then we just append PATH_SEP to the dir
1117 char *
1118 concat_dir_and_file (const char *dir, const char *file)
1120 int i = strlen (dir);
1122 if (dir[i - 1] == PATH_SEP)
1123 return g_strconcat (dir, file, (char *) NULL);
1124 else
1125 return g_strconcat (dir, PATH_SEP_STR, file, (char *) NULL);
1128 /* --------------------------------------------------------------------------------------------- */
1130 * Append text to GList, remove all entries with the same text
1133 GList *
1134 list_append_unique (GList * list, char *text)
1136 GList *lc_link;
1139 * Go to the last position and traverse the list backwards
1140 * starting from the second last entry to make sure that we
1141 * are not removing the current link.
1143 list = g_list_append (list, text);
1144 list = g_list_last (list);
1145 lc_link = g_list_previous (list);
1147 while (lc_link != NULL)
1149 GList *newlink;
1151 newlink = g_list_previous (lc_link);
1152 if (strcmp ((char *) lc_link->data, text) == 0)
1154 GList *tmp;
1156 g_free (lc_link->data);
1157 tmp = g_list_remove_link (list, lc_link);
1158 g_list_free_1 (lc_link);
1160 lc_link = newlink;
1163 return list;
1166 /* --------------------------------------------------------------------------------------------- */
1167 /* Following code heavily borrows from libiberty, mkstemps.c */
1169 * Arguments:
1170 * pname (output) - pointer to the name of the temp file (needs g_free).
1171 * NULL if the function fails.
1172 * prefix - part of the filename before the random part.
1173 * Prepend $TMPDIR or /tmp if there are no path separators.
1174 * suffix - if not NULL, part of the filename after the random part.
1176 * Result:
1177 * handle of the open file or -1 if couldn't open any.
1181 mc_mkstemps (char **pname, const char *prefix, const char *suffix)
1183 static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1184 static unsigned long value;
1185 struct timeval tv;
1186 char *tmpbase;
1187 char *tmpname;
1188 char *XXXXXX;
1189 int count;
1191 if (strchr (prefix, PATH_SEP) == NULL)
1193 /* Add prefix first to find the position of XXXXXX */
1194 tmpbase = concat_dir_and_file (mc_tmpdir (), prefix);
1196 else
1198 tmpbase = g_strdup (prefix);
1201 tmpname = g_strconcat (tmpbase, "XXXXXX", suffix, (char *) NULL);
1202 *pname = tmpname;
1203 XXXXXX = &tmpname[strlen (tmpbase)];
1204 g_free (tmpbase);
1206 /* Get some more or less random data. */
1207 gettimeofday (&tv, NULL);
1208 value += (tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
1210 for (count = 0; count < TMP_MAX; ++count)
1212 unsigned long v = value;
1213 int fd;
1215 /* Fill in the random bits. */
1216 XXXXXX[0] = letters[v % 62];
1217 v /= 62;
1218 XXXXXX[1] = letters[v % 62];
1219 v /= 62;
1220 XXXXXX[2] = letters[v % 62];
1221 v /= 62;
1222 XXXXXX[3] = letters[v % 62];
1223 v /= 62;
1224 XXXXXX[4] = letters[v % 62];
1225 v /= 62;
1226 XXXXXX[5] = letters[v % 62];
1228 fd = open (tmpname, O_RDWR | O_CREAT | O_TRUNC | O_EXCL, S_IRUSR | S_IWUSR);
1229 if (fd >= 0)
1231 /* Successfully created. */
1232 return fd;
1235 /* This is a random value. It is only necessary that the next
1236 TMP_MAX values generated by adding 7777 to VALUE are different
1237 with (module 2^32). */
1238 value += 7777;
1241 /* Unsuccessful. Free the filename. */
1242 g_free (tmpname);
1243 *pname = NULL;
1245 return -1;
1248 /* --------------------------------------------------------------------------------------------- */
1250 * Read and restore position for the given filename.
1251 * If there is no stored data, return line 1 and col 0.
1254 void
1255 load_file_position (const char *filename, long *line, long *column, off_t * offset,
1256 GArray ** bookmarks)
1258 char *fn;
1259 FILE *f;
1260 char buf[MC_MAXPATHLEN + 100];
1261 const size_t len = strlen (filename);
1263 /* defaults */
1264 *line = 1;
1265 *column = 0;
1266 *offset = 0;
1268 /* open file with positions */
1269 fn = g_build_filename (mc_config_get_cache_path (), MC_FILEPOS_FILE, NULL);
1270 f = fopen (fn, "r");
1271 g_free (fn);
1272 if (f == NULL)
1273 return;
1275 /* prepare array for serialized bookmarks */
1276 *bookmarks = g_array_sized_new (FALSE, FALSE, sizeof (size_t), MAX_SAVED_BOOKMARKS);
1278 while (fgets (buf, sizeof (buf), f) != NULL)
1280 const char *p;
1281 gchar **pos_tokens;
1283 /* check if the filename matches the beginning of string */
1284 if (strncmp (buf, filename, len) != 0)
1285 continue;
1287 /* followed by single space */
1288 if (buf[len] != ' ')
1289 continue;
1291 /* and string without spaces */
1292 p = &buf[len + 1];
1293 if (strchr (p, ' ') != NULL)
1294 continue;
1296 pos_tokens = g_strsplit (p, ";", 3 + MAX_SAVED_BOOKMARKS);
1297 if (pos_tokens[0] == NULL)
1299 *line = 1;
1300 *column = 0;
1301 *offset = 0;
1303 else
1305 *line = strtol (pos_tokens[0], NULL, 10);
1306 if (pos_tokens[1] == NULL)
1308 *column = 0;
1309 *offset = 0;
1311 else
1313 *column = strtol (pos_tokens[1], NULL, 10);
1314 if (pos_tokens[2] == NULL)
1315 *offset = 0;
1316 else
1318 size_t i;
1320 *offset = strtoll (pos_tokens[2], NULL, 10);
1322 for (i = 0; i < MAX_SAVED_BOOKMARKS && pos_tokens[3 + i] != NULL; i++)
1324 size_t val;
1326 val = strtoul (pos_tokens[3 + i], NULL, 10);
1327 g_array_append_val (*bookmarks, val);
1333 g_strfreev (pos_tokens);
1336 fclose (f);
1339 /* --------------------------------------------------------------------------------------------- */
1341 * Save position for the given file
1344 void
1345 save_file_position (const char *filename, long line, long column, off_t offset, GArray * bookmarks)
1347 static size_t filepos_max_saved_entries = 0;
1348 char *fn, *tmp_fn;
1349 FILE *f, *tmp_f;
1350 char buf[MC_MAXPATHLEN + 100];
1351 size_t i;
1352 const size_t len = strlen (filename);
1353 gboolean src_error = FALSE;
1355 if (filepos_max_saved_entries == 0)
1356 filepos_max_saved_entries = mc_config_get_int (mc_main_config, CONFIG_APP_SECTION,
1357 "filepos_max_saved_entries", 1024);
1359 fn = g_build_filename (mc_config_get_cache_path (), MC_FILEPOS_FILE, NULL);
1360 if (fn == NULL)
1361 goto early_error;
1363 mc_util_make_backup_if_possible (fn, TMP_SUFFIX);
1365 /* open file */
1366 f = fopen (fn, "w");
1367 if (f == NULL)
1368 goto open_target_error;
1370 tmp_fn = g_strdup_printf ("%s" TMP_SUFFIX, fn);
1371 tmp_f = fopen (tmp_fn, "r");
1372 if (tmp_f == NULL)
1374 src_error = TRUE;
1375 goto open_source_error;
1378 /* put the new record */
1379 if (line != 1 || column != 0 || bookmarks != NULL)
1381 if (fprintf (f, "%s %ld;%ld;%" PRIuMAX, filename, line, column, (uintmax_t) offset) < 0)
1382 goto write_position_error;
1383 if (bookmarks != NULL)
1384 for (i = 0; i < bookmarks->len && i < MAX_SAVED_BOOKMARKS; i++)
1385 if (fprintf (f, ";%zu", g_array_index (bookmarks, size_t, i)) < 0)
1386 goto write_position_error;
1388 if (fprintf (f, "\n") < 0)
1389 goto write_position_error;
1392 i = 1;
1393 while (fgets (buf, sizeof (buf), tmp_f) != NULL)
1395 if (buf[len] == ' ' && strncmp (buf, filename, len) == 0
1396 && strchr (&buf[len + 1], ' ') == NULL)
1397 continue;
1399 fprintf (f, "%s", buf);
1400 if (++i > filepos_max_saved_entries)
1401 break;
1404 write_position_error:
1405 fclose (tmp_f);
1406 open_source_error:
1407 g_free (tmp_fn);
1408 fclose (f);
1409 if (src_error)
1410 mc_util_restore_from_backup_if_possible (fn, TMP_SUFFIX);
1411 else
1412 mc_util_unlink_backup_if_possible (fn, TMP_SUFFIX);
1413 open_target_error:
1414 g_free (fn);
1415 early_error:
1416 if (bookmarks != NULL)
1417 g_array_free (bookmarks, TRUE);
1420 /* --------------------------------------------------------------------------------------------- */
1422 extern int
1423 ascii_alpha_to_cntrl (int ch)
1425 if ((ch >= ASCII_A && ch <= ASCII_Z) || (ch >= ASCII_a && ch <= ASCII_z))
1427 ch &= 0x1f;
1429 return ch;
1432 /* --------------------------------------------------------------------------------------------- */
1434 const char *
1435 Q_ (const char *s)
1437 const char *result, *sep;
1439 result = _(s);
1440 sep = strchr (result, '|');
1441 return (sep != NULL) ? sep + 1 : result;
1444 /* --------------------------------------------------------------------------------------------- */
1446 gboolean
1447 mc_util_make_backup_if_possible (const char *file_name, const char *backup_suffix)
1449 struct stat stat_buf;
1450 char *backup_path;
1451 gboolean ret;
1452 if (!exist_file (file_name))
1453 return FALSE;
1455 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1457 if (backup_path == NULL)
1458 return FALSE;
1460 ret = mc_util_write_backup_content (file_name, backup_path);
1462 if (ret)
1464 /* Backup file will have same ownership with main file. */
1465 if (stat (file_name, &stat_buf) == 0)
1466 chmod (backup_path, stat_buf.st_mode);
1467 else
1468 chmod (backup_path, S_IRUSR | S_IWUSR);
1471 g_free (backup_path);
1473 return ret;
1476 /* --------------------------------------------------------------------------------------------- */
1478 gboolean
1479 mc_util_restore_from_backup_if_possible (const char *file_name, const char *backup_suffix)
1481 gboolean ret;
1482 char *backup_path;
1484 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1485 if (backup_path == NULL)
1486 return FALSE;
1488 ret = mc_util_write_backup_content (backup_path, file_name);
1489 g_free (backup_path);
1491 return ret;
1494 /* --------------------------------------------------------------------------------------------- */
1496 gboolean
1497 mc_util_unlink_backup_if_possible (const char *file_name, const char *backup_suffix)
1499 char *backup_path;
1501 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1502 if (backup_path == NULL)
1503 return FALSE;
1505 if (exist_file (backup_path))
1506 mc_unlink (backup_path);
1508 g_free (backup_path);
1509 return TRUE;
1512 /* --------------------------------------------------------------------------------------------- */
1514 * partly taken from dcigettext.c, returns "" for default locale
1515 * value should be freed by calling function g_free()
1518 char *
1519 guess_message_value (void)
1521 static const char *const var[] = {
1522 /* Setting of LC_ALL overwrites all other. */
1523 /* Do not use LANGUAGE for check user locale and drowing hints */
1524 "LC_ALL",
1525 /* Next comes the name of the desired category. */
1526 "LC_MESSAGES",
1527 /* Last possibility is the LANG environment variable. */
1528 "LANG",
1529 /* NULL exit loops */
1530 NULL
1533 unsigned i = 0;
1534 const char *locale = NULL;
1536 while (var[i] != NULL)
1538 locale = getenv (var[i]);
1539 if (locale != NULL && locale[0] != '\0')
1540 break;
1541 i++;
1544 if (locale == NULL)
1545 locale = "";
1547 return g_strdup (locale);
1550 /* --------------------------------------------------------------------------------------------- */