Ticket #3241: colon was not recognized inside escape seq in prompt.
[midnight-commander.git] / lib / util.c
blobceace41f1f794a84cfb3decfea2467af267219c8
1 /*
2 Various utilities
4 Copyright (C) 1994-2014
5 Free Software Foundation, Inc.
7 Written by:
8 Miguel de Icaza, 1994, 1995, 1996
9 Janne Kukonlehto, 1994, 1995, 1996
10 Dugan Porter, 1994, 1995, 1996
11 Jakub Jelinek, 1994, 1995, 1996
12 Mauricio Plaza, 1994, 1995, 1996
13 Slava Zanko <slavazanko@gmail.com>, 2013
15 This file is part of the Midnight Commander.
17 The Midnight Commander is free software: you can redistribute it
18 and/or modify it under the terms of the GNU General Public License as
19 published by the Free Software Foundation, either version 3 of the License,
20 or (at your option) any later version.
22 The Midnight Commander is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 GNU General Public License for more details.
27 You should have received a copy of the GNU General Public License
28 along with this program. If not, see <http://www.gnu.org/licenses/>.
31 /** \file lib/util.c
32 * \brief Source: various utilities
35 #include <config.h>
37 #include <ctype.h>
38 #include <limits.h>
39 #include <stdarg.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <fcntl.h>
44 #include <sys/time.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <unistd.h>
49 #include "lib/global.h"
50 #include "lib/mcconfig.h"
51 #include "lib/fileloc.h"
52 #include "lib/vfs/vfs.h"
53 #include "lib/strutil.h"
54 #include "lib/util.h"
56 /*** global variables ****************************************************************************/
58 /*** file scope macro definitions ****************************************************************/
60 #define ismode(n,m) ((n & m) == m)
62 /* Number of attempts to create a temporary file */
63 #ifndef TMP_MAX
64 #define TMP_MAX 16384
65 #endif /* !TMP_MAX */
67 #define TMP_SUFFIX ".tmp"
69 #define ASCII_A (0x40 + 1)
70 #define ASCII_Z (0x40 + 26)
71 #define ASCII_a (0x60 + 1)
72 #define ASCII_z (0x60 + 26)
74 /*** file scope type declarations ****************************************************************/
76 /*** file scope variables ************************************************************************/
78 /*** file scope functions ************************************************************************/
79 /* --------------------------------------------------------------------------------------------- */
81 static inline int
82 is_7bit_printable (unsigned char c)
84 return (c > 31 && c < 127);
87 /* --------------------------------------------------------------------------------------------- */
89 static inline int
90 is_iso_printable (unsigned char c)
92 return ((c > 31 && c < 127) || c >= 160);
95 /* --------------------------------------------------------------------------------------------- */
97 static inline int
98 is_8bit_printable (unsigned char c)
100 /* "Full 8 bits output" doesn't work on xterm */
101 if (mc_global.tty.xterm_flag)
102 return is_iso_printable (c);
104 return (c > 31 && c != 127 && c != 155);
107 /* --------------------------------------------------------------------------------------------- */
109 static char *
110 resolve_symlinks (const vfs_path_t * vpath)
112 char *p, *p2;
113 char *buf, *buf2, *q, *r, c;
114 struct stat mybuf;
116 if (vpath->relative)
117 return NULL;
119 p = p2 = g_strdup (vfs_path_as_str (vpath));
120 r = buf = g_malloc (MC_MAXPATHLEN);
121 buf2 = g_malloc (MC_MAXPATHLEN);
122 *r++ = PATH_SEP;
123 *r = 0;
127 q = strchr (p + 1, PATH_SEP);
128 if (!q)
130 q = strchr (p + 1, 0);
131 if (q == p + 1)
132 break;
134 c = *q;
135 *q = 0;
136 if (mc_lstat (vpath, &mybuf) < 0)
138 MC_PTR_FREE (buf);
139 goto ret;
141 if (!S_ISLNK (mybuf.st_mode))
142 strcpy (r, p + 1);
143 else
145 int len;
147 len = mc_readlink (vpath, buf2, MC_MAXPATHLEN - 1);
148 if (len < 0)
150 MC_PTR_FREE (buf);
151 goto ret;
153 buf2[len] = 0;
154 if (*buf2 == PATH_SEP)
155 strcpy (buf, buf2);
156 else
157 strcpy (r, buf2);
159 canonicalize_pathname (buf);
160 r = strchr (buf, 0);
161 if (!*r || *(r - 1) != PATH_SEP)
162 /* FIXME: this condition is always true because r points to the EOL */
164 *r++ = PATH_SEP;
165 *r = 0;
167 *q = c;
168 p = q;
170 while (c != '\0');
172 if (!*buf)
173 strcpy (buf, PATH_SEP_STR);
174 else if (*(r - 1) == PATH_SEP && r != buf + 1)
175 *(r - 1) = 0;
177 ret:
178 g_free (buf2);
179 g_free (p2);
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;
208 /* cppcheck-suppress redundantAssignment */
209 ret2 = fflush (backup_fd);
210 /* cppcheck-suppress redundantAssignment */
211 ret2 = fclose (backup_fd);
212 (void) ret2;
214 g_free (contents);
215 return ret1;
218 /* --------------------------------------------------------------------------------------------- */
219 /*** public functions ****************************************************************************/
220 /* --------------------------------------------------------------------------------------------- */
223 is_printable (int c)
225 c &= 0xff;
227 #ifdef HAVE_CHARSET
228 /* "Display bits" is ignored, since the user controls the output
229 by setting the output codepage */
230 return is_8bit_printable (c);
231 #else
232 if (!mc_global.eight_bit_clean)
233 return is_7bit_printable (c);
235 if (mc_global.full_eight_bits)
237 return is_8bit_printable (c);
239 else
240 return is_iso_printable (c);
241 #endif /* !HAVE_CHARSET */
244 /* --------------------------------------------------------------------------------------------- */
246 * Quote the filename for the purpose of inserting it into the command
247 * line. If quote_percent is 1, replace "%" with "%%" - the percent is
248 * processed by the mc command line.
250 char *
251 name_quote (const char *s, int quote_percent)
253 char *ret, *d;
255 d = ret = g_malloc (strlen (s) * 2 + 2 + 1);
256 if (*s == '-')
258 *d++ = '.';
259 *d++ = '/';
262 for (; *s; s++, d++)
264 switch (*s)
266 case '%':
267 if (quote_percent)
268 *d++ = '%';
269 break;
270 case '\'':
271 case '\\':
272 case '\r':
273 case '\n':
274 case '\t':
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 case '&':
290 case '*':
291 case '(':
292 case ')':
293 *d++ = '\\';
294 break;
295 case '~':
296 case '#':
297 if (d == ret)
298 *d++ = '\\';
299 break;
301 *d = *s;
303 *d = '\0';
304 return ret;
307 /* --------------------------------------------------------------------------------------------- */
309 char *
310 fake_name_quote (const char *s, int quote_percent)
312 (void) quote_percent;
313 return g_strdup (s);
316 /* --------------------------------------------------------------------------------------------- */
318 * path_trunc() is the same as str_trunc() but
319 * it deletes possible password from path for security
320 * reasons.
323 const char *
324 path_trunc (const char *path, size_t trunc_len)
326 vfs_path_t *vpath;
327 char *secure_path;
328 const char *ret;
330 vpath = vfs_path_from_str (path);
331 secure_path = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
332 vfs_path_free (vpath);
334 ret = str_trunc (secure_path, trunc_len);
335 g_free (secure_path);
337 return ret;
340 /* --------------------------------------------------------------------------------------------- */
342 const char *
343 size_trunc (uintmax_t size, gboolean use_si)
345 static char x[BUF_TINY];
346 uintmax_t divisor = 1;
347 const char *xtra = "";
349 if (size > 999999999UL)
351 divisor = use_si ? 1000 : 1024;
352 xtra = use_si ? "k" : "K";
354 if (size / divisor > 999999999UL)
356 divisor = use_si ? (1000 * 1000) : (1024 * 1024);
357 xtra = use_si ? "m" : "M";
359 if (size / divisor > 999999999UL)
361 divisor = use_si ? (1000 * 1000 * 1000) : (1024 * 1024 * 1024);
362 xtra = use_si ? "g" : "G";
366 g_snprintf (x, sizeof (x), "%.0f%s", 1.0 * size / divisor, xtra);
367 return x;
370 /* --------------------------------------------------------------------------------------------- */
372 const char *
373 size_trunc_sep (uintmax_t size, gboolean use_si)
375 static char x[60];
376 int count;
377 const char *p, *y;
378 char *d;
380 p = y = size_trunc (size, use_si);
381 p += strlen (p) - 1;
382 d = x + sizeof (x) - 1;
383 *d-- = '\0';
384 while (p >= y && isalpha ((unsigned char) *p))
385 *d-- = *p--;
386 for (count = 0; p >= y; count++)
388 if (count == 3)
390 *d-- = ',';
391 count = 0;
393 *d-- = *p--;
395 d++;
396 if (*d == ',')
397 d++;
398 return d;
401 /* --------------------------------------------------------------------------------------------- */
403 * Print file SIZE to BUFFER, but don't exceed LEN characters,
404 * not including trailing 0. BUFFER should be at least LEN+1 long.
405 * This function is called for every file on panels, so avoid
406 * floating point by any means.
408 * Units: size units (filesystem sizes are 1K blocks)
409 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
412 void
413 size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gboolean use_si)
415 /* Avoid taking power for every file. */
416 /* *INDENT-OFF* */
417 static const uintmax_t power10[] = {
418 /* we hope that size of uintmax_t is 4 bytes at least */
419 1ULL,
420 10ULL,
421 100ULL,
422 1000ULL,
423 10000ULL,
424 100000ULL,
425 1000000ULL,
426 10000000ULL,
427 100000000ULL,
428 1000000000ULL
429 /* maximum value of uintmax_t (in case of 4 bytes) is
430 4294967295
432 #if SIZEOF_UINTMAX_T == 8
434 10000000000ULL,
435 100000000000ULL,
436 1000000000000ULL,
437 10000000000000ULL,
438 100000000000000ULL,
439 1000000000000000ULL,
440 10000000000000000ULL,
441 100000000000000000ULL,
442 1000000000000000000ULL,
443 10000000000000000000ULL
444 /* maximum value of uintmax_t (in case of 8 bytes) is
445 18447644073710439615
447 #endif
449 /* *INDENT-ON* */
450 static const char *const suffix[] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL };
451 static const char *const suffix_lc[] = { "", "k", "m", "g", "t", "p", "e", "z", "y", NULL };
453 const char *const *sfx = use_si ? suffix_lc : suffix;
454 int j = 0;
456 if (len == 0)
457 len = 9;
458 #if SIZEOF_UINTMAX_T == 8
459 /* 20 decimal digits are required to represent 8 bytes */
460 else if (len > 19)
461 len = 19;
462 #else
463 /* 10 decimal digits are required to represent 4 bytes */
464 else if (len > 9)
465 len = 9;
466 #endif
469 * recalculate from 1024 base to 1000 base if units>0
470 * We can't just multiply by 1024 - that might cause overflow
471 * if uintmax_t type is too small
473 if (use_si)
474 for (j = 0; j < units; j++)
476 uintmax_t size_remain;
478 size_remain = ((size % 125) * 1024) / 1000; /* size mod 125, recalculated */
479 size /= 125; /* 128/125 = 1024/1000 */
480 size *= 128; /* This will convert size from multiple of 1024 to multiple of 1000 */
481 size += size_remain; /* Re-add remainder lost by division/multiplication */
484 for (j = units; sfx[j] != NULL; j++)
486 if (size == 0)
488 if (j == units)
490 /* Empty files will print "0" even with minimal width. */
491 g_snprintf (buffer, len + 1, "%s", "0");
493 else
495 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
496 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s", (j > 1) ? sfx[j - 1] : "B");
498 break;
501 if (size < power10[len - (j > 0 ? 1 : 0)])
503 g_snprintf (buffer, len + 1, "%" PRIuMAX "%s", size, sfx[j]);
504 break;
507 /* Powers of 1000 or 1024, with rounding. */
508 if (use_si)
509 size = (size + 500) / 1000;
510 else
511 size = (size + 512) >> 10;
515 /* --------------------------------------------------------------------------------------------- */
517 const char *
518 string_perm (mode_t mode_bits)
520 static char mode[11];
522 strcpy (mode, "----------");
523 if (S_ISDIR (mode_bits))
524 mode[0] = 'd';
525 if (S_ISCHR (mode_bits))
526 mode[0] = 'c';
527 if (S_ISBLK (mode_bits))
528 mode[0] = 'b';
529 if (S_ISLNK (mode_bits))
530 mode[0] = 'l';
531 if (S_ISFIFO (mode_bits))
532 mode[0] = 'p';
533 if (S_ISNAM (mode_bits))
534 mode[0] = 'n';
535 if (S_ISSOCK (mode_bits))
536 mode[0] = 's';
537 if (S_ISDOOR (mode_bits))
538 mode[0] = 'D';
539 if (ismode (mode_bits, S_IXOTH))
540 mode[9] = 'x';
541 if (ismode (mode_bits, S_IWOTH))
542 mode[8] = 'w';
543 if (ismode (mode_bits, S_IROTH))
544 mode[7] = 'r';
545 if (ismode (mode_bits, S_IXGRP))
546 mode[6] = 'x';
547 if (ismode (mode_bits, S_IWGRP))
548 mode[5] = 'w';
549 if (ismode (mode_bits, S_IRGRP))
550 mode[4] = 'r';
551 if (ismode (mode_bits, S_IXUSR))
552 mode[3] = 'x';
553 if (ismode (mode_bits, S_IWUSR))
554 mode[2] = 'w';
555 if (ismode (mode_bits, S_IRUSR))
556 mode[1] = 'r';
557 #ifdef S_ISUID
558 if (ismode (mode_bits, S_ISUID))
559 mode[3] = (mode[3] == 'x') ? 's' : 'S';
560 #endif /* S_ISUID */
561 #ifdef S_ISGID
562 if (ismode (mode_bits, S_ISGID))
563 mode[6] = (mode[6] == 'x') ? 's' : 'S';
564 #endif /* S_ISGID */
565 #ifdef S_ISVTX
566 if (ismode (mode_bits, S_ISVTX))
567 mode[9] = (mode[9] == 'x') ? 't' : 'T';
568 #endif /* S_ISVTX */
569 return mode;
572 /* --------------------------------------------------------------------------------------------- */
574 const char *
575 extension (const char *filename)
577 const char *d = strrchr (filename, '.');
578 return (d != NULL) ? d + 1 : "";
581 /* --------------------------------------------------------------------------------------------- */
583 char *
584 load_mc_home_file (const char *from, const char *filename, char **allocated_filename)
586 char *hintfile_base, *hintfile;
587 char *lang;
588 char *data;
590 hintfile_base = g_build_filename (from, filename, (char *) NULL);
591 lang = guess_message_value ();
593 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
594 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
596 /* Fall back to the two-letter language code */
597 if (lang[0] != '\0' && lang[1] != '\0')
598 lang[2] = '\0';
599 g_free (hintfile);
600 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
601 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
603 g_free (hintfile);
604 hintfile = hintfile_base;
605 g_file_get_contents (hintfile_base, &data, NULL, NULL);
609 g_free (lang);
611 if (hintfile != hintfile_base)
612 g_free (hintfile_base);
614 if (allocated_filename != NULL)
615 *allocated_filename = hintfile;
616 else
617 g_free (hintfile);
619 return data;
622 /* --------------------------------------------------------------------------------------------- */
624 const char *
625 extract_line (const char *s, const char *top)
627 static char tmp_line[BUF_MEDIUM];
628 char *t = tmp_line;
630 while (*s && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line) - 1 && s < top)
631 *t++ = *s++;
632 *t = 0;
633 return tmp_line;
636 /* --------------------------------------------------------------------------------------------- */
638 * The basename routine
641 const char *
642 x_basename (const char *s)
644 const char *url_delim, *path_sep;
646 url_delim = g_strrstr (s, VFS_PATH_URL_DELIMITER);
647 path_sep = strrchr (s, PATH_SEP);
649 if (path_sep == NULL)
650 return s;
652 if (url_delim == NULL
653 || url_delim < path_sep - strlen (VFS_PATH_URL_DELIMITER)
654 || url_delim - s + strlen (VFS_PATH_URL_DELIMITER) < strlen (s))
656 /* avoid trailing PATH_SEP, if present */
657 if (s[strlen (s) - 1] == PATH_SEP)
659 while (--path_sep > s && *path_sep != PATH_SEP);
660 return (path_sep != s) ? path_sep + 1 : s;
662 else
663 return (path_sep != NULL) ? path_sep + 1 : s;
666 while (--url_delim > s && *url_delim != PATH_SEP);
667 while (--url_delim > s && *url_delim != PATH_SEP);
669 return (url_delim == s) ? s : url_delim + 1;
672 /* --------------------------------------------------------------------------------------------- */
674 const char *
675 unix_error_string (int error_num)
677 static char buffer[BUF_LARGE];
678 gchar *strerror_currentlocale;
680 strerror_currentlocale = g_locale_from_utf8 (g_strerror (error_num), -1, NULL, NULL, NULL);
681 g_snprintf (buffer, sizeof (buffer), "%s (%d)", strerror_currentlocale, error_num);
682 g_free (strerror_currentlocale);
684 return buffer;
687 /* --------------------------------------------------------------------------------------------- */
689 const char *
690 skip_separators (const char *s)
692 const char *su = s;
694 for (; *su; str_cnext_char (&su))
695 if (*su != ' ' && *su != '\t' && *su != ',')
696 break;
698 return su;
701 /* --------------------------------------------------------------------------------------------- */
703 const char *
704 skip_numbers (const char *s)
706 const char *su = s;
708 for (; *su; str_cnext_char (&su))
709 if (!str_isdigit (su))
710 break;
712 return su;
715 /* --------------------------------------------------------------------------------------------- */
717 * Remove all control sequences from the argument string. We define
718 * "control sequence", in a sort of pidgin BNF, as follows:
720 * control-seq = Esc non-'['
721 * | Esc '[' (0 or more digits or ';' or ':' or '?') (any other char)
723 * The 256-color and true-color escape sequences should allow either ';' or ':' inside as separator,
724 * actually, ':' is the more correct according to ECMA-48.
725 * Some terminal emulators (e.g. xterm, gnome-terminal) support this.
727 * Non-printable characters are also removed.
730 char *
731 strip_ctrl_codes (char *s)
733 char *w; /* Current position where the stripped data is written */
734 char *r; /* Current position where the original data is read */
736 if (s == NULL)
737 return NULL;
739 for (w = s, r = s; *r != '\0';)
741 if (*r == ESC_CHAR)
743 /* Skip the control sequence's arguments */ ;
744 /* '(' need to avoid strange 'B' letter in *Suse (if mc runs under root user) */
745 if (*(++r) == '[' || *r == '(')
747 /* strchr() matches trailing binary 0 */
748 while (*(++r) != '\0' && strchr ("0123456789;:?", *r) != NULL)
751 else if (*r == ']')
754 * Skip xterm's OSC (Operating System Command)
755 * http://www.xfree86.org/current/ctlseqs.html
756 * OSC P s ; P t ST
757 * OSC P s ; P t BEL
759 char *new_r = r;
761 for (; *new_r != '\0'; ++new_r)
763 switch (*new_r)
765 /* BEL */
766 case '\a':
767 r = new_r;
768 goto osc_out;
769 case ESC_CHAR:
770 /* ST */
771 if (*(new_r + 1) == '\\')
773 r = new_r + 1;
774 goto osc_out;
778 osc_out:
783 * Now we are at the last character of the sequence.
784 * Skip it unless it's binary 0.
786 if (*r != '\0')
787 r++;
789 else
791 char *n;
793 n = str_get_next_char (r);
794 if (str_isprint (r))
796 memmove (w, r, n - r);
797 w += n - r;
799 r = n;
803 *w = '\0';
804 return s;
807 /* --------------------------------------------------------------------------------------------- */
809 enum compression_type
810 get_compression_type (int fd, const char *name)
812 unsigned char magic[16];
813 size_t str_len;
815 /* Read the magic signature */
816 if (mc_read (fd, (char *) magic, 4) != 4)
817 return COMPRESSION_NONE;
819 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
820 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236))
822 return COMPRESSION_GZIP;
825 /* PKZIP_MAGIC */
826 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003 && magic[3] == 004)
828 /* Read compression type */
829 mc_lseek (fd, 8, SEEK_SET);
830 if (mc_read (fd, (char *) magic, 2) != 2)
831 return COMPRESSION_NONE;
833 /* Gzip can handle only deflated (8) or stored (0) files */
834 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
835 return COMPRESSION_NONE;
837 /* Compatible with gzip */
838 return COMPRESSION_GZIP;
841 /* PACK_MAGIC and LZH_MAGIC and compress magic */
842 if (magic[0] == 037 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235))
844 /* Compatible with gzip */
845 return COMPRESSION_GZIP;
848 /* BZIP and BZIP2 files */
849 if ((magic[0] == 'B') && (magic[1] == 'Z') && (magic[3] >= '1') && (magic[3] <= '9'))
851 switch (magic[2])
853 case '0':
854 return COMPRESSION_BZIP;
855 case 'h':
856 return COMPRESSION_BZIP2;
860 /* Support for LZMA (only utils format with magic in header).
861 * This is the default format of LZMA utils 4.32.1 and later. */
863 if (mc_read (fd, (char *) magic + 4, 2) != 2)
864 return COMPRESSION_NONE;
866 /* LZMA utils format */
867 if (magic[0] == 0xFF
868 && magic[1] == 'L'
869 && magic[2] == 'Z' && magic[3] == 'M' && magic[4] == 'A' && magic[5] == 0x00)
870 return COMPRESSION_LZMA;
872 /* XZ compression magic */
873 if (magic[0] == 0xFD
874 && magic[1] == 0x37
875 && magic[2] == 0x7A && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00)
876 return COMPRESSION_XZ;
878 str_len = strlen (name);
879 /* HACK: we must belive to extension of LZMA file :) ... */
880 if ((str_len > 5 && strcmp (&name[str_len - 5], ".lzma") == 0) ||
881 (str_len > 4 && strcmp (&name[str_len - 4], ".tlz") == 0))
882 return COMPRESSION_LZMA;
884 return COMPRESSION_NONE;
887 /* --------------------------------------------------------------------------------------------- */
889 const char *
890 decompress_extension (int type)
892 switch (type)
894 case COMPRESSION_GZIP:
895 return "/ugz" VFS_PATH_URL_DELIMITER;
896 case COMPRESSION_BZIP:
897 return "/ubz" VFS_PATH_URL_DELIMITER;
898 case COMPRESSION_BZIP2:
899 return "/ubz2" VFS_PATH_URL_DELIMITER;
900 case COMPRESSION_LZMA:
901 return "/ulzma" VFS_PATH_URL_DELIMITER;
902 case COMPRESSION_XZ:
903 return "/uxz" VFS_PATH_URL_DELIMITER;
905 /* Should never reach this place */
906 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
907 return 0;
910 /* --------------------------------------------------------------------------------------------- */
912 void
913 wipe_password (char *passwd)
915 char *p = passwd;
917 if (!p)
918 return;
919 for (; *p; p++)
920 *p = 0;
921 g_free (passwd);
924 /* --------------------------------------------------------------------------------------------- */
926 * Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key
928 * @param p pointer to string
930 * @return newly allocated string
933 char *
934 convert_controls (const char *p)
936 char *valcopy = g_strdup (p);
937 char *q;
939 /* Parse the escape special character */
940 for (q = valcopy; *p;)
942 if (*p == '\\')
944 p++;
945 if ((*p == 'e') || (*p == 'E'))
947 p++;
948 *q++ = ESC_CHAR;
951 else
953 if (*p == '^')
955 p++;
956 if (*p == '^')
957 *q++ = *p++;
958 else
960 char c = (*p | 0x20);
961 if (c >= 'a' && c <= 'z')
963 *q++ = c - 'a' + 1;
964 p++;
966 else if (*p)
967 p++;
970 else
971 *q++ = *p++;
974 *q = 0;
975 return valcopy;
978 /* --------------------------------------------------------------------------------------------- */
980 * Finds out a relative path from first to second, i.e. goes as many ..
981 * as needed up in first and then goes down using second
984 char *
985 diff_two_paths (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
987 char *p, *q, *r, *s, *buf = NULL;
988 int i, j, prevlen = -1, currlen;
989 char *my_first = NULL, *my_second = NULL;
991 my_first = resolve_symlinks (vpath1);
992 if (my_first == NULL)
993 goto ret;
995 my_second = resolve_symlinks (vpath2);
996 if (my_second == NULL)
997 goto ret;
999 for (j = 0; j < 2; j++)
1001 p = my_first;
1002 q = my_second;
1003 while (TRUE)
1005 r = strchr (p, PATH_SEP);
1006 s = strchr (q, PATH_SEP);
1007 if (r == NULL || s == NULL)
1008 break;
1009 *r = '\0';
1010 *s = '\0';
1011 if (strcmp (p, q) != 0)
1013 *r = PATH_SEP;
1014 *s = PATH_SEP;
1015 break;
1018 *r = PATH_SEP;
1019 *s = PATH_SEP;
1021 p = r + 1;
1022 q = s + 1;
1024 p--;
1025 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++)
1027 currlen = (i + 1) * 3 + strlen (q) + 1;
1028 if (j != 0)
1030 if (currlen < prevlen)
1031 g_free (buf);
1032 else
1033 goto ret;
1035 p = buf = g_malloc (currlen);
1036 prevlen = currlen;
1037 for (; i >= 0; i--, p += 3)
1038 strcpy (p, "../");
1039 strcpy (p, q);
1042 ret:
1043 g_free (my_first);
1044 g_free (my_second);
1045 return buf;
1048 /* --------------------------------------------------------------------------------------------- */
1050 * Append text to GList, remove all entries with the same text
1053 GList *
1054 list_append_unique (GList * list, char *text)
1056 GList *lc_link;
1059 * Go to the last position and traverse the list backwards
1060 * starting from the second last entry to make sure that we
1061 * are not removing the current link.
1063 list = g_list_append (list, text);
1064 list = g_list_last (list);
1065 lc_link = g_list_previous (list);
1067 while (lc_link != NULL)
1069 GList *newlink;
1071 newlink = g_list_previous (lc_link);
1072 if (strcmp ((char *) lc_link->data, text) == 0)
1074 GList *tmp;
1076 g_free (lc_link->data);
1077 tmp = g_list_remove_link (list, lc_link);
1078 (void) tmp;
1079 g_list_free_1 (lc_link);
1081 lc_link = newlink;
1084 return list;
1087 /* --------------------------------------------------------------------------------------------- */
1089 * Read and restore position for the given filename.
1090 * If there is no stored data, return line 1 and col 0.
1093 void
1094 load_file_position (const vfs_path_t * filename_vpath, long *line, long *column, off_t * offset,
1095 GArray ** bookmarks)
1097 char *fn;
1098 FILE *f;
1099 char buf[MC_MAXPATHLEN + 100];
1100 const size_t len = vfs_path_len (filename_vpath);
1102 /* defaults */
1103 *line = 1;
1104 *column = 0;
1105 *offset = 0;
1107 /* open file with positions */
1108 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1109 f = fopen (fn, "r");
1110 g_free (fn);
1111 if (f == NULL)
1112 return;
1114 /* prepare array for serialized bookmarks */
1115 if (bookmarks != NULL)
1116 *bookmarks = g_array_sized_new (FALSE, FALSE, sizeof (size_t), MAX_SAVED_BOOKMARKS);
1118 while (fgets (buf, sizeof (buf), f) != NULL)
1120 const char *p;
1121 gchar **pos_tokens;
1123 /* check if the filename matches the beginning of string */
1124 if (strncmp (buf, vfs_path_as_str (filename_vpath), len) != 0)
1125 continue;
1127 /* followed by single space */
1128 if (buf[len] != ' ')
1129 continue;
1131 /* and string without spaces */
1132 p = &buf[len + 1];
1133 if (strchr (p, ' ') != NULL)
1134 continue;
1136 pos_tokens = g_strsplit (p, ";", 3 + MAX_SAVED_BOOKMARKS);
1137 if (pos_tokens[0] == NULL)
1139 *line = 1;
1140 *column = 0;
1141 *offset = 0;
1143 else
1145 *line = strtol (pos_tokens[0], NULL, 10);
1146 if (pos_tokens[1] == NULL)
1148 *column = 0;
1149 *offset = 0;
1151 else
1153 *column = strtol (pos_tokens[1], NULL, 10);
1154 if (pos_tokens[2] == NULL)
1155 *offset = 0;
1156 else if (bookmarks != NULL)
1158 size_t i;
1160 *offset = (off_t) g_ascii_strtoll (pos_tokens[2], NULL, 10);
1162 for (i = 0; i < MAX_SAVED_BOOKMARKS && pos_tokens[3 + i] != NULL; i++)
1164 size_t val;
1166 val = strtoul (pos_tokens[3 + i], NULL, 10);
1167 g_array_append_val (*bookmarks, val);
1173 g_strfreev (pos_tokens);
1176 fclose (f);
1179 /* --------------------------------------------------------------------------------------------- */
1181 * Save position for the given file
1184 void
1185 save_file_position (const vfs_path_t * filename_vpath, long line, long column, off_t offset,
1186 GArray * bookmarks)
1188 static size_t filepos_max_saved_entries = 0;
1189 char *fn, *tmp_fn;
1190 FILE *f, *tmp_f;
1191 char buf[MC_MAXPATHLEN + 100];
1192 size_t i;
1193 const size_t len = vfs_path_len (filename_vpath);
1194 gboolean src_error = FALSE;
1196 if (filepos_max_saved_entries == 0)
1197 filepos_max_saved_entries = mc_config_get_int (mc_main_config, CONFIG_APP_SECTION,
1198 "filepos_max_saved_entries", 1024);
1200 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1201 if (fn == NULL)
1202 goto early_error;
1204 mc_util_make_backup_if_possible (fn, TMP_SUFFIX);
1206 /* open file */
1207 f = fopen (fn, "w");
1208 if (f == NULL)
1209 goto open_target_error;
1211 tmp_fn = g_strdup_printf ("%s" TMP_SUFFIX, fn);
1212 tmp_f = fopen (tmp_fn, "r");
1213 if (tmp_f == NULL)
1215 src_error = TRUE;
1216 goto open_source_error;
1219 /* put the new record */
1220 if (line != 1 || column != 0 || bookmarks != NULL)
1222 if (fprintf
1223 (f, "%s %ld;%ld;%" PRIuMAX, vfs_path_as_str (filename_vpath), line, column,
1224 (uintmax_t) offset) < 0)
1225 goto write_position_error;
1226 if (bookmarks != NULL)
1227 for (i = 0; i < bookmarks->len && i < MAX_SAVED_BOOKMARKS; i++)
1228 if (fprintf (f, ";%zu", g_array_index (bookmarks, size_t, i)) < 0)
1229 goto write_position_error;
1231 if (fprintf (f, "\n") < 0)
1232 goto write_position_error;
1235 i = 1;
1236 while (fgets (buf, sizeof (buf), tmp_f) != NULL)
1238 if (buf[len] == ' ' && strncmp (buf, vfs_path_as_str (filename_vpath), len) == 0
1239 && strchr (&buf[len + 1], ' ') == NULL)
1240 continue;
1242 fprintf (f, "%s", buf);
1243 if (++i > filepos_max_saved_entries)
1244 break;
1247 write_position_error:
1248 fclose (tmp_f);
1249 open_source_error:
1250 g_free (tmp_fn);
1251 fclose (f);
1252 if (src_error)
1253 mc_util_restore_from_backup_if_possible (fn, TMP_SUFFIX);
1254 else
1255 mc_util_unlink_backup_if_possible (fn, TMP_SUFFIX);
1256 open_target_error:
1257 g_free (fn);
1258 early_error:
1259 if (bookmarks != NULL)
1260 g_array_free (bookmarks, TRUE);
1263 /* --------------------------------------------------------------------------------------------- */
1265 extern int
1266 ascii_alpha_to_cntrl (int ch)
1268 if ((ch >= ASCII_A && ch <= ASCII_Z) || (ch >= ASCII_a && ch <= ASCII_z))
1270 ch &= 0x1f;
1272 return ch;
1275 /* --------------------------------------------------------------------------------------------- */
1277 const char *
1278 Q_ (const char *s)
1280 const char *result, *sep;
1282 result = _(s);
1283 sep = strchr (result, '|');
1284 return (sep != NULL) ? sep + 1 : result;
1287 /* --------------------------------------------------------------------------------------------- */
1289 gboolean
1290 mc_util_make_backup_if_possible (const char *file_name, const char *backup_suffix)
1292 struct stat stat_buf;
1293 char *backup_path;
1294 gboolean ret;
1295 if (!exist_file (file_name))
1296 return FALSE;
1298 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1300 if (backup_path == NULL)
1301 return FALSE;
1303 ret = mc_util_write_backup_content (file_name, backup_path);
1305 if (ret)
1307 /* Backup file will have same ownership with main file. */
1308 if (stat (file_name, &stat_buf) == 0)
1309 chmod (backup_path, stat_buf.st_mode);
1310 else
1311 chmod (backup_path, S_IRUSR | S_IWUSR);
1314 g_free (backup_path);
1316 return ret;
1319 /* --------------------------------------------------------------------------------------------- */
1321 gboolean
1322 mc_util_restore_from_backup_if_possible (const char *file_name, const char *backup_suffix)
1324 gboolean ret;
1325 char *backup_path;
1327 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1328 if (backup_path == NULL)
1329 return FALSE;
1331 ret = mc_util_write_backup_content (backup_path, file_name);
1332 g_free (backup_path);
1334 return ret;
1337 /* --------------------------------------------------------------------------------------------- */
1339 gboolean
1340 mc_util_unlink_backup_if_possible (const char *file_name, const char *backup_suffix)
1342 char *backup_path;
1344 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1345 if (backup_path == NULL)
1346 return FALSE;
1348 if (exist_file (backup_path))
1350 vfs_path_t *vpath;
1352 vpath = vfs_path_from_str (backup_path);
1353 mc_unlink (vpath);
1354 vfs_path_free (vpath);
1357 g_free (backup_path);
1358 return TRUE;
1361 /* --------------------------------------------------------------------------------------------- */
1363 * partly taken from dcigettext.c, returns "" for default locale
1364 * value should be freed by calling function g_free()
1367 char *
1368 guess_message_value (void)
1370 static const char *const var[] = {
1371 /* Setting of LC_ALL overwrites all other. */
1372 /* Do not use LANGUAGE for check user locale and drowing hints */
1373 "LC_ALL",
1374 /* Next comes the name of the desired category. */
1375 "LC_MESSAGES",
1376 /* Last possibility is the LANG environment variable. */
1377 "LANG",
1378 /* NULL exit loops */
1379 NULL
1382 unsigned i = 0;
1383 const char *locale = NULL;
1385 while (var[i] != NULL)
1387 locale = getenv (var[i]);
1388 if (locale != NULL && locale[0] != '\0')
1389 break;
1390 i++;
1393 if (locale == NULL)
1394 locale = "";
1396 return g_strdup (locale);
1399 /* --------------------------------------------------------------------------------------------- */
1401 * Propagate error in simple way.
1403 * @param dest error return location
1404 * @param code error code
1405 * @param format printf()-style format for error message
1406 * @param ... parameters for message format
1409 void
1410 mc_propagate_error (GError ** dest, int code, const char *format, ...)
1412 if (dest != NULL && *dest == NULL)
1414 GError *tmp_error;
1415 va_list args;
1417 va_start (args, format);
1418 tmp_error = g_error_new_valist (MC_ERROR, code, format, args);
1419 va_end (args);
1421 g_propagate_error (dest, tmp_error);
1425 /* --------------------------------------------------------------------------------------------- */
1427 * Replace existing error in simple way.
1429 * @param dest error return location
1430 * @param code error code
1431 * @param format printf()-style format for error message
1432 * @param ... parameters for message format
1435 void
1436 mc_replace_error (GError ** dest, int code, const char *format, ...)
1438 if (dest != NULL)
1440 GError *tmp_error;
1441 va_list args;
1443 va_start (args, format);
1444 tmp_error = g_error_new_valist (MC_ERROR, code, format, args);
1445 va_end (args);
1447 g_error_free (*dest);
1448 *dest = NULL;
1449 g_propagate_error (dest, tmp_error);
1453 /* --------------------------------------------------------------------------------------------- */