Updated doc/NEWS file
[midnight-commander.git] / lib / util.c
blob3a77de2e3b77d9065dd6528bd3303bc6a617d553
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 lib/util.c
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 vfs_path_t * vpath)
115 char *p, *p2;
116 char *buf, *buf2, *q, *r, c;
117 struct stat mybuf;
119 if (vpath->relative)
120 return NULL;
122 p = p2 = vfs_path_to_str (vpath);
123 r = buf = g_malloc (MC_MAXPATHLEN);
124 buf2 = g_malloc (MC_MAXPATHLEN);
125 *r++ = PATH_SEP;
126 *r = 0;
130 q = strchr (p + 1, PATH_SEP);
131 if (!q)
133 q = strchr (p + 1, 0);
134 if (q == p + 1)
135 break;
137 c = *q;
138 *q = 0;
139 if (mc_lstat (vpath, &mybuf) < 0)
141 g_free (buf);
142 buf = NULL;
143 goto ret;
145 if (!S_ISLNK (mybuf.st_mode))
146 strcpy (r, p + 1);
147 else
149 int len;
151 len = mc_readlink (vpath, buf2, MC_MAXPATHLEN - 1);
152 if (len < 0)
154 g_free (buf);
155 buf = NULL;
156 goto ret;
158 buf2[len] = 0;
159 if (*buf2 == PATH_SEP)
160 strcpy (buf, buf2);
161 else
162 strcpy (r, buf2);
164 canonicalize_pathname (buf);
165 r = strchr (buf, 0);
166 if (!*r || *(r - 1) != PATH_SEP)
167 /* FIXME: this condition is always true because r points to the EOL */
169 *r++ = PATH_SEP;
170 *r = 0;
172 *q = c;
173 p = q;
175 while (c != '\0');
177 if (!*buf)
178 strcpy (buf, PATH_SEP_STR);
179 else if (*(r - 1) == PATH_SEP && r != buf + 1)
180 *(r - 1) = 0;
182 ret:
183 g_free (buf2);
184 g_free (p2);
185 return buf;
188 /* --------------------------------------------------------------------------------------------- */
190 static gboolean
191 mc_util_write_backup_content (const char *from_file_name, const char *to_file_name)
193 FILE *backup_fd;
194 char *contents;
195 gsize length;
196 gboolean ret1 = TRUE;
198 if (!g_file_get_contents (from_file_name, &contents, &length, NULL))
199 return FALSE;
201 backup_fd = fopen (to_file_name, "w");
202 if (backup_fd == NULL)
204 g_free (contents);
205 return FALSE;
208 if (fwrite ((const void *) contents, 1, length, backup_fd) != length)
209 ret1 = FALSE;
211 int ret2;
212 ret2 = fflush (backup_fd);
213 ret2 = fclose (backup_fd);
215 g_free (contents);
216 return ret1;
219 /* --------------------------------------------------------------------------------------------- */
220 /*** public functions ****************************************************************************/
221 /* --------------------------------------------------------------------------------------------- */
224 is_printable (int c)
226 c &= 0xff;
228 #ifdef HAVE_CHARSET
229 /* "Display bits" is ignored, since the user controls the output
230 by setting the output codepage */
231 return is_8bit_printable (c);
232 #else
233 if (!mc_global.eight_bit_clean)
234 return is_7bit_printable (c);
236 if (mc_global.full_eight_bits)
238 return is_8bit_printable (c);
240 else
241 return is_iso_printable (c);
242 #endif /* !HAVE_CHARSET */
245 /* --------------------------------------------------------------------------------------------- */
247 * Quote the filename for the purpose of inserting it into the command
248 * line. If quote_percent is 1, replace "%" with "%%" - the percent is
249 * processed by the mc command line.
251 char *
252 name_quote (const char *s, int quote_percent)
254 char *ret, *d;
256 d = ret = g_malloc (strlen (s) * 2 + 2 + 1);
257 if (*s == '-')
259 *d++ = '.';
260 *d++ = '/';
263 for (; *s; s++, d++)
265 switch (*s)
267 case '%':
268 if (quote_percent)
269 *d++ = '%';
270 break;
271 case '\'':
272 case '\\':
273 case '\r':
274 case '\n':
275 case '\t':
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 case '$':
290 case '&':
291 case '*':
292 case '(':
293 case ')':
294 *d++ = '\\';
295 break;
296 case '~':
297 case '#':
298 if (d == ret)
299 *d++ = '\\';
300 break;
302 *d = *s;
304 *d = '\0';
305 return ret;
308 /* --------------------------------------------------------------------------------------------- */
310 char *
311 fake_name_quote (const char *s, int quote_percent)
313 (void) quote_percent;
314 return g_strdup (s);
317 /* --------------------------------------------------------------------------------------------- */
319 * path_trunc() is the same as str_trunc() but
320 * it deletes possible password from path for security
321 * reasons.
324 const char *
325 path_trunc (const char *path, size_t trunc_len)
327 vfs_path_t *vpath;
328 char *secure_path;
329 const char *ret;
331 vpath = vfs_path_from_str (path);
332 secure_path = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
333 vfs_path_free (vpath);
335 ret = str_trunc (secure_path, trunc_len);
336 g_free (secure_path);
338 return ret;
341 /* --------------------------------------------------------------------------------------------- */
343 const char *
344 size_trunc (uintmax_t size, gboolean use_si)
346 static char x[BUF_TINY];
347 uintmax_t divisor = 1;
348 const char *xtra = "";
350 if (size > 999999999UL)
352 divisor = use_si ? 1000 : 1024;
353 xtra = use_si ? "k" : "K";
355 if (size / divisor > 999999999UL)
357 divisor = use_si ? (1000 * 1000) : (1024 * 1024);
358 xtra = use_si ? "m" : "M";
360 if (size / divisor > 999999999UL)
362 divisor = use_si ? (1000 * 1000 * 1000) : (1024 * 1024 * 1024);
363 xtra = use_si ? "g" : "G";
367 g_snprintf (x, sizeof (x), "%.0f%s", 1.0 * size / divisor, xtra);
368 return x;
371 /* --------------------------------------------------------------------------------------------- */
373 const char *
374 size_trunc_sep (uintmax_t size, gboolean use_si)
376 static char x[60];
377 int count;
378 const char *p, *y;
379 char *d;
381 p = y = size_trunc (size, use_si);
382 p += strlen (p) - 1;
383 d = x + sizeof (x) - 1;
384 *d-- = '\0';
385 while (p >= y && isalpha ((unsigned char) *p))
386 *d-- = *p--;
387 for (count = 0; p >= y; count++)
389 if (count == 3)
391 *d-- = ',';
392 count = 0;
394 *d-- = *p--;
396 d++;
397 if (*d == ',')
398 d++;
399 return d;
402 /* --------------------------------------------------------------------------------------------- */
404 * Print file SIZE to BUFFER, but don't exceed LEN characters,
405 * not including trailing 0. BUFFER should be at least LEN+1 long.
406 * This function is called for every file on panels, so avoid
407 * floating point by any means.
409 * Units: size units (filesystem sizes are 1K blocks)
410 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
413 void
414 size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gboolean use_si)
416 /* Avoid taking power for every file. */
417 /* *INDENT-OFF* */
418 static const uintmax_t power10[] = {
419 /* we hope that size of uintmax_t is 4 bytes at least */
420 1ULL,
421 10ULL,
422 100ULL,
423 1000ULL,
424 10000ULL,
425 100000ULL,
426 1000000ULL,
427 10000000ULL,
428 100000000ULL,
429 1000000000ULL
430 /* maximmum value of uintmax_t (in case of 4 bytes) is
431 4294967295
433 #if SIZEOF_UINTMAX_T == 8
435 10000000000ULL,
436 100000000000ULL,
437 1000000000000ULL,
438 10000000000000ULL,
439 100000000000000ULL,
440 1000000000000000ULL,
441 10000000000000000ULL,
442 100000000000000000ULL,
443 1000000000000000000ULL,
444 10000000000000000000ULL
445 /* maximmum value of uintmax_t (in case of 8 bytes) is
446 18447644073710439615
448 #endif
450 /* *INDENT-ON* */
451 static const char *const suffix[] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL };
452 static const char *const suffix_lc[] = { "", "k", "m", "g", "t", "p", "e", "z", "y", NULL };
453 int j = 0;
455 if (len == 0)
456 len = 9;
457 #if SIZEOF_UINTMAX_T == 8
458 /* 20 decimal digits are required to represent 8 bytes */
459 else if (len > 19)
460 len = 19;
461 #else
462 /* 10 decimal digits are required to represent 4 bytes */
463 else if (len > 9)
464 len = 9;
465 #endif
468 * recalculate from 1024 base to 1000 base if units>0
469 * We can't just multiply by 1024 - that might cause overflow
470 * if off_t type is too small
472 if (use_si)
473 for (j = 0; j < units; j++)
475 uintmax_t size_remain;
477 size_remain = ((size % 125) * 1024) / 1000; /* size mod 125, recalculated */
478 size = size / 125; /* 128/125 = 1024/1000 */
479 size = size * 128; /* This will convert size from multiple of 1024 to multiple of 1000 */
480 size += size_remain; /* Re-add remainder lost by division/multiplication */
483 for (j = units; suffix[j] != NULL; j++)
485 if (size == 0)
487 if (j == units)
489 /* Empty files will print "0" even with minimal width. */
490 g_snprintf (buffer, len + 1, "0");
491 break;
494 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
495 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s",
496 (j > 1) ? (use_si ? suffix_lc[j - 1] : suffix[j - 1]) : "B");
497 break;
500 if (size < power10[len - (j > 0 ? 1 : 0)])
502 g_snprintf (buffer, len + 1, "%" PRIuMAX "%s", size, use_si ? suffix_lc[j] : suffix[j]);
503 break;
506 /* Powers of 1000 or 1024, with rounding. */
507 if (use_si)
508 size = (size + 500) / 1000;
509 else
510 size = (size + 512) >> 10;
514 /* --------------------------------------------------------------------------------------------- */
516 const char *
517 string_perm (mode_t mode_bits)
519 static char mode[11];
521 strcpy (mode, "----------");
522 if (S_ISDIR (mode_bits))
523 mode[0] = 'd';
524 if (S_ISCHR (mode_bits))
525 mode[0] = 'c';
526 if (S_ISBLK (mode_bits))
527 mode[0] = 'b';
528 if (S_ISLNK (mode_bits))
529 mode[0] = 'l';
530 if (S_ISFIFO (mode_bits))
531 mode[0] = 'p';
532 if (S_ISNAM (mode_bits))
533 mode[0] = 'n';
534 if (S_ISSOCK (mode_bits))
535 mode[0] = 's';
536 if (S_ISDOOR (mode_bits))
537 mode[0] = 'D';
538 if (ismode (mode_bits, S_IXOTH))
539 mode[9] = 'x';
540 if (ismode (mode_bits, S_IWOTH))
541 mode[8] = 'w';
542 if (ismode (mode_bits, S_IROTH))
543 mode[7] = 'r';
544 if (ismode (mode_bits, S_IXGRP))
545 mode[6] = 'x';
546 if (ismode (mode_bits, S_IWGRP))
547 mode[5] = 'w';
548 if (ismode (mode_bits, S_IRGRP))
549 mode[4] = 'r';
550 if (ismode (mode_bits, S_IXUSR))
551 mode[3] = 'x';
552 if (ismode (mode_bits, S_IWUSR))
553 mode[2] = 'w';
554 if (ismode (mode_bits, S_IRUSR))
555 mode[1] = 'r';
556 #ifdef S_ISUID
557 if (ismode (mode_bits, S_ISUID))
558 mode[3] = (mode[3] == 'x') ? 's' : 'S';
559 #endif /* S_ISUID */
560 #ifdef S_ISGID
561 if (ismode (mode_bits, S_ISGID))
562 mode[6] = (mode[6] == 'x') ? 's' : 'S';
563 #endif /* S_ISGID */
564 #ifdef S_ISVTX
565 if (ismode (mode_bits, S_ISVTX))
566 mode[9] = (mode[9] == 'x') ? 't' : 'T';
567 #endif /* S_ISVTX */
568 return mode;
571 /* --------------------------------------------------------------------------------------------- */
573 const char *
574 extension (const char *filename)
576 const char *d = strrchr (filename, '.');
577 return (d != NULL) ? d + 1 : "";
580 /* --------------------------------------------------------------------------------------------- */
582 char *
583 load_mc_home_file (const char *from, const char *filename, char **allocated_filename)
585 char *hintfile_base, *hintfile;
586 char *lang;
587 char *data;
589 hintfile_base = g_build_filename (from, filename, (char *) NULL);
590 lang = guess_message_value ();
592 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
593 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
595 /* Fall back to the two-letter language code */
596 if (lang[0] != '\0' && lang[1] != '\0')
597 lang[2] = '\0';
598 g_free (hintfile);
599 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
600 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
602 g_free (hintfile);
603 hintfile = hintfile_base;
604 g_file_get_contents (hintfile_base, &data, NULL, NULL);
608 g_free (lang);
610 if (hintfile != hintfile_base)
611 g_free (hintfile_base);
613 if (allocated_filename != NULL)
614 *allocated_filename = hintfile;
615 else
616 g_free (hintfile);
618 return data;
621 /* --------------------------------------------------------------------------------------------- */
623 const char *
624 extract_line (const char *s, const char *top)
626 static char tmp_line[BUF_MEDIUM];
627 char *t = tmp_line;
629 while (*s && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line) - 1 && s < top)
630 *t++ = *s++;
631 *t = 0;
632 return tmp_line;
635 /* --------------------------------------------------------------------------------------------- */
637 * The basename routine
640 const char *
641 x_basename (const char *s)
643 const char *url_delim, *path_sep;
645 url_delim = g_strrstr (s, VFS_PATH_URL_DELIMITER);
646 path_sep = strrchr (s, PATH_SEP);
648 if (url_delim == NULL
649 || url_delim < path_sep - strlen (VFS_PATH_URL_DELIMITER)
650 || url_delim - s + strlen (VFS_PATH_URL_DELIMITER) < strlen (s))
652 /* avoid trailing PATH_SEP, if present */
653 if (s[strlen (s) - 1] == PATH_SEP)
655 while (--path_sep > s && *path_sep != PATH_SEP);
656 return (path_sep != s) ? path_sep + 1 : s;
658 else
659 return (path_sep != NULL) ? path_sep + 1 : s;
662 while (--url_delim > s && *url_delim != PATH_SEP);
663 while (--url_delim > s && *url_delim != PATH_SEP);
665 return (url_delim == s) ? s : url_delim + 1;
668 /* --------------------------------------------------------------------------------------------- */
670 const char *
671 unix_error_string (int error_num)
673 static char buffer[BUF_LARGE];
674 gchar *strerror_currentlocale;
676 strerror_currentlocale = g_locale_from_utf8 (g_strerror (error_num), -1, NULL, NULL, NULL);
677 g_snprintf (buffer, sizeof (buffer), "%s (%d)", strerror_currentlocale, error_num);
678 g_free (strerror_currentlocale);
680 return buffer;
683 /* --------------------------------------------------------------------------------------------- */
685 const char *
686 skip_separators (const char *s)
688 const char *su = s;
690 for (; *su; str_cnext_char (&su))
691 if (*su != ' ' && *su != '\t' && *su != ',')
692 break;
694 return su;
697 /* --------------------------------------------------------------------------------------------- */
699 const char *
700 skip_numbers (const char *s)
702 const char *su = s;
704 for (; *su; str_cnext_char (&su))
705 if (!str_isdigit (su))
706 break;
708 return su;
711 /* --------------------------------------------------------------------------------------------- */
713 * Remove all control sequences from the argument string. We define
714 * "control sequence", in a sort of pidgin BNF, as follows:
716 * control-seq = Esc non-'['
717 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
719 * This scheme works for all the terminals described in my termcap /
720 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
721 * terminals. If I hear from a single person who uses such a terminal
722 * with MC, I'll be glad to add support for it. (Dugan)
723 * Non-printable characters are also removed.
726 char *
727 strip_ctrl_codes (char *s)
729 char *w; /* Current position where the stripped data is written */
730 char *r; /* Current position where the original data is read */
731 char *n;
733 if (!s)
734 return 0;
736 for (w = s, r = s; *r;)
738 if (*r == ESC_CHAR)
740 /* Skip the control sequence's arguments */ ;
741 /* '(' need to avoid strange 'B' letter in *Suse (if mc runs under root user) */
742 if (*(++r) == '[' || *r == '(')
744 /* strchr() matches trailing binary 0 */
745 while (*(++r) && strchr ("0123456789;?", *r));
747 else if (*r == ']')
750 * Skip xterm's OSC (Operating System Command)
751 * http://www.xfree86.org/current/ctlseqs.html
752 * OSC P s ; P t ST
753 * OSC P s ; P t BEL
755 char *new_r = r;
757 for (; *new_r; ++new_r)
759 switch (*new_r)
761 /* BEL */
762 case '\a':
763 r = new_r;
764 goto osc_out;
765 case ESC_CHAR:
766 /* ST */
767 if (*(new_r + 1) == '\\')
769 r = new_r + 1;
770 goto osc_out;
774 osc_out:;
778 * Now we are at the last character of the sequence.
779 * Skip it unless it's binary 0.
781 if (*r)
782 r++;
783 continue;
786 n = str_get_next_char (r);
787 if (str_isprint (r))
789 memmove (w, r, n - r);
790 w += n - r;
792 r = n;
794 *w = 0;
795 return s;
798 /* --------------------------------------------------------------------------------------------- */
800 enum compression_type
801 get_compression_type (int fd, const char *name)
803 unsigned char magic[16];
804 size_t str_len;
806 /* Read the magic signature */
807 if (mc_read (fd, (char *) magic, 4) != 4)
808 return COMPRESSION_NONE;
810 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
811 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236))
813 return COMPRESSION_GZIP;
816 /* PKZIP_MAGIC */
817 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003 && magic[3] == 004)
819 /* Read compression type */
820 mc_lseek (fd, 8, SEEK_SET);
821 if (mc_read (fd, (char *) magic, 2) != 2)
822 return COMPRESSION_NONE;
824 /* Gzip can handle only deflated (8) or stored (0) files */
825 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
826 return COMPRESSION_NONE;
828 /* Compatible with gzip */
829 return COMPRESSION_GZIP;
832 /* PACK_MAGIC and LZH_MAGIC and compress magic */
833 if (magic[0] == 037 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235))
835 /* Compatible with gzip */
836 return COMPRESSION_GZIP;
839 /* BZIP and BZIP2 files */
840 if ((magic[0] == 'B') && (magic[1] == 'Z') && (magic[3] >= '1') && (magic[3] <= '9'))
842 switch (magic[2])
844 case '0':
845 return COMPRESSION_BZIP;
846 case 'h':
847 return COMPRESSION_BZIP2;
851 /* Support for LZMA (only utils format with magic in header).
852 * This is the default format of LZMA utils 4.32.1 and later. */
854 if (mc_read (fd, (char *) magic + 4, 2) != 2)
855 return COMPRESSION_NONE;
857 /* LZMA utils format */
858 if (magic[0] == 0xFF
859 && magic[1] == 'L'
860 && magic[2] == 'Z' && magic[3] == 'M' && magic[4] == 'A' && magic[5] == 0x00)
861 return COMPRESSION_LZMA;
863 /* XZ compression magic */
864 if (magic[0] == 0xFD
865 && magic[1] == 0x37
866 && magic[2] == 0x7A && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00)
867 return COMPRESSION_XZ;
869 str_len = strlen (name);
870 /* HACK: we must belive to extention of LZMA file :) ... */
871 if ((str_len > 5 && strcmp (&name[str_len - 5], ".lzma") == 0) ||
872 (str_len > 4 && strcmp (&name[str_len - 4], ".tlz") == 0))
873 return COMPRESSION_LZMA;
875 return COMPRESSION_NONE;
878 /* --------------------------------------------------------------------------------------------- */
880 const char *
881 decompress_extension (int type)
883 switch (type)
885 case COMPRESSION_GZIP:
886 return "/ugz" VFS_PATH_URL_DELIMITER;
887 case COMPRESSION_BZIP:
888 return "/ubz" VFS_PATH_URL_DELIMITER;
889 case COMPRESSION_BZIP2:
890 return "/ubz2" VFS_PATH_URL_DELIMITER;
891 case COMPRESSION_LZMA:
892 return "/ulzma" VFS_PATH_URL_DELIMITER;
893 case COMPRESSION_XZ:
894 return "/uxz" VFS_PATH_URL_DELIMITER;
896 /* Should never reach this place */
897 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
898 return 0;
901 /* --------------------------------------------------------------------------------------------- */
903 void
904 wipe_password (char *passwd)
906 char *p = passwd;
908 if (!p)
909 return;
910 for (; *p; p++)
911 *p = 0;
912 g_free (passwd);
915 /* --------------------------------------------------------------------------------------------- */
917 * Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key
919 * @param p pointer to string
921 * @return newly allocated string
924 char *
925 convert_controls (const char *p)
927 char *valcopy = g_strdup (p);
928 char *q;
930 /* Parse the escape special character */
931 for (q = valcopy; *p;)
933 if (*p == '\\')
935 p++;
936 if ((*p == 'e') || (*p == 'E'))
938 p++;
939 *q++ = ESC_CHAR;
942 else
944 if (*p == '^')
946 p++;
947 if (*p == '^')
948 *q++ = *p++;
949 else
951 char c = (*p | 0x20);
952 if (c >= 'a' && c <= 'z')
954 *q++ = c - 'a' + 1;
955 p++;
957 else if (*p)
958 p++;
961 else
962 *q++ = *p++;
965 *q = 0;
966 return valcopy;
969 /* --------------------------------------------------------------------------------------------- */
971 * Finds out a relative path from first to second, i.e. goes as many ..
972 * as needed up in first and then goes down using second
975 char *
976 diff_two_paths (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
978 char *p, *q, *r, *s, *buf = NULL;
979 int i, j, prevlen = -1, currlen;
980 char *my_first = NULL, *my_second = NULL;
982 my_first = resolve_symlinks (vpath1);
983 if (my_first == NULL)
984 goto ret;
986 my_second = resolve_symlinks (vpath2);
987 if (my_second == NULL)
988 goto ret;
990 for (j = 0; j < 2; j++)
992 p = my_first;
993 q = my_second;
994 while (TRUE)
996 r = strchr (p, PATH_SEP);
997 s = strchr (q, PATH_SEP);
998 if (r == NULL || s == NULL)
999 break;
1000 *r = '\0';
1001 *s = '\0';
1002 if (strcmp (p, q) != 0)
1004 *r = PATH_SEP;
1005 *s = PATH_SEP;
1006 break;
1009 *r = PATH_SEP;
1010 *s = PATH_SEP;
1012 p = r + 1;
1013 q = s + 1;
1015 p--;
1016 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++)
1018 currlen = (i + 1) * 3 + strlen (q) + 1;
1019 if (j != 0)
1021 if (currlen < prevlen)
1022 g_free (buf);
1023 else
1024 goto ret;
1026 p = buf = g_malloc (currlen);
1027 prevlen = currlen;
1028 for (; i >= 0; i--, p += 3)
1029 strcpy (p, "../");
1030 strcpy (p, q);
1033 ret:
1034 g_free (my_first);
1035 g_free (my_second);
1036 return buf;
1039 /* --------------------------------------------------------------------------------------------- */
1041 * Append text to GList, remove all entries with the same text
1044 GList *
1045 list_append_unique (GList * list, char *text)
1047 GList *lc_link;
1050 * Go to the last position and traverse the list backwards
1051 * starting from the second last entry to make sure that we
1052 * are not removing the current link.
1054 list = g_list_append (list, text);
1055 list = g_list_last (list);
1056 lc_link = g_list_previous (list);
1058 while (lc_link != NULL)
1060 GList *newlink;
1062 newlink = g_list_previous (lc_link);
1063 if (strcmp ((char *) lc_link->data, text) == 0)
1065 GList *tmp;
1067 g_free (lc_link->data);
1068 tmp = g_list_remove_link (list, lc_link);
1069 g_list_free_1 (lc_link);
1071 lc_link = newlink;
1074 return list;
1077 /* --------------------------------------------------------------------------------------------- */
1079 * Read and restore position for the given filename.
1080 * If there is no stored data, return line 1 and col 0.
1083 void
1084 load_file_position (const vfs_path_t * filename_vpath, long *line, long *column, off_t * offset,
1085 GArray ** bookmarks)
1087 char *fn;
1088 FILE *f;
1089 char buf[MC_MAXPATHLEN + 100];
1090 const size_t len = vfs_path_len (filename_vpath);
1091 char *filename;
1093 /* defaults */
1094 *line = 1;
1095 *column = 0;
1096 *offset = 0;
1098 /* open file with positions */
1099 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1100 f = fopen (fn, "r");
1101 g_free (fn);
1102 if (f == NULL)
1103 return;
1105 /* prepare array for serialized bookmarks */
1106 *bookmarks = g_array_sized_new (FALSE, FALSE, sizeof (size_t), MAX_SAVED_BOOKMARKS);
1107 filename = vfs_path_to_str (filename_vpath);
1109 while (fgets (buf, sizeof (buf), f) != NULL)
1111 const char *p;
1112 gchar **pos_tokens;
1114 /* check if the filename matches the beginning of string */
1115 if (strncmp (buf, filename, len) != 0)
1116 continue;
1118 /* followed by single space */
1119 if (buf[len] != ' ')
1120 continue;
1122 /* and string without spaces */
1123 p = &buf[len + 1];
1124 if (strchr (p, ' ') != NULL)
1125 continue;
1127 pos_tokens = g_strsplit (p, ";", 3 + MAX_SAVED_BOOKMARKS);
1128 if (pos_tokens[0] == NULL)
1130 *line = 1;
1131 *column = 0;
1132 *offset = 0;
1134 else
1136 *line = strtol (pos_tokens[0], NULL, 10);
1137 if (pos_tokens[1] == NULL)
1139 *column = 0;
1140 *offset = 0;
1142 else
1144 *column = strtol (pos_tokens[1], NULL, 10);
1145 if (pos_tokens[2] == NULL)
1146 *offset = 0;
1147 else
1149 size_t i;
1151 *offset = (off_t) g_ascii_strtoll (pos_tokens[2], NULL, 10);
1153 for (i = 0; i < MAX_SAVED_BOOKMARKS && pos_tokens[3 + i] != NULL; i++)
1155 size_t val;
1157 val = strtoul (pos_tokens[3 + i], NULL, 10);
1158 g_array_append_val (*bookmarks, val);
1164 g_strfreev (pos_tokens);
1167 g_free (filename);
1168 fclose (f);
1171 /* --------------------------------------------------------------------------------------------- */
1173 * Save position for the given file
1176 void
1177 save_file_position (const vfs_path_t * filename_vpath, long line, long column, off_t offset,
1178 GArray * bookmarks)
1180 static size_t filepos_max_saved_entries = 0;
1181 char *fn, *tmp_fn;
1182 FILE *f, *tmp_f;
1183 char buf[MC_MAXPATHLEN + 100];
1184 size_t i;
1185 const size_t len = vfs_path_len (filename_vpath);
1186 gboolean src_error = FALSE;
1187 char *filename;
1189 if (filepos_max_saved_entries == 0)
1190 filepos_max_saved_entries = mc_config_get_int (mc_main_config, CONFIG_APP_SECTION,
1191 "filepos_max_saved_entries", 1024);
1193 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1194 if (fn == NULL)
1195 goto early_error;
1197 mc_util_make_backup_if_possible (fn, TMP_SUFFIX);
1199 /* open file */
1200 f = fopen (fn, "w");
1201 if (f == NULL)
1202 goto open_target_error;
1204 tmp_fn = g_strdup_printf ("%s" TMP_SUFFIX, fn);
1205 tmp_f = fopen (tmp_fn, "r");
1206 if (tmp_f == NULL)
1208 src_error = TRUE;
1209 goto open_source_error;
1212 filename = vfs_path_to_str (filename_vpath);
1213 /* put the new record */
1214 if (line != 1 || column != 0 || bookmarks != NULL)
1216 if (fprintf (f, "%s %ld;%ld;%" PRIuMAX, filename, line, column, (uintmax_t) offset) < 0)
1217 goto write_position_error;
1218 if (bookmarks != NULL)
1219 for (i = 0; i < bookmarks->len && i < MAX_SAVED_BOOKMARKS; i++)
1220 if (fprintf (f, ";%zu", g_array_index (bookmarks, size_t, i)) < 0)
1221 goto write_position_error;
1223 if (fprintf (f, "\n") < 0)
1224 goto write_position_error;
1227 i = 1;
1228 while (fgets (buf, sizeof (buf), tmp_f) != NULL)
1230 if (buf[len] == ' ' && strncmp (buf, filename, len) == 0
1231 && strchr (&buf[len + 1], ' ') == NULL)
1232 continue;
1234 fprintf (f, "%s", buf);
1235 if (++i > filepos_max_saved_entries)
1236 break;
1239 write_position_error:
1240 g_free (filename);
1241 fclose (tmp_f);
1242 open_source_error:
1243 g_free (tmp_fn);
1244 fclose (f);
1245 if (src_error)
1246 mc_util_restore_from_backup_if_possible (fn, TMP_SUFFIX);
1247 else
1248 mc_util_unlink_backup_if_possible (fn, TMP_SUFFIX);
1249 open_target_error:
1250 g_free (fn);
1251 early_error:
1252 if (bookmarks != NULL)
1253 g_array_free (bookmarks, TRUE);
1256 /* --------------------------------------------------------------------------------------------- */
1258 extern int
1259 ascii_alpha_to_cntrl (int ch)
1261 if ((ch >= ASCII_A && ch <= ASCII_Z) || (ch >= ASCII_a && ch <= ASCII_z))
1263 ch &= 0x1f;
1265 return ch;
1268 /* --------------------------------------------------------------------------------------------- */
1270 const char *
1271 Q_ (const char *s)
1273 const char *result, *sep;
1275 result = _(s);
1276 sep = strchr (result, '|');
1277 return (sep != NULL) ? sep + 1 : result;
1280 /* --------------------------------------------------------------------------------------------- */
1282 gboolean
1283 mc_util_make_backup_if_possible (const char *file_name, const char *backup_suffix)
1285 struct stat stat_buf;
1286 char *backup_path;
1287 gboolean ret;
1288 if (!exist_file (file_name))
1289 return FALSE;
1291 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1293 if (backup_path == NULL)
1294 return FALSE;
1296 ret = mc_util_write_backup_content (file_name, backup_path);
1298 if (ret)
1300 /* Backup file will have same ownership with main file. */
1301 if (stat (file_name, &stat_buf) == 0)
1302 chmod (backup_path, stat_buf.st_mode);
1303 else
1304 chmod (backup_path, S_IRUSR | S_IWUSR);
1307 g_free (backup_path);
1309 return ret;
1312 /* --------------------------------------------------------------------------------------------- */
1314 gboolean
1315 mc_util_restore_from_backup_if_possible (const char *file_name, const char *backup_suffix)
1317 gboolean ret;
1318 char *backup_path;
1320 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1321 if (backup_path == NULL)
1322 return FALSE;
1324 ret = mc_util_write_backup_content (backup_path, file_name);
1325 g_free (backup_path);
1327 return ret;
1330 /* --------------------------------------------------------------------------------------------- */
1332 gboolean
1333 mc_util_unlink_backup_if_possible (const char *file_name, const char *backup_suffix)
1335 char *backup_path;
1337 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1338 if (backup_path == NULL)
1339 return FALSE;
1341 if (exist_file (backup_path))
1343 vfs_path_t *vpath;
1345 vpath = vfs_path_from_str (backup_path);
1346 mc_unlink (vpath);
1347 vfs_path_free (vpath);
1350 g_free (backup_path);
1351 return TRUE;
1354 /* --------------------------------------------------------------------------------------------- */
1356 * partly taken from dcigettext.c, returns "" for default locale
1357 * value should be freed by calling function g_free()
1360 char *
1361 guess_message_value (void)
1363 static const char *const var[] = {
1364 /* Setting of LC_ALL overwrites all other. */
1365 /* Do not use LANGUAGE for check user locale and drowing hints */
1366 "LC_ALL",
1367 /* Next comes the name of the desired category. */
1368 "LC_MESSAGES",
1369 /* Last possibility is the LANG environment variable. */
1370 "LANG",
1371 /* NULL exit loops */
1372 NULL
1375 unsigned i = 0;
1376 const char *locale = NULL;
1378 while (var[i] != NULL)
1380 locale = getenv (var[i]);
1381 if (locale != NULL && locale[0] != '\0')
1382 break;
1383 i++;
1386 if (locale == NULL)
1387 locale = "";
1389 return g_strdup (locale);
1392 /* --------------------------------------------------------------------------------------------- */