Merge branch '2206_jump_line'
[midnight-commander.git] / lib / util.c
blob4c4aeb08343e8d6dee2f3130eaa03ec905c1f453
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 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 = vfs_path_to_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 g_free (buf);
139 buf = NULL;
140 goto ret;
142 if (!S_ISLNK (mybuf.st_mode))
143 strcpy (r, p + 1);
144 else
146 int len;
148 len = mc_readlink (vpath, buf2, MC_MAXPATHLEN - 1);
149 if (len < 0)
151 g_free (buf);
152 buf = NULL;
153 goto ret;
155 buf2[len] = 0;
156 if (*buf2 == PATH_SEP)
157 strcpy (buf, buf2);
158 else
159 strcpy (r, buf2);
161 canonicalize_pathname (buf);
162 r = strchr (buf, 0);
163 if (!*r || *(r - 1) != PATH_SEP)
164 /* FIXME: this condition is always true because r points to the EOL */
166 *r++ = PATH_SEP;
167 *r = 0;
169 *q = c;
170 p = q;
172 while (c != '\0');
174 if (!*buf)
175 strcpy (buf, PATH_SEP_STR);
176 else if (*(r - 1) == PATH_SEP && r != buf + 1)
177 *(r - 1) = 0;
179 ret:
180 g_free (buf2);
181 g_free (p2);
182 return buf;
185 /* --------------------------------------------------------------------------------------------- */
187 static gboolean
188 mc_util_write_backup_content (const char *from_file_name, const char *to_file_name)
190 FILE *backup_fd;
191 char *contents;
192 gsize length;
193 gboolean ret1 = TRUE;
195 if (!g_file_get_contents (from_file_name, &contents, &length, NULL))
196 return FALSE;
198 backup_fd = fopen (to_file_name, "w");
199 if (backup_fd == NULL)
201 g_free (contents);
202 return FALSE;
205 if (fwrite ((const void *) contents, 1, length, backup_fd) != length)
206 ret1 = FALSE;
208 int ret2;
209 ret2 = fflush (backup_fd);
210 ret2 = fclose (backup_fd);
211 (void) ret2;
213 g_free (contents);
214 return ret1;
217 /* --------------------------------------------------------------------------------------------- */
218 /*** public functions ****************************************************************************/
219 /* --------------------------------------------------------------------------------------------- */
222 is_printable (int c)
224 c &= 0xff;
226 #ifdef HAVE_CHARSET
227 /* "Display bits" is ignored, since the user controls the output
228 by setting the output codepage */
229 return is_8bit_printable (c);
230 #else
231 if (!mc_global.eight_bit_clean)
232 return is_7bit_printable (c);
234 if (mc_global.full_eight_bits)
236 return is_8bit_printable (c);
238 else
239 return is_iso_printable (c);
240 #endif /* !HAVE_CHARSET */
243 /* --------------------------------------------------------------------------------------------- */
245 * Quote the filename for the purpose of inserting it into the command
246 * line. If quote_percent is 1, replace "%" with "%%" - the percent is
247 * processed by the mc command line.
249 char *
250 name_quote (const char *s, int quote_percent)
252 char *ret, *d;
254 d = ret = g_malloc (strlen (s) * 2 + 2 + 1);
255 if (*s == '-')
257 *d++ = '.';
258 *d++ = '/';
261 for (; *s; s++, d++)
263 switch (*s)
265 case '%':
266 if (quote_percent)
267 *d++ = '%';
268 break;
269 case '\'':
270 case '\\':
271 case '\r':
272 case '\n':
273 case '\t':
274 case '"':
275 case ';':
276 case ' ':
277 case '?':
278 case '|':
279 case '[':
280 case ']':
281 case '{':
282 case '}':
283 case '<':
284 case '>':
285 case '`':
286 case '!':
287 case '$':
288 case '&':
289 case '*':
290 case '(':
291 case ')':
292 *d++ = '\\';
293 break;
294 case '~':
295 case '#':
296 if (d == ret)
297 *d++ = '\\';
298 break;
300 *d = *s;
302 *d = '\0';
303 return ret;
306 /* --------------------------------------------------------------------------------------------- */
308 char *
309 fake_name_quote (const char *s, int quote_percent)
311 (void) quote_percent;
312 return g_strdup (s);
315 /* --------------------------------------------------------------------------------------------- */
317 * path_trunc() is the same as str_trunc() but
318 * it deletes possible password from path for security
319 * reasons.
322 const char *
323 path_trunc (const char *path, size_t trunc_len)
325 vfs_path_t *vpath;
326 char *secure_path;
327 const char *ret;
329 vpath = vfs_path_from_str (path);
330 secure_path = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
331 vfs_path_free (vpath);
333 ret = str_trunc (secure_path, trunc_len);
334 g_free (secure_path);
336 return ret;
339 /* --------------------------------------------------------------------------------------------- */
341 const char *
342 size_trunc (uintmax_t size, gboolean use_si)
344 static char x[BUF_TINY];
345 uintmax_t divisor = 1;
346 const char *xtra = "";
348 if (size > 999999999UL)
350 divisor = use_si ? 1000 : 1024;
351 xtra = use_si ? "k" : "K";
353 if (size / divisor > 999999999UL)
355 divisor = use_si ? (1000 * 1000) : (1024 * 1024);
356 xtra = use_si ? "m" : "M";
358 if (size / divisor > 999999999UL)
360 divisor = use_si ? (1000 * 1000 * 1000) : (1024 * 1024 * 1024);
361 xtra = use_si ? "g" : "G";
365 g_snprintf (x, sizeof (x), "%.0f%s", 1.0 * size / divisor, xtra);
366 return x;
369 /* --------------------------------------------------------------------------------------------- */
371 const char *
372 size_trunc_sep (uintmax_t size, gboolean use_si)
374 static char x[60];
375 int count;
376 const char *p, *y;
377 char *d;
379 p = y = size_trunc (size, use_si);
380 p += strlen (p) - 1;
381 d = x + sizeof (x) - 1;
382 *d-- = '\0';
383 while (p >= y && isalpha ((unsigned char) *p))
384 *d-- = *p--;
385 for (count = 0; p >= y; count++)
387 if (count == 3)
389 *d-- = ',';
390 count = 0;
392 *d-- = *p--;
394 d++;
395 if (*d == ',')
396 d++;
397 return d;
400 /* --------------------------------------------------------------------------------------------- */
402 * Print file SIZE to BUFFER, but don't exceed LEN characters,
403 * not including trailing 0. BUFFER should be at least LEN+1 long.
404 * This function is called for every file on panels, so avoid
405 * floating point by any means.
407 * Units: size units (filesystem sizes are 1K blocks)
408 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
411 void
412 size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gboolean use_si)
414 /* Avoid taking power for every file. */
415 /* *INDENT-OFF* */
416 static const uintmax_t power10[] = {
417 /* we hope that size of uintmax_t is 4 bytes at least */
418 1ULL,
419 10ULL,
420 100ULL,
421 1000ULL,
422 10000ULL,
423 100000ULL,
424 1000000ULL,
425 10000000ULL,
426 100000000ULL,
427 1000000000ULL
428 /* maximmum value of uintmax_t (in case of 4 bytes) is
429 4294967295
431 #if SIZEOF_UINTMAX_T == 8
433 10000000000ULL,
434 100000000000ULL,
435 1000000000000ULL,
436 10000000000000ULL,
437 100000000000000ULL,
438 1000000000000000ULL,
439 10000000000000000ULL,
440 100000000000000000ULL,
441 1000000000000000000ULL,
442 10000000000000000000ULL
443 /* maximmum value of uintmax_t (in case of 8 bytes) is
444 18447644073710439615
446 #endif
448 /* *INDENT-ON* */
449 static const char *const suffix[] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL };
450 static const char *const suffix_lc[] = { "", "k", "m", "g", "t", "p", "e", "z", "y", NULL };
451 int j = 0;
453 if (len == 0)
454 len = 9;
455 #if SIZEOF_UINTMAX_T == 8
456 /* 20 decimal digits are required to represent 8 bytes */
457 else if (len > 19)
458 len = 19;
459 #else
460 /* 10 decimal digits are required to represent 4 bytes */
461 else if (len > 9)
462 len = 9;
463 #endif
466 * recalculate from 1024 base to 1000 base if units>0
467 * We can't just multiply by 1024 - that might cause overflow
468 * if off_t type is too small
470 if (use_si)
471 for (j = 0; j < units; j++)
473 uintmax_t size_remain;
475 size_remain = ((size % 125) * 1024) / 1000; /* size mod 125, recalculated */
476 size = size / 125; /* 128/125 = 1024/1000 */
477 size = size * 128; /* This will convert size from multiple of 1024 to multiple of 1000 */
478 size += size_remain; /* Re-add remainder lost by division/multiplication */
481 for (j = units; suffix[j] != NULL; j++)
483 if (size == 0)
485 if (j == units)
487 /* Empty files will print "0" even with minimal width. */
488 g_snprintf (buffer, len + 1, "0");
489 break;
492 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
493 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s",
494 (j > 1) ? (use_si ? suffix_lc[j - 1] : suffix[j - 1]) : "B");
495 break;
498 if (size < power10[len - (j > 0 ? 1 : 0)])
500 g_snprintf (buffer, len + 1, "%" PRIuMAX "%s", size, use_si ? suffix_lc[j] : suffix[j]);
501 break;
504 /* Powers of 1000 or 1024, with rounding. */
505 if (use_si)
506 size = (size + 500) / 1000;
507 else
508 size = (size + 512) >> 10;
512 /* --------------------------------------------------------------------------------------------- */
514 const char *
515 string_perm (mode_t mode_bits)
517 static char mode[11];
519 strcpy (mode, "----------");
520 if (S_ISDIR (mode_bits))
521 mode[0] = 'd';
522 if (S_ISCHR (mode_bits))
523 mode[0] = 'c';
524 if (S_ISBLK (mode_bits))
525 mode[0] = 'b';
526 if (S_ISLNK (mode_bits))
527 mode[0] = 'l';
528 if (S_ISFIFO (mode_bits))
529 mode[0] = 'p';
530 if (S_ISNAM (mode_bits))
531 mode[0] = 'n';
532 if (S_ISSOCK (mode_bits))
533 mode[0] = 's';
534 if (S_ISDOOR (mode_bits))
535 mode[0] = 'D';
536 if (ismode (mode_bits, S_IXOTH))
537 mode[9] = 'x';
538 if (ismode (mode_bits, S_IWOTH))
539 mode[8] = 'w';
540 if (ismode (mode_bits, S_IROTH))
541 mode[7] = 'r';
542 if (ismode (mode_bits, S_IXGRP))
543 mode[6] = 'x';
544 if (ismode (mode_bits, S_IWGRP))
545 mode[5] = 'w';
546 if (ismode (mode_bits, S_IRGRP))
547 mode[4] = 'r';
548 if (ismode (mode_bits, S_IXUSR))
549 mode[3] = 'x';
550 if (ismode (mode_bits, S_IWUSR))
551 mode[2] = 'w';
552 if (ismode (mode_bits, S_IRUSR))
553 mode[1] = 'r';
554 #ifdef S_ISUID
555 if (ismode (mode_bits, S_ISUID))
556 mode[3] = (mode[3] == 'x') ? 's' : 'S';
557 #endif /* S_ISUID */
558 #ifdef S_ISGID
559 if (ismode (mode_bits, S_ISGID))
560 mode[6] = (mode[6] == 'x') ? 's' : 'S';
561 #endif /* S_ISGID */
562 #ifdef S_ISVTX
563 if (ismode (mode_bits, S_ISVTX))
564 mode[9] = (mode[9] == 'x') ? 't' : 'T';
565 #endif /* S_ISVTX */
566 return mode;
569 /* --------------------------------------------------------------------------------------------- */
571 const char *
572 extension (const char *filename)
574 const char *d = strrchr (filename, '.');
575 return (d != NULL) ? d + 1 : "";
578 /* --------------------------------------------------------------------------------------------- */
580 char *
581 load_mc_home_file (const char *from, const char *filename, char **allocated_filename)
583 char *hintfile_base, *hintfile;
584 char *lang;
585 char *data;
587 hintfile_base = g_build_filename (from, filename, (char *) NULL);
588 lang = guess_message_value ();
590 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
591 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
593 /* Fall back to the two-letter language code */
594 if (lang[0] != '\0' && lang[1] != '\0')
595 lang[2] = '\0';
596 g_free (hintfile);
597 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
598 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
600 g_free (hintfile);
601 hintfile = hintfile_base;
602 g_file_get_contents (hintfile_base, &data, NULL, NULL);
606 g_free (lang);
608 if (hintfile != hintfile_base)
609 g_free (hintfile_base);
611 if (allocated_filename != NULL)
612 *allocated_filename = hintfile;
613 else
614 g_free (hintfile);
616 return data;
619 /* --------------------------------------------------------------------------------------------- */
621 const char *
622 extract_line (const char *s, const char *top)
624 static char tmp_line[BUF_MEDIUM];
625 char *t = tmp_line;
627 while (*s && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line) - 1 && s < top)
628 *t++ = *s++;
629 *t = 0;
630 return tmp_line;
633 /* --------------------------------------------------------------------------------------------- */
635 * The basename routine
638 const char *
639 x_basename (const char *s)
641 const char *url_delim, *path_sep;
643 url_delim = g_strrstr (s, VFS_PATH_URL_DELIMITER);
644 path_sep = strrchr (s, PATH_SEP);
646 if (url_delim == NULL
647 || url_delim < path_sep - strlen (VFS_PATH_URL_DELIMITER)
648 || url_delim - s + strlen (VFS_PATH_URL_DELIMITER) < strlen (s))
650 /* avoid trailing PATH_SEP, if present */
651 if (s[strlen (s) - 1] == PATH_SEP)
653 while (--path_sep > s && *path_sep != PATH_SEP);
654 return (path_sep != s) ? path_sep + 1 : s;
656 else
657 return (path_sep != NULL) ? path_sep + 1 : s;
660 while (--url_delim > s && *url_delim != PATH_SEP);
661 while (--url_delim > s && *url_delim != PATH_SEP);
663 return (url_delim == s) ? s : url_delim + 1;
666 /* --------------------------------------------------------------------------------------------- */
668 const char *
669 unix_error_string (int error_num)
671 static char buffer[BUF_LARGE];
672 gchar *strerror_currentlocale;
674 strerror_currentlocale = g_locale_from_utf8 (g_strerror (error_num), -1, NULL, NULL, NULL);
675 g_snprintf (buffer, sizeof (buffer), "%s (%d)", strerror_currentlocale, error_num);
676 g_free (strerror_currentlocale);
678 return buffer;
681 /* --------------------------------------------------------------------------------------------- */
683 const char *
684 skip_separators (const char *s)
686 const char *su = s;
688 for (; *su; str_cnext_char (&su))
689 if (*su != ' ' && *su != '\t' && *su != ',')
690 break;
692 return su;
695 /* --------------------------------------------------------------------------------------------- */
697 const char *
698 skip_numbers (const char *s)
700 const char *su = s;
702 for (; *su; str_cnext_char (&su))
703 if (!str_isdigit (su))
704 break;
706 return su;
709 /* --------------------------------------------------------------------------------------------- */
711 * Remove all control sequences from the argument string. We define
712 * "control sequence", in a sort of pidgin BNF, as follows:
714 * control-seq = Esc non-'['
715 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
717 * This scheme works for all the terminals described in my termcap /
718 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
719 * terminals. If I hear from a single person who uses such a terminal
720 * with MC, I'll be glad to add support for it. (Dugan)
721 * Non-printable characters are also removed.
724 char *
725 strip_ctrl_codes (char *s)
727 char *w; /* Current position where the stripped data is written */
728 char *r; /* Current position where the original data is read */
729 char *n;
731 if (!s)
732 return 0;
734 for (w = s, r = s; *r;)
736 if (*r == ESC_CHAR)
738 /* Skip the control sequence's arguments */ ;
739 /* '(' need to avoid strange 'B' letter in *Suse (if mc runs under root user) */
740 if (*(++r) == '[' || *r == '(')
742 /* strchr() matches trailing binary 0 */
743 while (*(++r) && strchr ("0123456789;?", *r));
745 else if (*r == ']')
748 * Skip xterm's OSC (Operating System Command)
749 * http://www.xfree86.org/current/ctlseqs.html
750 * OSC P s ; P t ST
751 * OSC P s ; P t BEL
753 char *new_r = r;
755 for (; *new_r; ++new_r)
757 switch (*new_r)
759 /* BEL */
760 case '\a':
761 r = new_r;
762 goto osc_out;
763 case ESC_CHAR:
764 /* ST */
765 if (*(new_r + 1) == '\\')
767 r = new_r + 1;
768 goto osc_out;
772 osc_out:;
776 * Now we are at the last character of the sequence.
777 * Skip it unless it's binary 0.
779 if (*r)
780 r++;
781 continue;
784 n = str_get_next_char (r);
785 if (str_isprint (r))
787 memmove (w, r, n - r);
788 w += n - r;
790 r = n;
792 *w = 0;
793 return s;
796 /* --------------------------------------------------------------------------------------------- */
798 enum compression_type
799 get_compression_type (int fd, const char *name)
801 unsigned char magic[16];
802 size_t str_len;
804 /* Read the magic signature */
805 if (mc_read (fd, (char *) magic, 4) != 4)
806 return COMPRESSION_NONE;
808 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
809 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236))
811 return COMPRESSION_GZIP;
814 /* PKZIP_MAGIC */
815 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003 && magic[3] == 004)
817 /* Read compression type */
818 mc_lseek (fd, 8, SEEK_SET);
819 if (mc_read (fd, (char *) magic, 2) != 2)
820 return COMPRESSION_NONE;
822 /* Gzip can handle only deflated (8) or stored (0) files */
823 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
824 return COMPRESSION_NONE;
826 /* Compatible with gzip */
827 return COMPRESSION_GZIP;
830 /* PACK_MAGIC and LZH_MAGIC and compress magic */
831 if (magic[0] == 037 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235))
833 /* Compatible with gzip */
834 return COMPRESSION_GZIP;
837 /* BZIP and BZIP2 files */
838 if ((magic[0] == 'B') && (magic[1] == 'Z') && (magic[3] >= '1') && (magic[3] <= '9'))
840 switch (magic[2])
842 case '0':
843 return COMPRESSION_BZIP;
844 case 'h':
845 return COMPRESSION_BZIP2;
849 /* Support for LZMA (only utils format with magic in header).
850 * This is the default format of LZMA utils 4.32.1 and later. */
852 if (mc_read (fd, (char *) magic + 4, 2) != 2)
853 return COMPRESSION_NONE;
855 /* LZMA utils format */
856 if (magic[0] == 0xFF
857 && magic[1] == 'L'
858 && magic[2] == 'Z' && magic[3] == 'M' && magic[4] == 'A' && magic[5] == 0x00)
859 return COMPRESSION_LZMA;
861 /* XZ compression magic */
862 if (magic[0] == 0xFD
863 && magic[1] == 0x37
864 && magic[2] == 0x7A && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00)
865 return COMPRESSION_XZ;
867 str_len = strlen (name);
868 /* HACK: we must belive to extention of LZMA file :) ... */
869 if ((str_len > 5 && strcmp (&name[str_len - 5], ".lzma") == 0) ||
870 (str_len > 4 && strcmp (&name[str_len - 4], ".tlz") == 0))
871 return COMPRESSION_LZMA;
873 return COMPRESSION_NONE;
876 /* --------------------------------------------------------------------------------------------- */
878 const char *
879 decompress_extension (int type)
881 switch (type)
883 case COMPRESSION_GZIP:
884 return "/ugz" VFS_PATH_URL_DELIMITER;
885 case COMPRESSION_BZIP:
886 return "/ubz" VFS_PATH_URL_DELIMITER;
887 case COMPRESSION_BZIP2:
888 return "/ubz2" VFS_PATH_URL_DELIMITER;
889 case COMPRESSION_LZMA:
890 return "/ulzma" VFS_PATH_URL_DELIMITER;
891 case COMPRESSION_XZ:
892 return "/uxz" VFS_PATH_URL_DELIMITER;
894 /* Should never reach this place */
895 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
896 return 0;
899 /* --------------------------------------------------------------------------------------------- */
901 void
902 wipe_password (char *passwd)
904 char *p = passwd;
906 if (!p)
907 return;
908 for (; *p; p++)
909 *p = 0;
910 g_free (passwd);
913 /* --------------------------------------------------------------------------------------------- */
915 * Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key
917 * @param p pointer to string
919 * @return newly allocated string
922 char *
923 convert_controls (const char *p)
925 char *valcopy = g_strdup (p);
926 char *q;
928 /* Parse the escape special character */
929 for (q = valcopy; *p;)
931 if (*p == '\\')
933 p++;
934 if ((*p == 'e') || (*p == 'E'))
936 p++;
937 *q++ = ESC_CHAR;
940 else
942 if (*p == '^')
944 p++;
945 if (*p == '^')
946 *q++ = *p++;
947 else
949 char c = (*p | 0x20);
950 if (c >= 'a' && c <= 'z')
952 *q++ = c - 'a' + 1;
953 p++;
955 else if (*p)
956 p++;
959 else
960 *q++ = *p++;
963 *q = 0;
964 return valcopy;
967 /* --------------------------------------------------------------------------------------------- */
969 * Finds out a relative path from first to second, i.e. goes as many ..
970 * as needed up in first and then goes down using second
973 char *
974 diff_two_paths (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
976 char *p, *q, *r, *s, *buf = NULL;
977 int i, j, prevlen = -1, currlen;
978 char *my_first = NULL, *my_second = NULL;
980 my_first = resolve_symlinks (vpath1);
981 if (my_first == NULL)
982 goto ret;
984 my_second = resolve_symlinks (vpath2);
985 if (my_second == NULL)
986 goto ret;
988 for (j = 0; j < 2; j++)
990 p = my_first;
991 q = my_second;
992 while (TRUE)
994 r = strchr (p, PATH_SEP);
995 s = strchr (q, PATH_SEP);
996 if (r == NULL || s == NULL)
997 break;
998 *r = '\0';
999 *s = '\0';
1000 if (strcmp (p, q) != 0)
1002 *r = PATH_SEP;
1003 *s = PATH_SEP;
1004 break;
1007 *r = PATH_SEP;
1008 *s = PATH_SEP;
1010 p = r + 1;
1011 q = s + 1;
1013 p--;
1014 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++)
1016 currlen = (i + 1) * 3 + strlen (q) + 1;
1017 if (j != 0)
1019 if (currlen < prevlen)
1020 g_free (buf);
1021 else
1022 goto ret;
1024 p = buf = g_malloc (currlen);
1025 prevlen = currlen;
1026 for (; i >= 0; i--, p += 3)
1027 strcpy (p, "../");
1028 strcpy (p, q);
1031 ret:
1032 g_free (my_first);
1033 g_free (my_second);
1034 return buf;
1037 /* --------------------------------------------------------------------------------------------- */
1039 * Append text to GList, remove all entries with the same text
1042 GList *
1043 list_append_unique (GList * list, char *text)
1045 GList *lc_link;
1048 * Go to the last position and traverse the list backwards
1049 * starting from the second last entry to make sure that we
1050 * are not removing the current link.
1052 list = g_list_append (list, text);
1053 list = g_list_last (list);
1054 lc_link = g_list_previous (list);
1056 while (lc_link != NULL)
1058 GList *newlink;
1060 newlink = g_list_previous (lc_link);
1061 if (strcmp ((char *) lc_link->data, text) == 0)
1063 GList *tmp;
1065 g_free (lc_link->data);
1066 tmp = g_list_remove_link (list, lc_link);
1067 (void) tmp;
1068 g_list_free_1 (lc_link);
1070 lc_link = newlink;
1073 return list;
1076 /* --------------------------------------------------------------------------------------------- */
1078 * Read and restore position for the given filename.
1079 * If there is no stored data, return line 1 and col 0.
1082 void
1083 load_file_position (const vfs_path_t * filename_vpath, long *line, long *column, off_t * offset,
1084 GArray ** bookmarks)
1086 char *fn;
1087 FILE *f;
1088 char buf[MC_MAXPATHLEN + 100];
1089 const size_t len = vfs_path_len (filename_vpath);
1090 char *filename;
1092 /* defaults */
1093 *line = 1;
1094 *column = 0;
1095 *offset = 0;
1097 /* open file with positions */
1098 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1099 f = fopen (fn, "r");
1100 g_free (fn);
1101 if (f == NULL)
1102 return;
1104 /* prepare array for serialized bookmarks */
1105 if (bookmarks != NULL)
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 if (bookmarks != NULL)
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 /* --------------------------------------------------------------------------------------------- */