0a2e7cde008f292d554ee9e76d54a80a6b0f4a15
[midnight-commander.git] / lib / util.c
blob0a2e7cde008f292d554ee9e76d54a80a6b0f4a15
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);
212 g_free (contents);
213 return ret1;
216 /* --------------------------------------------------------------------------------------------- */
217 /*** public functions ****************************************************************************/
218 /* --------------------------------------------------------------------------------------------- */
221 is_printable (int c)
223 c &= 0xff;
225 #ifdef HAVE_CHARSET
226 /* "Display bits" is ignored, since the user controls the output
227 by setting the output codepage */
228 return is_8bit_printable (c);
229 #else
230 if (!mc_global.eight_bit_clean)
231 return is_7bit_printable (c);
233 if (mc_global.full_eight_bits)
235 return is_8bit_printable (c);
237 else
238 return is_iso_printable (c);
239 #endif /* !HAVE_CHARSET */
242 /* --------------------------------------------------------------------------------------------- */
244 * Quote the filename for the purpose of inserting it into the command
245 * line. If quote_percent is 1, replace "%" with "%%" - the percent is
246 * processed by the mc command line.
248 char *
249 name_quote (const char *s, int quote_percent)
251 char *ret, *d;
253 d = ret = g_malloc (strlen (s) * 2 + 2 + 1);
254 if (*s == '-')
256 *d++ = '.';
257 *d++ = '/';
260 for (; *s; s++, d++)
262 switch (*s)
264 case '%':
265 if (quote_percent)
266 *d++ = '%';
267 break;
268 case '\'':
269 case '\\':
270 case '\r':
271 case '\n':
272 case '\t':
273 case '"':
274 case ';':
275 case ' ':
276 case '?':
277 case '|':
278 case '[':
279 case ']':
280 case '{':
281 case '}':
282 case '<':
283 case '>':
284 case '`':
285 case '!':
286 case '$':
287 case '&':
288 case '*':
289 case '(':
290 case ')':
291 *d++ = '\\';
292 break;
293 case '~':
294 case '#':
295 if (d == ret)
296 *d++ = '\\';
297 break;
299 *d = *s;
301 *d = '\0';
302 return ret;
305 /* --------------------------------------------------------------------------------------------- */
307 char *
308 fake_name_quote (const char *s, int quote_percent)
310 (void) quote_percent;
311 return g_strdup (s);
314 /* --------------------------------------------------------------------------------------------- */
316 * path_trunc() is the same as str_trunc() but
317 * it deletes possible password from path for security
318 * reasons.
321 const char *
322 path_trunc (const char *path, size_t trunc_len)
324 vfs_path_t *vpath;
325 char *secure_path;
326 const char *ret;
328 vpath = vfs_path_from_str (path);
329 secure_path = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
330 vfs_path_free (vpath);
332 ret = str_trunc (secure_path, trunc_len);
333 g_free (secure_path);
335 return ret;
338 /* --------------------------------------------------------------------------------------------- */
340 const char *
341 size_trunc (uintmax_t size, gboolean use_si)
343 static char x[BUF_TINY];
344 uintmax_t divisor = 1;
345 const char *xtra = "";
347 if (size > 999999999UL)
349 divisor = use_si ? 1000 : 1024;
350 xtra = use_si ? "k" : "K";
352 if (size / divisor > 999999999UL)
354 divisor = use_si ? (1000 * 1000) : (1024 * 1024);
355 xtra = use_si ? "m" : "M";
357 if (size / divisor > 999999999UL)
359 divisor = use_si ? (1000 * 1000 * 1000) : (1024 * 1024 * 1024);
360 xtra = use_si ? "g" : "G";
364 g_snprintf (x, sizeof (x), "%.0f%s", 1.0 * size / divisor, xtra);
365 return x;
368 /* --------------------------------------------------------------------------------------------- */
370 const char *
371 size_trunc_sep (uintmax_t size, gboolean use_si)
373 static char x[60];
374 int count;
375 const char *p, *y;
376 char *d;
378 p = y = size_trunc (size, use_si);
379 p += strlen (p) - 1;
380 d = x + sizeof (x) - 1;
381 *d-- = '\0';
382 while (p >= y && isalpha ((unsigned char) *p))
383 *d-- = *p--;
384 for (count = 0; p >= y; count++)
386 if (count == 3)
388 *d-- = ',';
389 count = 0;
391 *d-- = *p--;
393 d++;
394 if (*d == ',')
395 d++;
396 return d;
399 /* --------------------------------------------------------------------------------------------- */
401 * Print file SIZE to BUFFER, but don't exceed LEN characters,
402 * not including trailing 0. BUFFER should be at least LEN+1 long.
403 * This function is called for every file on panels, so avoid
404 * floating point by any means.
406 * Units: size units (filesystem sizes are 1K blocks)
407 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
410 void
411 size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gboolean use_si)
413 /* Avoid taking power for every file. */
414 /* *INDENT-OFF* */
415 static const uintmax_t power10[] = {
416 /* we hope that size of uintmax_t is 4 bytes at least */
417 1ULL,
418 10ULL,
419 100ULL,
420 1000ULL,
421 10000ULL,
422 100000ULL,
423 1000000ULL,
424 10000000ULL,
425 100000000ULL,
426 1000000000ULL
427 /* maximmum value of uintmax_t (in case of 4 bytes) is
428 4294967295
430 #if SIZEOF_UINTMAX_T == 8
432 10000000000ULL,
433 100000000000ULL,
434 1000000000000ULL,
435 10000000000000ULL,
436 100000000000000ULL,
437 1000000000000000ULL,
438 10000000000000000ULL,
439 100000000000000000ULL,
440 1000000000000000000ULL,
441 10000000000000000000ULL
442 /* maximmum value of uintmax_t (in case of 8 bytes) is
443 18447644073710439615
445 #endif
447 /* *INDENT-ON* */
448 static const char *const suffix[] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL };
449 static const char *const suffix_lc[] = { "", "k", "m", "g", "t", "p", "e", "z", "y", NULL };
450 int j = 0;
452 if (len == 0)
453 len = 9;
454 #if SIZEOF_UINTMAX_T == 8
455 /* 20 decimal digits are required to represent 8 bytes */
456 else if (len > 19)
457 len = 19;
458 #else
459 /* 10 decimal digits are required to represent 4 bytes */
460 else if (len > 9)
461 len = 9;
462 #endif
465 * recalculate from 1024 base to 1000 base if units>0
466 * We can't just multiply by 1024 - that might cause overflow
467 * if off_t type is too small
469 if (use_si)
470 for (j = 0; j < units; j++)
472 uintmax_t size_remain;
474 size_remain = ((size % 125) * 1024) / 1000; /* size mod 125, recalculated */
475 size = size / 125; /* 128/125 = 1024/1000 */
476 size = size * 128; /* This will convert size from multiple of 1024 to multiple of 1000 */
477 size += size_remain; /* Re-add remainder lost by division/multiplication */
480 for (j = units; suffix[j] != NULL; j++)
482 if (size == 0)
484 if (j == units)
486 /* Empty files will print "0" even with minimal width. */
487 g_snprintf (buffer, len + 1, "0");
488 break;
491 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
492 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s",
493 (j > 1) ? (use_si ? suffix_lc[j - 1] : suffix[j - 1]) : "B");
494 break;
497 if (size < power10[len - (j > 0 ? 1 : 0)])
499 g_snprintf (buffer, len + 1, "%" PRIuMAX "%s", size, use_si ? suffix_lc[j] : suffix[j]);
500 break;
503 /* Powers of 1000 or 1024, with rounding. */
504 if (use_si)
505 size = (size + 500) / 1000;
506 else
507 size = (size + 512) >> 10;
511 /* --------------------------------------------------------------------------------------------- */
513 const char *
514 string_perm (mode_t mode_bits)
516 static char mode[11];
518 strcpy (mode, "----------");
519 if (S_ISDIR (mode_bits))
520 mode[0] = 'd';
521 if (S_ISCHR (mode_bits))
522 mode[0] = 'c';
523 if (S_ISBLK (mode_bits))
524 mode[0] = 'b';
525 if (S_ISLNK (mode_bits))
526 mode[0] = 'l';
527 if (S_ISFIFO (mode_bits))
528 mode[0] = 'p';
529 if (S_ISNAM (mode_bits))
530 mode[0] = 'n';
531 if (S_ISSOCK (mode_bits))
532 mode[0] = 's';
533 if (S_ISDOOR (mode_bits))
534 mode[0] = 'D';
535 if (ismode (mode_bits, S_IXOTH))
536 mode[9] = 'x';
537 if (ismode (mode_bits, S_IWOTH))
538 mode[8] = 'w';
539 if (ismode (mode_bits, S_IROTH))
540 mode[7] = 'r';
541 if (ismode (mode_bits, S_IXGRP))
542 mode[6] = 'x';
543 if (ismode (mode_bits, S_IWGRP))
544 mode[5] = 'w';
545 if (ismode (mode_bits, S_IRGRP))
546 mode[4] = 'r';
547 if (ismode (mode_bits, S_IXUSR))
548 mode[3] = 'x';
549 if (ismode (mode_bits, S_IWUSR))
550 mode[2] = 'w';
551 if (ismode (mode_bits, S_IRUSR))
552 mode[1] = 'r';
553 #ifdef S_ISUID
554 if (ismode (mode_bits, S_ISUID))
555 mode[3] = (mode[3] == 'x') ? 's' : 'S';
556 #endif /* S_ISUID */
557 #ifdef S_ISGID
558 if (ismode (mode_bits, S_ISGID))
559 mode[6] = (mode[6] == 'x') ? 's' : 'S';
560 #endif /* S_ISGID */
561 #ifdef S_ISVTX
562 if (ismode (mode_bits, S_ISVTX))
563 mode[9] = (mode[9] == 'x') ? 't' : 'T';
564 #endif /* S_ISVTX */
565 return mode;
568 /* --------------------------------------------------------------------------------------------- */
570 const char *
571 extension (const char *filename)
573 const char *d = strrchr (filename, '.');
574 return (d != NULL) ? d + 1 : "";
577 /* --------------------------------------------------------------------------------------------- */
579 char *
580 load_mc_home_file (const char *from, const char *filename, char **allocated_filename)
582 char *hintfile_base, *hintfile;
583 char *lang;
584 char *data;
586 hintfile_base = g_build_filename (from, filename, (char *) NULL);
587 lang = guess_message_value ();
589 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
590 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
592 /* Fall back to the two-letter language code */
593 if (lang[0] != '\0' && lang[1] != '\0')
594 lang[2] = '\0';
595 g_free (hintfile);
596 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
597 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
599 g_free (hintfile);
600 hintfile = hintfile_base;
601 g_file_get_contents (hintfile_base, &data, NULL, NULL);
605 g_free (lang);
607 if (hintfile != hintfile_base)
608 g_free (hintfile_base);
610 if (allocated_filename != NULL)
611 *allocated_filename = hintfile;
612 else
613 g_free (hintfile);
615 return data;
618 /* --------------------------------------------------------------------------------------------- */
620 const char *
621 extract_line (const char *s, const char *top)
623 static char tmp_line[BUF_MEDIUM];
624 char *t = tmp_line;
626 while (*s && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line) - 1 && s < top)
627 *t++ = *s++;
628 *t = 0;
629 return tmp_line;
632 /* --------------------------------------------------------------------------------------------- */
634 * The basename routine
637 const char *
638 x_basename (const char *s)
640 const char *url_delim, *path_sep;
642 url_delim = g_strrstr (s, VFS_PATH_URL_DELIMITER);
643 path_sep = strrchr (s, PATH_SEP);
645 if (url_delim == NULL
646 || url_delim < path_sep - strlen (VFS_PATH_URL_DELIMITER)
647 || url_delim - s + strlen (VFS_PATH_URL_DELIMITER) < strlen (s))
649 /* avoid trailing PATH_SEP, if present */
650 if (s[strlen (s) - 1] == PATH_SEP)
652 while (--path_sep > s && *path_sep != PATH_SEP);
653 return (path_sep != s) ? path_sep + 1 : s;
655 else
656 return (path_sep != NULL) ? path_sep + 1 : s;
659 while (--url_delim > s && *url_delim != PATH_SEP);
660 while (--url_delim > s && *url_delim != PATH_SEP);
662 return (url_delim == s) ? s : url_delim + 1;
665 /* --------------------------------------------------------------------------------------------- */
667 const char *
668 unix_error_string (int error_num)
670 static char buffer[BUF_LARGE];
671 gchar *strerror_currentlocale;
673 strerror_currentlocale = g_locale_from_utf8 (g_strerror (error_num), -1, NULL, NULL, NULL);
674 g_snprintf (buffer, sizeof (buffer), "%s (%d)", strerror_currentlocale, error_num);
675 g_free (strerror_currentlocale);
677 return buffer;
680 /* --------------------------------------------------------------------------------------------- */
682 const char *
683 skip_separators (const char *s)
685 const char *su = s;
687 for (; *su; str_cnext_char (&su))
688 if (*su != ' ' && *su != '\t' && *su != ',')
689 break;
691 return su;
694 /* --------------------------------------------------------------------------------------------- */
696 const char *
697 skip_numbers (const char *s)
699 const char *su = s;
701 for (; *su; str_cnext_char (&su))
702 if (!str_isdigit (su))
703 break;
705 return su;
708 /* --------------------------------------------------------------------------------------------- */
710 * Remove all control sequences from the argument string. We define
711 * "control sequence", in a sort of pidgin BNF, as follows:
713 * control-seq = Esc non-'['
714 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
716 * This scheme works for all the terminals described in my termcap /
717 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
718 * terminals. If I hear from a single person who uses such a terminal
719 * with MC, I'll be glad to add support for it. (Dugan)
720 * Non-printable characters are also removed.
723 char *
724 strip_ctrl_codes (char *s)
726 char *w; /* Current position where the stripped data is written */
727 char *r; /* Current position where the original data is read */
728 char *n;
730 if (!s)
731 return 0;
733 for (w = s, r = s; *r;)
735 if (*r == ESC_CHAR)
737 /* Skip the control sequence's arguments */ ;
738 /* '(' need to avoid strange 'B' letter in *Suse (if mc runs under root user) */
739 if (*(++r) == '[' || *r == '(')
741 /* strchr() matches trailing binary 0 */
742 while (*(++r) && strchr ("0123456789;?", *r));
744 else if (*r == ']')
747 * Skip xterm's OSC (Operating System Command)
748 * http://www.xfree86.org/current/ctlseqs.html
749 * OSC P s ; P t ST
750 * OSC P s ; P t BEL
752 char *new_r = r;
754 for (; *new_r; ++new_r)
756 switch (*new_r)
758 /* BEL */
759 case '\a':
760 r = new_r;
761 goto osc_out;
762 case ESC_CHAR:
763 /* ST */
764 if (*(new_r + 1) == '\\')
766 r = new_r + 1;
767 goto osc_out;
771 osc_out:;
775 * Now we are at the last character of the sequence.
776 * Skip it unless it's binary 0.
778 if (*r)
779 r++;
780 continue;
783 n = str_get_next_char (r);
784 if (str_isprint (r))
786 memmove (w, r, n - r);
787 w += n - r;
789 r = n;
791 *w = 0;
792 return s;
795 /* --------------------------------------------------------------------------------------------- */
797 enum compression_type
798 get_compression_type (int fd, const char *name)
800 unsigned char magic[16];
801 size_t str_len;
803 /* Read the magic signature */
804 if (mc_read (fd, (char *) magic, 4) != 4)
805 return COMPRESSION_NONE;
807 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
808 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236))
810 return COMPRESSION_GZIP;
813 /* PKZIP_MAGIC */
814 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003 && magic[3] == 004)
816 /* Read compression type */
817 mc_lseek (fd, 8, SEEK_SET);
818 if (mc_read (fd, (char *) magic, 2) != 2)
819 return COMPRESSION_NONE;
821 /* Gzip can handle only deflated (8) or stored (0) files */
822 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
823 return COMPRESSION_NONE;
825 /* Compatible with gzip */
826 return COMPRESSION_GZIP;
829 /* PACK_MAGIC and LZH_MAGIC and compress magic */
830 if (magic[0] == 037 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235))
832 /* Compatible with gzip */
833 return COMPRESSION_GZIP;
836 /* BZIP and BZIP2 files */
837 if ((magic[0] == 'B') && (magic[1] == 'Z') && (magic[3] >= '1') && (magic[3] <= '9'))
839 switch (magic[2])
841 case '0':
842 return COMPRESSION_BZIP;
843 case 'h':
844 return COMPRESSION_BZIP2;
848 /* Support for LZMA (only utils format with magic in header).
849 * This is the default format of LZMA utils 4.32.1 and later. */
851 if (mc_read (fd, (char *) magic + 4, 2) != 2)
852 return COMPRESSION_NONE;
854 /* LZMA utils format */
855 if (magic[0] == 0xFF
856 && magic[1] == 'L'
857 && magic[2] == 'Z' && magic[3] == 'M' && magic[4] == 'A' && magic[5] == 0x00)
858 return COMPRESSION_LZMA;
860 /* XZ compression magic */
861 if (magic[0] == 0xFD
862 && magic[1] == 0x37
863 && magic[2] == 0x7A && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00)
864 return COMPRESSION_XZ;
866 str_len = strlen (name);
867 /* HACK: we must belive to extention of LZMA file :) ... */
868 if ((str_len > 5 && strcmp (&name[str_len - 5], ".lzma") == 0) ||
869 (str_len > 4 && strcmp (&name[str_len - 4], ".tlz") == 0))
870 return COMPRESSION_LZMA;
872 return COMPRESSION_NONE;
875 /* --------------------------------------------------------------------------------------------- */
877 const char *
878 decompress_extension (int type)
880 switch (type)
882 case COMPRESSION_GZIP:
883 return "/ugz" VFS_PATH_URL_DELIMITER;
884 case COMPRESSION_BZIP:
885 return "/ubz" VFS_PATH_URL_DELIMITER;
886 case COMPRESSION_BZIP2:
887 return "/ubz2" VFS_PATH_URL_DELIMITER;
888 case COMPRESSION_LZMA:
889 return "/ulzma" VFS_PATH_URL_DELIMITER;
890 case COMPRESSION_XZ:
891 return "/uxz" VFS_PATH_URL_DELIMITER;
893 /* Should never reach this place */
894 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
895 return 0;
898 /* --------------------------------------------------------------------------------------------- */
900 void
901 wipe_password (char *passwd)
903 char *p = passwd;
905 if (!p)
906 return;
907 for (; *p; p++)
908 *p = 0;
909 g_free (passwd);
912 /* --------------------------------------------------------------------------------------------- */
914 * Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key
916 * @param p pointer to string
918 * @return newly allocated string
921 char *
922 convert_controls (const char *p)
924 char *valcopy = g_strdup (p);
925 char *q;
927 /* Parse the escape special character */
928 for (q = valcopy; *p;)
930 if (*p == '\\')
932 p++;
933 if ((*p == 'e') || (*p == 'E'))
935 p++;
936 *q++ = ESC_CHAR;
939 else
941 if (*p == '^')
943 p++;
944 if (*p == '^')
945 *q++ = *p++;
946 else
948 char c = (*p | 0x20);
949 if (c >= 'a' && c <= 'z')
951 *q++ = c - 'a' + 1;
952 p++;
954 else if (*p)
955 p++;
958 else
959 *q++ = *p++;
962 *q = 0;
963 return valcopy;
966 /* --------------------------------------------------------------------------------------------- */
968 * Finds out a relative path from first to second, i.e. goes as many ..
969 * as needed up in first and then goes down using second
972 char *
973 diff_two_paths (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
975 char *p, *q, *r, *s, *buf = NULL;
976 int i, j, prevlen = -1, currlen;
977 char *my_first = NULL, *my_second = NULL;
979 my_first = resolve_symlinks (vpath1);
980 if (my_first == NULL)
981 goto ret;
983 my_second = resolve_symlinks (vpath2);
984 if (my_second == NULL)
985 goto ret;
987 for (j = 0; j < 2; j++)
989 p = my_first;
990 q = my_second;
991 while (TRUE)
993 r = strchr (p, PATH_SEP);
994 s = strchr (q, PATH_SEP);
995 if (r == NULL || s == NULL)
996 break;
997 *r = '\0';
998 *s = '\0';
999 if (strcmp (p, q) != 0)
1001 *r = PATH_SEP;
1002 *s = PATH_SEP;
1003 break;
1006 *r = PATH_SEP;
1007 *s = PATH_SEP;
1009 p = r + 1;
1010 q = s + 1;
1012 p--;
1013 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++)
1015 currlen = (i + 1) * 3 + strlen (q) + 1;
1016 if (j != 0)
1018 if (currlen < prevlen)
1019 g_free (buf);
1020 else
1021 goto ret;
1023 p = buf = g_malloc (currlen);
1024 prevlen = currlen;
1025 for (; i >= 0; i--, p += 3)
1026 strcpy (p, "../");
1027 strcpy (p, q);
1030 ret:
1031 g_free (my_first);
1032 g_free (my_second);
1033 return buf;
1036 /* --------------------------------------------------------------------------------------------- */
1038 * Append text to GList, remove all entries with the same text
1041 GList *
1042 list_append_unique (GList * list, char *text)
1044 GList *lc_link;
1047 * Go to the last position and traverse the list backwards
1048 * starting from the second last entry to make sure that we
1049 * are not removing the current link.
1051 list = g_list_append (list, text);
1052 list = g_list_last (list);
1053 lc_link = g_list_previous (list);
1055 while (lc_link != NULL)
1057 GList *newlink;
1059 newlink = g_list_previous (lc_link);
1060 if (strcmp ((char *) lc_link->data, text) == 0)
1062 GList *tmp;
1064 g_free (lc_link->data);
1065 tmp = g_list_remove_link (list, lc_link);
1066 g_list_free_1 (lc_link);
1068 lc_link = newlink;
1071 return list;
1074 /* --------------------------------------------------------------------------------------------- */
1076 * Read and restore position for the given filename.
1077 * If there is no stored data, return line 1 and col 0.
1080 void
1081 load_file_position (const vfs_path_t * filename_vpath, long *line, long *column, off_t * offset,
1082 GArray ** bookmarks)
1084 char *fn;
1085 FILE *f;
1086 char buf[MC_MAXPATHLEN + 100];
1087 const size_t len = vfs_path_len (filename_vpath);
1088 char *filename;
1090 /* defaults */
1091 *line = 1;
1092 *column = 0;
1093 *offset = 0;
1095 /* open file with positions */
1096 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1097 f = fopen (fn, "r");
1098 g_free (fn);
1099 if (f == NULL)
1100 return;
1102 /* prepare array for serialized bookmarks */
1103 *bookmarks = g_array_sized_new (FALSE, FALSE, sizeof (size_t), MAX_SAVED_BOOKMARKS);
1104 filename = vfs_path_to_str (filename_vpath);
1106 while (fgets (buf, sizeof (buf), f) != NULL)
1108 const char *p;
1109 gchar **pos_tokens;
1111 /* check if the filename matches the beginning of string */
1112 if (strncmp (buf, filename, len) != 0)
1113 continue;
1115 /* followed by single space */
1116 if (buf[len] != ' ')
1117 continue;
1119 /* and string without spaces */
1120 p = &buf[len + 1];
1121 if (strchr (p, ' ') != NULL)
1122 continue;
1124 pos_tokens = g_strsplit (p, ";", 3 + MAX_SAVED_BOOKMARKS);
1125 if (pos_tokens[0] == NULL)
1127 *line = 1;
1128 *column = 0;
1129 *offset = 0;
1131 else
1133 *line = strtol (pos_tokens[0], NULL, 10);
1134 if (pos_tokens[1] == NULL)
1136 *column = 0;
1137 *offset = 0;
1139 else
1141 *column = strtol (pos_tokens[1], NULL, 10);
1142 if (pos_tokens[2] == NULL)
1143 *offset = 0;
1144 else
1146 size_t i;
1148 *offset = (off_t) g_ascii_strtoll (pos_tokens[2], NULL, 10);
1150 for (i = 0; i < MAX_SAVED_BOOKMARKS && pos_tokens[3 + i] != NULL; i++)
1152 size_t val;
1154 val = strtoul (pos_tokens[3 + i], NULL, 10);
1155 g_array_append_val (*bookmarks, val);
1161 g_strfreev (pos_tokens);
1164 g_free (filename);
1165 fclose (f);
1168 /* --------------------------------------------------------------------------------------------- */
1170 * Save position for the given file
1173 void
1174 save_file_position (const vfs_path_t * filename_vpath, long line, long column, off_t offset,
1175 GArray * bookmarks)
1177 static size_t filepos_max_saved_entries = 0;
1178 char *fn, *tmp_fn;
1179 FILE *f, *tmp_f;
1180 char buf[MC_MAXPATHLEN + 100];
1181 size_t i;
1182 const size_t len = vfs_path_len (filename_vpath);
1183 gboolean src_error = FALSE;
1184 char *filename;
1186 if (filepos_max_saved_entries == 0)
1187 filepos_max_saved_entries = mc_config_get_int (mc_main_config, CONFIG_APP_SECTION,
1188 "filepos_max_saved_entries", 1024);
1190 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1191 if (fn == NULL)
1192 goto early_error;
1194 mc_util_make_backup_if_possible (fn, TMP_SUFFIX);
1196 /* open file */
1197 f = fopen (fn, "w");
1198 if (f == NULL)
1199 goto open_target_error;
1201 tmp_fn = g_strdup_printf ("%s" TMP_SUFFIX, fn);
1202 tmp_f = fopen (tmp_fn, "r");
1203 if (tmp_f == NULL)
1205 src_error = TRUE;
1206 goto open_source_error;
1209 filename = vfs_path_to_str (filename_vpath);
1210 /* put the new record */
1211 if (line != 1 || column != 0 || bookmarks != NULL)
1213 if (fprintf (f, "%s %ld;%ld;%" PRIuMAX, filename, line, column, (uintmax_t) offset) < 0)
1214 goto write_position_error;
1215 if (bookmarks != NULL)
1216 for (i = 0; i < bookmarks->len && i < MAX_SAVED_BOOKMARKS; i++)
1217 if (fprintf (f, ";%zu", g_array_index (bookmarks, size_t, i)) < 0)
1218 goto write_position_error;
1220 if (fprintf (f, "\n") < 0)
1221 goto write_position_error;
1224 i = 1;
1225 while (fgets (buf, sizeof (buf), tmp_f) != NULL)
1227 if (buf[len] == ' ' && strncmp (buf, filename, len) == 0
1228 && strchr (&buf[len + 1], ' ') == NULL)
1229 continue;
1231 fprintf (f, "%s", buf);
1232 if (++i > filepos_max_saved_entries)
1233 break;
1236 write_position_error:
1237 g_free (filename);
1238 fclose (tmp_f);
1239 open_source_error:
1240 g_free (tmp_fn);
1241 fclose (f);
1242 if (src_error)
1243 mc_util_restore_from_backup_if_possible (fn, TMP_SUFFIX);
1244 else
1245 mc_util_unlink_backup_if_possible (fn, TMP_SUFFIX);
1246 open_target_error:
1247 g_free (fn);
1248 early_error:
1249 if (bookmarks != NULL)
1250 g_array_free (bookmarks, TRUE);
1253 /* --------------------------------------------------------------------------------------------- */
1255 extern int
1256 ascii_alpha_to_cntrl (int ch)
1258 if ((ch >= ASCII_A && ch <= ASCII_Z) || (ch >= ASCII_a && ch <= ASCII_z))
1260 ch &= 0x1f;
1262 return ch;
1265 /* --------------------------------------------------------------------------------------------- */
1267 const char *
1268 Q_ (const char *s)
1270 const char *result, *sep;
1272 result = _(s);
1273 sep = strchr (result, '|');
1274 return (sep != NULL) ? sep + 1 : result;
1277 /* --------------------------------------------------------------------------------------------- */
1279 gboolean
1280 mc_util_make_backup_if_possible (const char *file_name, const char *backup_suffix)
1282 struct stat stat_buf;
1283 char *backup_path;
1284 gboolean ret;
1285 if (!exist_file (file_name))
1286 return FALSE;
1288 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1290 if (backup_path == NULL)
1291 return FALSE;
1293 ret = mc_util_write_backup_content (file_name, backup_path);
1295 if (ret)
1297 /* Backup file will have same ownership with main file. */
1298 if (stat (file_name, &stat_buf) == 0)
1299 chmod (backup_path, stat_buf.st_mode);
1300 else
1301 chmod (backup_path, S_IRUSR | S_IWUSR);
1304 g_free (backup_path);
1306 return ret;
1309 /* --------------------------------------------------------------------------------------------- */
1311 gboolean
1312 mc_util_restore_from_backup_if_possible (const char *file_name, const char *backup_suffix)
1314 gboolean ret;
1315 char *backup_path;
1317 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1318 if (backup_path == NULL)
1319 return FALSE;
1321 ret = mc_util_write_backup_content (backup_path, file_name);
1322 g_free (backup_path);
1324 return ret;
1327 /* --------------------------------------------------------------------------------------------- */
1329 gboolean
1330 mc_util_unlink_backup_if_possible (const char *file_name, const char *backup_suffix)
1332 char *backup_path;
1334 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1335 if (backup_path == NULL)
1336 return FALSE;
1338 if (exist_file (backup_path))
1340 vfs_path_t *vpath;
1342 vpath = vfs_path_from_str (backup_path);
1343 mc_unlink (vpath);
1344 vfs_path_free (vpath);
1347 g_free (backup_path);
1348 return TRUE;
1351 /* --------------------------------------------------------------------------------------------- */
1353 * partly taken from dcigettext.c, returns "" for default locale
1354 * value should be freed by calling function g_free()
1357 char *
1358 guess_message_value (void)
1360 static const char *const var[] = {
1361 /* Setting of LC_ALL overwrites all other. */
1362 /* Do not use LANGUAGE for check user locale and drowing hints */
1363 "LC_ALL",
1364 /* Next comes the name of the desired category. */
1365 "LC_MESSAGES",
1366 /* Last possibility is the LANG environment variable. */
1367 "LANG",
1368 /* NULL exit loops */
1369 NULL
1372 unsigned i = 0;
1373 const char *locale = NULL;
1375 while (var[i] != NULL)
1377 locale = getenv (var[i]);
1378 if (locale != NULL && locale[0] != '\0')
1379 break;
1380 i++;
1383 if (locale == NULL)
1384 locale = "";
1386 return g_strdup (locale);
1389 /* --------------------------------------------------------------------------------------------- */