Add the hint-files synchronization with Transifex.
[midnight-commander.git] / lib / util.c
blobb7787ab7eae431aca92c7f22aa87b36b150d96d4
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.
409 * -1 means maximum possible unit for specified size
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 };
452 int j = 0;
454 if (len == 0)
455 len = 9;
456 #if SIZEOF_UINTMAX_T == 8
457 /* 20 decimal digits are required to represent 8 bytes */
458 else if (len > 19)
459 len = 19;
460 #else
461 /* 10 decimal digits are required to represent 4 bytes */
462 else if (len > 9)
463 len = 9;
464 #endif
466 /* find maximum unit */
467 if (units < 0)
469 const unsigned int divider = use_si ? 1000 : 1024;
470 uintmax_t size_remain = size;
472 for (units = 0; size_remain >= divider; units++)
473 size_remain /= divider;
477 * recalculate from 1024 base to 1000 base if units>0
478 * We can't just multiply by 1024 - that might cause overflow
479 * if off_t type is too small
481 if (use_si)
482 for (j = 0; j < units; j++)
484 uintmax_t size_remain;
486 size_remain = ((size % 125) * 1024) / 1000; /* size mod 125, recalculated */
487 size = size / 125; /* 128/125 = 1024/1000 */
488 size = size * 128; /* This will convert size from multiple of 1024 to multiple of 1000 */
489 size += size_remain; /* Re-add remainder lost by division/multiplication */
492 for (j = units; suffix[j] != NULL; j++)
494 if (size == 0)
496 if (j == units)
498 /* Empty files will print "0" even with minimal width. */
499 g_snprintf (buffer, len + 1, "0");
500 break;
503 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
504 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s",
505 (j > 1) ? (use_si ? suffix_lc[j - 1] : suffix[j - 1]) : "B");
506 break;
509 if (size < power10[len - (j > 0 ? 1 : 0)])
511 g_snprintf (buffer, len + 1, "%" PRIuMAX "%s", size, use_si ? suffix_lc[j] : suffix[j]);
512 break;
515 /* Powers of 1000 or 1024, with rounding. */
516 if (use_si)
517 size = (size + 500) / 1000;
518 else
519 size = (size + 512) >> 10;
523 /* --------------------------------------------------------------------------------------------- */
525 const char *
526 string_perm (mode_t mode_bits)
528 static char mode[11];
530 strcpy (mode, "----------");
531 if (S_ISDIR (mode_bits))
532 mode[0] = 'd';
533 if (S_ISCHR (mode_bits))
534 mode[0] = 'c';
535 if (S_ISBLK (mode_bits))
536 mode[0] = 'b';
537 if (S_ISLNK (mode_bits))
538 mode[0] = 'l';
539 if (S_ISFIFO (mode_bits))
540 mode[0] = 'p';
541 if (S_ISNAM (mode_bits))
542 mode[0] = 'n';
543 if (S_ISSOCK (mode_bits))
544 mode[0] = 's';
545 if (S_ISDOOR (mode_bits))
546 mode[0] = 'D';
547 if (ismode (mode_bits, S_IXOTH))
548 mode[9] = 'x';
549 if (ismode (mode_bits, S_IWOTH))
550 mode[8] = 'w';
551 if (ismode (mode_bits, S_IROTH))
552 mode[7] = 'r';
553 if (ismode (mode_bits, S_IXGRP))
554 mode[6] = 'x';
555 if (ismode (mode_bits, S_IWGRP))
556 mode[5] = 'w';
557 if (ismode (mode_bits, S_IRGRP))
558 mode[4] = 'r';
559 if (ismode (mode_bits, S_IXUSR))
560 mode[3] = 'x';
561 if (ismode (mode_bits, S_IWUSR))
562 mode[2] = 'w';
563 if (ismode (mode_bits, S_IRUSR))
564 mode[1] = 'r';
565 #ifdef S_ISUID
566 if (ismode (mode_bits, S_ISUID))
567 mode[3] = (mode[3] == 'x') ? 's' : 'S';
568 #endif /* S_ISUID */
569 #ifdef S_ISGID
570 if (ismode (mode_bits, S_ISGID))
571 mode[6] = (mode[6] == 'x') ? 's' : 'S';
572 #endif /* S_ISGID */
573 #ifdef S_ISVTX
574 if (ismode (mode_bits, S_ISVTX))
575 mode[9] = (mode[9] == 'x') ? 't' : 'T';
576 #endif /* S_ISVTX */
577 return mode;
580 /* --------------------------------------------------------------------------------------------- */
582 const char *
583 extension (const char *filename)
585 const char *d = strrchr (filename, '.');
586 return (d != NULL) ? d + 1 : "";
589 /* --------------------------------------------------------------------------------------------- */
591 char *
592 load_mc_home_file (const char *from, const char *filename, char **allocated_filename)
594 char *hintfile_base, *hintfile;
595 char *lang;
596 char *data;
598 hintfile_base = g_build_filename (from, filename, (char *) NULL);
599 lang = guess_message_value ();
601 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
602 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
604 /* Fall back to the two-letter language code */
605 if (lang[0] != '\0' && lang[1] != '\0')
606 lang[2] = '\0';
607 g_free (hintfile);
608 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
609 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
611 g_free (hintfile);
612 hintfile = hintfile_base;
613 g_file_get_contents (hintfile_base, &data, NULL, NULL);
617 g_free (lang);
619 if (hintfile != hintfile_base)
620 g_free (hintfile_base);
622 if (allocated_filename != NULL)
623 *allocated_filename = hintfile;
624 else
625 g_free (hintfile);
627 return data;
630 /* --------------------------------------------------------------------------------------------- */
632 const char *
633 extract_line (const char *s, const char *top)
635 static char tmp_line[BUF_MEDIUM];
636 char *t = tmp_line;
638 while (*s && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line) - 1 && s < top)
639 *t++ = *s++;
640 *t = 0;
641 return tmp_line;
644 /* --------------------------------------------------------------------------------------------- */
646 * The basename routine
649 const char *
650 x_basename (const char *s)
652 const char *url_delim, *path_sep;
654 url_delim = g_strrstr (s, VFS_PATH_URL_DELIMITER);
655 path_sep = strrchr (s, PATH_SEP);
657 if (url_delim == NULL
658 || url_delim < path_sep - strlen (VFS_PATH_URL_DELIMITER)
659 || url_delim - s + strlen (VFS_PATH_URL_DELIMITER) < strlen (s))
661 /* avoid trailing PATH_SEP, if present */
662 if (s[strlen (s) - 1] == PATH_SEP)
664 while (--path_sep > s && *path_sep != PATH_SEP);
665 return (path_sep != s) ? path_sep + 1 : s;
667 else
668 return (path_sep != NULL) ? path_sep + 1 : s;
671 while (--url_delim > s && *url_delim != PATH_SEP);
672 while (--url_delim > s && *url_delim != PATH_SEP);
674 return (url_delim == s) ? s : url_delim + 1;
677 /* --------------------------------------------------------------------------------------------- */
679 const char *
680 unix_error_string (int error_num)
682 static char buffer[BUF_LARGE];
683 gchar *strerror_currentlocale;
685 strerror_currentlocale = g_locale_from_utf8 (g_strerror (error_num), -1, NULL, NULL, NULL);
686 g_snprintf (buffer, sizeof (buffer), "%s (%d)", strerror_currentlocale, error_num);
687 g_free (strerror_currentlocale);
689 return buffer;
692 /* --------------------------------------------------------------------------------------------- */
694 const char *
695 skip_separators (const char *s)
697 const char *su = s;
699 for (; *su; str_cnext_char (&su))
700 if (*su != ' ' && *su != '\t' && *su != ',')
701 break;
703 return su;
706 /* --------------------------------------------------------------------------------------------- */
708 const char *
709 skip_numbers (const char *s)
711 const char *su = s;
713 for (; *su; str_cnext_char (&su))
714 if (!str_isdigit (su))
715 break;
717 return su;
720 /* --------------------------------------------------------------------------------------------- */
722 * Remove all control sequences from the argument string. We define
723 * "control sequence", in a sort of pidgin BNF, as follows:
725 * control-seq = Esc non-'['
726 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
728 * This scheme works for all the terminals described in my termcap /
729 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
730 * terminals. If I hear from a single person who uses such a terminal
731 * with MC, I'll be glad to add support for it. (Dugan)
732 * Non-printable characters are also removed.
735 char *
736 strip_ctrl_codes (char *s)
738 char *w; /* Current position where the stripped data is written */
739 char *r; /* Current position where the original data is read */
740 char *n;
742 if (!s)
743 return 0;
745 for (w = s, r = s; *r;)
747 if (*r == ESC_CHAR)
749 /* Skip the control sequence's arguments */ ;
750 /* '(' need to avoid strange 'B' letter in *Suse (if mc runs under root user) */
751 if (*(++r) == '[' || *r == '(')
753 /* strchr() matches trailing binary 0 */
754 while (*(++r) && strchr ("0123456789;?", *r));
756 else if (*r == ']')
759 * Skip xterm's OSC (Operating System Command)
760 * http://www.xfree86.org/current/ctlseqs.html
761 * OSC P s ; P t ST
762 * OSC P s ; P t BEL
764 char *new_r = r;
766 for (; *new_r; ++new_r)
768 switch (*new_r)
770 /* BEL */
771 case '\a':
772 r = new_r;
773 goto osc_out;
774 case ESC_CHAR:
775 /* ST */
776 if (*(new_r + 1) == '\\')
778 r = new_r + 1;
779 goto osc_out;
783 osc_out:;
787 * Now we are at the last character of the sequence.
788 * Skip it unless it's binary 0.
790 if (*r)
791 r++;
792 continue;
795 n = str_get_next_char (r);
796 if (str_isprint (r))
798 memmove (w, r, n - r);
799 w += n - r;
801 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 extention 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);
1101 char *filename;
1103 /* defaults */
1104 *line = 1;
1105 *column = 0;
1106 *offset = 0;
1108 /* open file with positions */
1109 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1110 f = fopen (fn, "r");
1111 g_free (fn);
1112 if (f == NULL)
1113 return;
1115 /* prepare array for serialized bookmarks */
1116 if (bookmarks != NULL)
1117 *bookmarks = g_array_sized_new (FALSE, FALSE, sizeof (size_t), MAX_SAVED_BOOKMARKS);
1118 filename = vfs_path_to_str (filename_vpath);
1120 while (fgets (buf, sizeof (buf), f) != NULL)
1122 const char *p;
1123 gchar **pos_tokens;
1125 /* check if the filename matches the beginning of string */
1126 if (strncmp (buf, filename, len) != 0)
1127 continue;
1129 /* followed by single space */
1130 if (buf[len] != ' ')
1131 continue;
1133 /* and string without spaces */
1134 p = &buf[len + 1];
1135 if (strchr (p, ' ') != NULL)
1136 continue;
1138 pos_tokens = g_strsplit (p, ";", 3 + MAX_SAVED_BOOKMARKS);
1139 if (pos_tokens[0] == NULL)
1141 *line = 1;
1142 *column = 0;
1143 *offset = 0;
1145 else
1147 *line = strtol (pos_tokens[0], NULL, 10);
1148 if (pos_tokens[1] == NULL)
1150 *column = 0;
1151 *offset = 0;
1153 else
1155 *column = strtol (pos_tokens[1], NULL, 10);
1156 if (pos_tokens[2] == NULL)
1157 *offset = 0;
1158 else if (bookmarks != NULL)
1160 size_t i;
1162 *offset = (off_t) g_ascii_strtoll (pos_tokens[2], NULL, 10);
1164 for (i = 0; i < MAX_SAVED_BOOKMARKS && pos_tokens[3 + i] != NULL; i++)
1166 size_t val;
1168 val = strtoul (pos_tokens[3 + i], NULL, 10);
1169 g_array_append_val (*bookmarks, val);
1175 g_strfreev (pos_tokens);
1178 g_free (filename);
1179 fclose (f);
1182 /* --------------------------------------------------------------------------------------------- */
1184 * Save position for the given file
1187 void
1188 save_file_position (const vfs_path_t * filename_vpath, long line, long column, off_t offset,
1189 GArray * bookmarks)
1191 static size_t filepos_max_saved_entries = 0;
1192 char *fn, *tmp_fn;
1193 FILE *f, *tmp_f;
1194 char buf[MC_MAXPATHLEN + 100];
1195 size_t i;
1196 const size_t len = vfs_path_len (filename_vpath);
1197 gboolean src_error = FALSE;
1198 char *filename;
1200 if (filepos_max_saved_entries == 0)
1201 filepos_max_saved_entries = mc_config_get_int (mc_main_config, CONFIG_APP_SECTION,
1202 "filepos_max_saved_entries", 1024);
1204 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1205 if (fn == NULL)
1206 goto early_error;
1208 mc_util_make_backup_if_possible (fn, TMP_SUFFIX);
1210 /* open file */
1211 f = fopen (fn, "w");
1212 if (f == NULL)
1213 goto open_target_error;
1215 tmp_fn = g_strdup_printf ("%s" TMP_SUFFIX, fn);
1216 tmp_f = fopen (tmp_fn, "r");
1217 if (tmp_f == NULL)
1219 src_error = TRUE;
1220 goto open_source_error;
1223 filename = vfs_path_to_str (filename_vpath);
1224 /* put the new record */
1225 if (line != 1 || column != 0 || bookmarks != NULL)
1227 if (fprintf (f, "%s %ld;%ld;%" PRIuMAX, filename, line, column, (uintmax_t) offset) < 0)
1228 goto write_position_error;
1229 if (bookmarks != NULL)
1230 for (i = 0; i < bookmarks->len && i < MAX_SAVED_BOOKMARKS; i++)
1231 if (fprintf (f, ";%zu", g_array_index (bookmarks, size_t, i)) < 0)
1232 goto write_position_error;
1234 if (fprintf (f, "\n") < 0)
1235 goto write_position_error;
1238 i = 1;
1239 while (fgets (buf, sizeof (buf), tmp_f) != NULL)
1241 if (buf[len] == ' ' && strncmp (buf, filename, len) == 0
1242 && strchr (&buf[len + 1], ' ') == NULL)
1243 continue;
1245 fprintf (f, "%s", buf);
1246 if (++i > filepos_max_saved_entries)
1247 break;
1250 write_position_error:
1251 g_free (filename);
1252 fclose (tmp_f);
1253 open_source_error:
1254 g_free (tmp_fn);
1255 fclose (f);
1256 if (src_error)
1257 mc_util_restore_from_backup_if_possible (fn, TMP_SUFFIX);
1258 else
1259 mc_util_unlink_backup_if_possible (fn, TMP_SUFFIX);
1260 open_target_error:
1261 g_free (fn);
1262 early_error:
1263 if (bookmarks != NULL)
1264 g_array_free (bookmarks, TRUE);
1267 /* --------------------------------------------------------------------------------------------- */
1269 extern int
1270 ascii_alpha_to_cntrl (int ch)
1272 if ((ch >= ASCII_A && ch <= ASCII_Z) || (ch >= ASCII_a && ch <= ASCII_z))
1274 ch &= 0x1f;
1276 return ch;
1279 /* --------------------------------------------------------------------------------------------- */
1281 const char *
1282 Q_ (const char *s)
1284 const char *result, *sep;
1286 result = _(s);
1287 sep = strchr (result, '|');
1288 return (sep != NULL) ? sep + 1 : result;
1291 /* --------------------------------------------------------------------------------------------- */
1293 gboolean
1294 mc_util_make_backup_if_possible (const char *file_name, const char *backup_suffix)
1296 struct stat stat_buf;
1297 char *backup_path;
1298 gboolean ret;
1299 if (!exist_file (file_name))
1300 return FALSE;
1302 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1304 if (backup_path == NULL)
1305 return FALSE;
1307 ret = mc_util_write_backup_content (file_name, backup_path);
1309 if (ret)
1311 /* Backup file will have same ownership with main file. */
1312 if (stat (file_name, &stat_buf) == 0)
1313 chmod (backup_path, stat_buf.st_mode);
1314 else
1315 chmod (backup_path, S_IRUSR | S_IWUSR);
1318 g_free (backup_path);
1320 return ret;
1323 /* --------------------------------------------------------------------------------------------- */
1325 gboolean
1326 mc_util_restore_from_backup_if_possible (const char *file_name, const char *backup_suffix)
1328 gboolean ret;
1329 char *backup_path;
1331 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1332 if (backup_path == NULL)
1333 return FALSE;
1335 ret = mc_util_write_backup_content (backup_path, file_name);
1336 g_free (backup_path);
1338 return ret;
1341 /* --------------------------------------------------------------------------------------------- */
1343 gboolean
1344 mc_util_unlink_backup_if_possible (const char *file_name, const char *backup_suffix)
1346 char *backup_path;
1348 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1349 if (backup_path == NULL)
1350 return FALSE;
1352 if (exist_file (backup_path))
1354 vfs_path_t *vpath;
1356 vpath = vfs_path_from_str (backup_path);
1357 mc_unlink (vpath);
1358 vfs_path_free (vpath);
1361 g_free (backup_path);
1362 return TRUE;
1365 /* --------------------------------------------------------------------------------------------- */
1367 * partly taken from dcigettext.c, returns "" for default locale
1368 * value should be freed by calling function g_free()
1371 char *
1372 guess_message_value (void)
1374 static const char *const var[] = {
1375 /* Setting of LC_ALL overwrites all other. */
1376 /* Do not use LANGUAGE for check user locale and drowing hints */
1377 "LC_ALL",
1378 /* Next comes the name of the desired category. */
1379 "LC_MESSAGES",
1380 /* Last possibility is the LANG environment variable. */
1381 "LANG",
1382 /* NULL exit loops */
1383 NULL
1386 unsigned i = 0;
1387 const char *locale = NULL;
1389 while (var[i] != NULL)
1391 locale = getenv (var[i]);
1392 if (locale != NULL && locale[0] != '\0')
1393 break;
1394 i++;
1397 if (locale == NULL)
1398 locale = "";
1400 return g_strdup (locale);
1403 /* --------------------------------------------------------------------------------------------- */