Extended shortcuts like 'ctrl-x x' are unavailable in editor.
[midnight-commander.git] / lib / util.c
blob3a93a0cefb732609bfd9e2dbe56c31bb960bf9be
1 /* Various utilities
2 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
3 2004, 2005, 2007, 2009 Free Software Foundation, Inc.
4 Written 1994, 1995, 1996 by:
5 Miguel de Icaza, Janne Kukonlehto, Dugan Porter,
6 Jakub Jelinek, Mauricio Plaza.
8 The file_date routine is mostly from GNU's fileutils package,
9 written by Richard Stallman and David MacKenzie.
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
25 /** \file
26 * \brief Source: various utilities
29 #include <config.h>
31 #include <ctype.h>
32 #include <limits.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <fcntl.h>
38 #include <sys/time.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
43 #include "lib/global.h"
44 #include "lib/tty/win.h" /* xterm_flag */
45 #include "lib/mcconfig.h"
46 #include "lib/fileloc.h"
47 #include "lib/vfs/vfs.h"
48 #include "lib/strutil.h"
49 #include "lib/util.h"
51 /*** global variables ****************************************************************************/
53 /*** file scope macro definitions ****************************************************************/
55 #define ismode(n,m) ((n & m) == m)
57 /* Number of attempts to create a temporary file */
58 #ifndef TMP_MAX
59 #define TMP_MAX 16384
60 #endif /* !TMP_MAX */
62 #define TMP_SUFFIX ".tmp"
64 #define ASCII_A (0x40 + 1)
65 #define ASCII_Z (0x40 + 26)
66 #define ASCII_a (0x60 + 1)
67 #define ASCII_z (0x60 + 26)
69 /*** file scope type declarations ****************************************************************/
71 /*** file scope variables ************************************************************************/
73 /*** file scope functions ************************************************************************/
74 /* --------------------------------------------------------------------------------------------- */
76 static inline int
77 is_7bit_printable (unsigned char c)
79 return (c > 31 && c < 127);
82 /* --------------------------------------------------------------------------------------------- */
84 static inline int
85 is_iso_printable (unsigned char c)
87 return ((c > 31 && c < 127) || c >= 160);
90 /* --------------------------------------------------------------------------------------------- */
92 static inline int
93 is_8bit_printable (unsigned char c)
95 /* "Full 8 bits output" doesn't work on xterm */
96 if (xterm_flag)
97 return is_iso_printable (c);
99 return (c > 31 && c != 127 && c != 155);
102 /* --------------------------------------------------------------------------------------------- */
104 static char *
105 resolve_symlinks (const char *path)
107 char *buf, *buf2, *q, *r, c;
108 int len;
109 struct stat mybuf;
110 const char *p;
112 if (*path != PATH_SEP)
113 return NULL;
114 r = buf = g_malloc (MC_MAXPATHLEN);
115 buf2 = g_malloc (MC_MAXPATHLEN);
116 *r++ = PATH_SEP;
117 *r = 0;
118 p = path;
119 for (;;)
121 q = strchr (p + 1, PATH_SEP);
122 if (!q)
124 q = strchr (p + 1, 0);
125 if (q == p + 1)
126 break;
128 c = *q;
129 *q = 0;
130 if (mc_lstat (path, &mybuf) < 0)
132 g_free (buf);
133 g_free (buf2);
134 *q = c;
135 return NULL;
137 if (!S_ISLNK (mybuf.st_mode))
138 strcpy (r, p + 1);
139 else
141 len = mc_readlink (path, buf2, MC_MAXPATHLEN - 1);
142 if (len < 0)
144 g_free (buf);
145 g_free (buf2);
146 *q = c;
147 return NULL;
149 buf2[len] = 0;
150 if (*buf2 == PATH_SEP)
151 strcpy (buf, buf2);
152 else
153 strcpy (r, buf2);
155 canonicalize_pathname (buf);
156 r = strchr (buf, 0);
157 if (!*r || *(r - 1) != PATH_SEP)
159 *r++ = PATH_SEP;
160 *r = 0;
162 *q = c;
163 p = q;
164 if (!c)
165 break;
167 if (!*buf)
168 strcpy (buf, PATH_SEP_STR);
169 else if (*(r - 1) == PATH_SEP && r != buf + 1)
170 *(r - 1) = 0;
171 g_free (buf2);
172 return buf;
175 /* --------------------------------------------------------------------------------------------- */
177 static gboolean
178 mc_util_write_backup_content (const char *from_file_name, const char *to_file_name)
180 FILE *backup_fd;
181 char *contents;
182 gsize length;
183 gboolean ret1 = TRUE;
185 if (!g_file_get_contents (from_file_name, &contents, &length, NULL))
186 return FALSE;
188 backup_fd = fopen (to_file_name, "w");
189 if (backup_fd == NULL)
191 g_free (contents);
192 return FALSE;
195 if (fwrite ((const void *) contents, 1, length, backup_fd) != length)
196 ret1 = FALSE;
198 int ret2;
199 ret2 = fflush (backup_fd);
200 ret2 = fclose (backup_fd);
202 g_free (contents);
203 return ret1;
206 /* --------------------------------------------------------------------------------------------- */
207 /*** public functions ****************************************************************************/
208 /* --------------------------------------------------------------------------------------------- */
211 is_printable (int c)
213 c &= 0xff;
215 #ifdef HAVE_CHARSET
216 /* "Display bits" is ignored, since the user controls the output
217 by setting the output codepage */
218 return is_8bit_printable (c);
219 #else
220 if (!mc_global.eight_bit_clean)
221 return is_7bit_printable (c);
223 if (mc_global.full_eight_bits)
225 return is_8bit_printable (c);
227 else
228 return is_iso_printable (c);
229 #endif /* !HAVE_CHARSET */
232 /* --------------------------------------------------------------------------------------------- */
234 * Quote the filename for the purpose of inserting it into the command
235 * line. If quote_percent is 1, replace "%" with "%%" - the percent is
236 * processed by the mc command line.
238 char *
239 name_quote (const char *s, int quote_percent)
241 char *ret, *d;
243 d = ret = g_malloc (strlen (s) * 2 + 2 + 1);
244 if (*s == '-')
246 *d++ = '.';
247 *d++ = '/';
250 for (; *s; s++, d++)
252 switch (*s)
254 case '%':
255 if (quote_percent)
256 *d++ = '%';
257 break;
258 case '\'':
259 case '\\':
260 case '\r':
261 case '\n':
262 case '\t':
263 case '"':
264 case ';':
265 case ' ':
266 case '?':
267 case '|':
268 case '[':
269 case ']':
270 case '{':
271 case '}':
272 case '<':
273 case '>':
274 case '`':
275 case '!':
276 case '$':
277 case '&':
278 case '*':
279 case '(':
280 case ')':
281 *d++ = '\\';
282 break;
283 case '~':
284 case '#':
285 if (d == ret)
286 *d++ = '\\';
287 break;
289 *d = *s;
291 *d = '\0';
292 return ret;
295 /* --------------------------------------------------------------------------------------------- */
297 char *
298 fake_name_quote (const char *s, int quote_percent)
300 (void) quote_percent;
301 return g_strdup (s);
304 /* --------------------------------------------------------------------------------------------- */
306 * path_trunc() is the same as str_trunc() but
307 * it deletes possible password from path for security
308 * reasons.
311 const char *
312 path_trunc (const char *path, size_t trunc_len)
314 char *secure_path = strip_password (g_strdup (path), 1);
316 const char *ret = str_trunc (secure_path, trunc_len);
317 g_free (secure_path);
319 return ret;
322 /* --------------------------------------------------------------------------------------------- */
324 const char *
325 size_trunc (uintmax_t size, gboolean use_si)
327 static char x[BUF_TINY];
328 uintmax_t divisor = 1;
329 const char *xtra = "";
331 if (size > 999999999UL)
333 divisor = use_si ? 1000 : 1024;
334 xtra = use_si ? "k" : "K";
335 if (size / divisor > 999999999UL)
337 divisor = use_si ? (1000 * 1000) : (1024 * 1024);
338 xtra = use_si ? "m" : "M";
341 g_snprintf (x, sizeof (x), "%.0f%s", 1.0 * size / divisor, xtra);
342 return x;
345 /* --------------------------------------------------------------------------------------------- */
347 const char *
348 size_trunc_sep (uintmax_t size, gboolean use_si)
350 static char x[60];
351 int count;
352 const char *p, *y;
353 char *d;
355 p = y = size_trunc (size, use_si);
356 p += strlen (p) - 1;
357 d = x + sizeof (x) - 1;
358 *d-- = '\0';
359 while (p >= y && isalpha ((unsigned char) *p))
360 *d-- = *p--;
361 for (count = 0; p >= y; count++)
363 if (count == 3)
365 *d-- = ',';
366 count = 0;
368 *d-- = *p--;
370 d++;
371 if (*d == ',')
372 d++;
373 return d;
376 /* --------------------------------------------------------------------------------------------- */
378 * Print file SIZE to BUFFER, but don't exceed LEN characters,
379 * not including trailing 0. BUFFER should be at least LEN+1 long.
380 * This function is called for every file on panels, so avoid
381 * floating point by any means.
383 * Units: size units (filesystem sizes are 1K blocks)
384 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
387 void
388 size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gboolean use_si)
390 /* Avoid taking power for every file. */
391 static const uintmax_t power10[] = {
392 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
394 static const char *const suffix[] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL };
395 static const char *const suffix_lc[] = { "", "k", "m", "g", "t", "p", "e", "z", "y", NULL };
396 int j = 0;
397 int size_remain;
399 if (len == 0)
400 len = 9;
403 * recalculate from 1024 base to 1000 base if units>0
404 * We can't just multiply by 1024 - that might cause overflow
405 * if off_t type is too small
407 if (use_si)
408 for (j = 0; j < units; j++)
410 size_remain = ((size % 125) * 1024) / 1000; /* size mod 125, recalculated */
411 size = size / 125; /* 128/125 = 1024/1000 */
412 size = size * 128; /* This will convert size from multiple of 1024 to multiple of 1000 */
413 size += size_remain; /* Re-add remainder lost by division/multiplication */
416 for (j = units; suffix[j] != NULL; j++)
418 if (size == 0)
420 if (j == units)
422 /* Empty files will print "0" even with minimal width. */
423 g_snprintf (buffer, len + 1, "0");
424 break;
427 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
428 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s",
429 (j > 1) ? (use_si ? suffix_lc[j - 1] : suffix[j - 1]) : "B");
430 break;
433 if (size < power10[len - (j > 0)])
435 g_snprintf (buffer, len + 1, "%" PRIuMAX "%s", size, use_si ? suffix_lc[j] : suffix[j]);
436 break;
439 /* Powers of 1000 or 1024, with rounding. */
440 if (use_si)
441 size = (size + 500) / 1000;
442 else
443 size = (size + 512) >> 10;
447 /* --------------------------------------------------------------------------------------------- */
449 const char *
450 string_perm (mode_t mode_bits)
452 static char mode[11];
454 strcpy (mode, "----------");
455 if (S_ISDIR (mode_bits))
456 mode[0] = 'd';
457 if (S_ISCHR (mode_bits))
458 mode[0] = 'c';
459 if (S_ISBLK (mode_bits))
460 mode[0] = 'b';
461 if (S_ISLNK (mode_bits))
462 mode[0] = 'l';
463 if (S_ISFIFO (mode_bits))
464 mode[0] = 'p';
465 if (S_ISNAM (mode_bits))
466 mode[0] = 'n';
467 if (S_ISSOCK (mode_bits))
468 mode[0] = 's';
469 if (S_ISDOOR (mode_bits))
470 mode[0] = 'D';
471 if (ismode (mode_bits, S_IXOTH))
472 mode[9] = 'x';
473 if (ismode (mode_bits, S_IWOTH))
474 mode[8] = 'w';
475 if (ismode (mode_bits, S_IROTH))
476 mode[7] = 'r';
477 if (ismode (mode_bits, S_IXGRP))
478 mode[6] = 'x';
479 if (ismode (mode_bits, S_IWGRP))
480 mode[5] = 'w';
481 if (ismode (mode_bits, S_IRGRP))
482 mode[4] = 'r';
483 if (ismode (mode_bits, S_IXUSR))
484 mode[3] = 'x';
485 if (ismode (mode_bits, S_IWUSR))
486 mode[2] = 'w';
487 if (ismode (mode_bits, S_IRUSR))
488 mode[1] = 'r';
489 #ifdef S_ISUID
490 if (ismode (mode_bits, S_ISUID))
491 mode[3] = (mode[3] == 'x') ? 's' : 'S';
492 #endif /* S_ISUID */
493 #ifdef S_ISGID
494 if (ismode (mode_bits, S_ISGID))
495 mode[6] = (mode[6] == 'x') ? 's' : 'S';
496 #endif /* S_ISGID */
497 #ifdef S_ISVTX
498 if (ismode (mode_bits, S_ISVTX))
499 mode[9] = (mode[9] == 'x') ? 't' : 'T';
500 #endif /* S_ISVTX */
501 return mode;
504 /* --------------------------------------------------------------------------------------------- */
506 * p: string which might contain an url with a password (this parameter is
507 * modified in place).
508 * has_prefix = 0: The first parameter is an url without a prefix
509 * (user[:pass]@]machine[:port][remote-dir). Delete
510 * the password.
511 * has_prefix = 1: Search p for known url prefixes. If found delete
512 * the password from the url.
513 * Caveat: only the first url is found
516 char *
517 strip_password (char *p, int has_prefix)
519 static const struct
521 const char *name;
522 size_t len;
523 } prefixes[] =
525 /* *INDENT-OFF* */
526 { "/#ftp:", 6 },
527 { "ftp://", 6 },
528 { "/#smb:", 6 },
529 { "smb://", 6 },
530 { "/#sh:", 5 },
531 { "sh://", 5 },
532 { "ssh://", 6 }
533 /* *INDENT-ON* */
536 char *at, *inner_colon, *dir;
537 size_t i;
538 char *result = p;
540 for (i = 0; i < sizeof (prefixes) / sizeof (prefixes[0]); i++)
542 char *q;
544 if (has_prefix)
546 q = strstr (p, prefixes[i].name);
547 if (q == NULL)
548 continue;
549 else
550 p = q + prefixes[i].len;
553 dir = strchr (p, PATH_SEP);
554 if (dir != NULL)
555 *dir = '\0';
557 /* search for any possible user */
558 at = strrchr (p, '@');
560 if (dir)
561 *dir = PATH_SEP;
563 /* We have a username */
564 if (at)
566 inner_colon = memchr (p, ':', at - p);
567 if (inner_colon)
568 memmove (inner_colon, at, strlen (at) + 1);
570 break;
572 return (result);
575 /* --------------------------------------------------------------------------------------------- */
577 const char *
578 strip_home_and_password (const char *dir)
580 size_t len;
581 static char newdir[MC_MAXPATHLEN];
583 len = strlen (mc_config_get_home_dir ());
584 if (mc_config_get_home_dir () != NULL && strncmp (dir, mc_config_get_home_dir (), len) == 0 &&
585 (dir[len] == PATH_SEP || dir[len] == '\0'))
587 newdir[0] = '~';
588 g_strlcpy (&newdir[1], &dir[len], sizeof (newdir) - 1);
589 return newdir;
592 /* We do not strip homes in /#ftp tree, I do not like ~'s there
593 (see ftpfs.c why) */
594 g_strlcpy (newdir, dir, sizeof (newdir));
595 strip_password (newdir, 1);
596 return newdir;
599 /* --------------------------------------------------------------------------------------------- */
601 const char *
602 extension (const char *filename)
604 const char *d = strrchr (filename, '.');
605 return (d != NULL) ? d + 1 : "";
608 /* --------------------------------------------------------------------------------------------- */
610 char *
611 load_mc_home_file (const char *from, const char *filename, char **allocated_filename)
613 char *hintfile_base, *hintfile;
614 char *lang;
615 char *data;
617 hintfile_base = g_build_filename (from, filename, (char *) NULL);
618 lang = guess_message_value ();
620 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
621 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
623 /* Fall back to the two-letter language code */
624 if (lang[0] != '\0' && lang[1] != '\0')
625 lang[2] = '\0';
626 g_free (hintfile);
627 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
628 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
630 g_free (hintfile);
631 hintfile = hintfile_base;
632 g_file_get_contents (hintfile_base, &data, NULL, NULL);
636 g_free (lang);
638 if (hintfile != hintfile_base)
639 g_free (hintfile_base);
641 if (allocated_filename != NULL)
642 *allocated_filename = hintfile;
643 else
644 g_free (hintfile);
646 return data;
649 /* --------------------------------------------------------------------------------------------- */
651 const char *
652 extract_line (const char *s, const char *top)
654 static char tmp_line[BUF_MEDIUM];
655 char *t = tmp_line;
657 while (*s && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line) - 1 && s < top)
658 *t++ = *s++;
659 *t = 0;
660 return tmp_line;
663 /* --------------------------------------------------------------------------------------------- */
665 * The basename routine
668 const char *
669 x_basename (const char *s)
671 const char *url_delim, *path_sep;
673 url_delim = g_strrstr (s, VFS_PATH_URL_DELIMITER);
674 path_sep = strrchr (s, PATH_SEP);
676 if (url_delim == NULL
677 || url_delim < path_sep - strlen (VFS_PATH_URL_DELIMITER)
678 || url_delim - s + strlen (VFS_PATH_URL_DELIMITER) < strlen (s))
680 /* avoid trailing PATH_SEP, if present */
681 if (s[strlen (s) - 1] == PATH_SEP)
683 while (--path_sep > s && *path_sep != PATH_SEP);
684 return (path_sep != s) ? path_sep + 1 : s;
686 else
687 return (path_sep != NULL) ? path_sep + 1 : s;
690 while (--url_delim > s && *url_delim != PATH_SEP);
691 while (--url_delim > s && *url_delim != PATH_SEP);
693 return (url_delim == s) ? s : url_delim + 1;
696 /* --------------------------------------------------------------------------------------------- */
698 const char *
699 unix_error_string (int error_num)
701 static char buffer[BUF_LARGE];
702 gchar *strerror_currentlocale;
704 strerror_currentlocale = g_locale_from_utf8 (g_strerror (error_num), -1, NULL, NULL, NULL);
705 g_snprintf (buffer, sizeof (buffer), "%s (%d)", strerror_currentlocale, error_num);
706 g_free (strerror_currentlocale);
708 return buffer;
711 /* --------------------------------------------------------------------------------------------- */
713 const char *
714 skip_separators (const char *s)
716 const char *su = s;
718 for (; *su; str_cnext_char (&su))
719 if (*su != ' ' && *su != '\t' && *su != ',')
720 break;
722 return su;
725 /* --------------------------------------------------------------------------------------------- */
727 const char *
728 skip_numbers (const char *s)
730 const char *su = s;
732 for (; *su; str_cnext_char (&su))
733 if (!str_isdigit (su))
734 break;
736 return su;
739 /* --------------------------------------------------------------------------------------------- */
741 * Remove all control sequences from the argument string. We define
742 * "control sequence", in a sort of pidgin BNF, as follows:
744 * control-seq = Esc non-'['
745 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
747 * This scheme works for all the terminals described in my termcap /
748 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
749 * terminals. If I hear from a single person who uses such a terminal
750 * with MC, I'll be glad to add support for it. (Dugan)
751 * Non-printable characters are also removed.
754 char *
755 strip_ctrl_codes (char *s)
757 char *w; /* Current position where the stripped data is written */
758 char *r; /* Current position where the original data is read */
759 char *n;
761 if (!s)
762 return 0;
764 for (w = s, r = s; *r;)
766 if (*r == ESC_CHAR)
768 /* Skip the control sequence's arguments */ ;
769 /* '(' need to avoid strange 'B' letter in *Suse (if mc runs under root user) */
770 if (*(++r) == '[' || *r == '(')
772 /* strchr() matches trailing binary 0 */
773 while (*(++r) && strchr ("0123456789;?", *r));
775 else if (*r == ']')
778 * Skip xterm's OSC (Operating System Command)
779 * http://www.xfree86.org/current/ctlseqs.html
780 * OSC P s ; P t ST
781 * OSC P s ; P t BEL
783 char *new_r = r;
785 for (; *new_r; ++new_r)
787 switch (*new_r)
789 /* BEL */
790 case '\a':
791 r = new_r;
792 goto osc_out;
793 case ESC_CHAR:
794 /* ST */
795 if (*(new_r + 1) == '\\')
797 r = new_r + 1;
798 goto osc_out;
802 osc_out:;
806 * Now we are at the last character of the sequence.
807 * Skip it unless it's binary 0.
809 if (*r)
810 r++;
811 continue;
814 n = str_get_next_char (r);
815 if (str_isprint (r))
817 memmove (w, r, n - r);
818 w += n - r;
820 r = n;
822 *w = 0;
823 return s;
826 /* --------------------------------------------------------------------------------------------- */
828 enum compression_type
829 get_compression_type (int fd, const char *name)
831 unsigned char magic[16];
832 size_t str_len;
834 /* Read the magic signature */
835 if (mc_read (fd, (char *) magic, 4) != 4)
836 return COMPRESSION_NONE;
838 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
839 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236))
841 return COMPRESSION_GZIP;
844 /* PKZIP_MAGIC */
845 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003 && magic[3] == 004)
847 /* Read compression type */
848 mc_lseek (fd, 8, SEEK_SET);
849 if (mc_read (fd, (char *) magic, 2) != 2)
850 return COMPRESSION_NONE;
852 /* Gzip can handle only deflated (8) or stored (0) files */
853 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
854 return COMPRESSION_NONE;
856 /* Compatible with gzip */
857 return COMPRESSION_GZIP;
860 /* PACK_MAGIC and LZH_MAGIC and compress magic */
861 if (magic[0] == 037 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235))
863 /* Compatible with gzip */
864 return COMPRESSION_GZIP;
867 /* BZIP and BZIP2 files */
868 if ((magic[0] == 'B') && (magic[1] == 'Z') && (magic[3] >= '1') && (magic[3] <= '9'))
870 switch (magic[2])
872 case '0':
873 return COMPRESSION_BZIP;
874 case 'h':
875 return COMPRESSION_BZIP2;
879 /* Support for LZMA (only utils format with magic in header).
880 * This is the default format of LZMA utils 4.32.1 and later. */
882 if (mc_read (fd, (char *) magic + 4, 2) != 2)
883 return COMPRESSION_NONE;
885 /* LZMA utils format */
886 if (magic[0] == 0xFF
887 && magic[1] == 'L'
888 && magic[2] == 'Z' && magic[3] == 'M' && magic[4] == 'A' && magic[5] == 0x00)
889 return COMPRESSION_LZMA;
891 /* XZ compression magic */
892 if (magic[0] == 0xFD
893 && magic[1] == 0x37
894 && magic[2] == 0x7A && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00)
895 return COMPRESSION_XZ;
897 str_len = strlen (name);
898 /* HACK: we must belive to extention of LZMA file :) ... */
899 if ((str_len > 5 && strcmp (&name[str_len - 5], ".lzma") == 0) ||
900 (str_len > 4 && strcmp (&name[str_len - 4], ".tlz") == 0))
901 return COMPRESSION_LZMA;
903 return COMPRESSION_NONE;
906 /* --------------------------------------------------------------------------------------------- */
908 const char *
909 decompress_extension (int type)
911 switch (type)
913 case COMPRESSION_GZIP:
914 return "/ugz" VFS_PATH_URL_DELIMITER;
915 case COMPRESSION_BZIP:
916 return "/ubz" VFS_PATH_URL_DELIMITER;
917 case COMPRESSION_BZIP2:
918 return "/ubz2" VFS_PATH_URL_DELIMITER;
919 case COMPRESSION_LZMA:
920 return "/ulzma" VFS_PATH_URL_DELIMITER;
921 case COMPRESSION_XZ:
922 return "/uxz" VFS_PATH_URL_DELIMITER;
924 /* Should never reach this place */
925 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
926 return 0;
929 /* --------------------------------------------------------------------------------------------- */
931 void
932 wipe_password (char *passwd)
934 char *p = passwd;
936 if (!p)
937 return;
938 for (; *p; p++)
939 *p = 0;
940 g_free (passwd);
943 /* --------------------------------------------------------------------------------------------- */
945 * Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key
946 * @returns a newly allocated string
949 char *
950 convert_controls (const char *p)
952 char *valcopy = g_strdup (p);
953 char *q;
955 /* Parse the escape special character */
956 for (q = valcopy; *p;)
958 if (*p == '\\')
960 p++;
961 if ((*p == 'e') || (*p == 'E'))
963 p++;
964 *q++ = ESC_CHAR;
967 else
969 if (*p == '^')
971 p++;
972 if (*p == '^')
973 *q++ = *p++;
974 else
976 char c = (*p | 0x20);
977 if (c >= 'a' && c <= 'z')
979 *q++ = c - 'a' + 1;
980 p++;
982 else if (*p)
983 p++;
986 else
987 *q++ = *p++;
990 *q = 0;
991 return valcopy;
994 /* --------------------------------------------------------------------------------------------- */
996 * Finds out a relative path from first to second, i.e. goes as many ..
997 * as needed up in first and then goes down using second
1000 char *
1001 diff_two_paths (const char *first, const char *second)
1003 char *p, *q, *r, *s, *buf = NULL;
1004 int i, j, prevlen = -1, currlen;
1005 char *my_first = NULL, *my_second = NULL;
1007 my_first = resolve_symlinks (first);
1008 if (my_first == NULL)
1009 return NULL;
1010 my_second = resolve_symlinks (second);
1011 if (my_second == NULL)
1013 g_free (my_first);
1014 return NULL;
1016 for (j = 0; j < 2; j++)
1018 p = my_first;
1019 q = my_second;
1020 for (;;)
1022 r = strchr (p, PATH_SEP);
1023 s = strchr (q, PATH_SEP);
1024 if (!r || !s)
1025 break;
1026 *r = 0;
1027 *s = 0;
1028 if (strcmp (p, q))
1030 *r = PATH_SEP;
1031 *s = PATH_SEP;
1032 break;
1034 else
1036 *r = PATH_SEP;
1037 *s = PATH_SEP;
1039 p = r + 1;
1040 q = s + 1;
1042 p--;
1043 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++);
1044 currlen = (i + 1) * 3 + strlen (q) + 1;
1045 if (j)
1047 if (currlen < prevlen)
1048 g_free (buf);
1049 else
1051 g_free (my_first);
1052 g_free (my_second);
1053 return buf;
1056 p = buf = g_malloc (currlen);
1057 prevlen = currlen;
1058 for (; i >= 0; i--, p += 3)
1059 strcpy (p, "../");
1060 strcpy (p, q);
1062 g_free (my_first);
1063 g_free (my_second);
1064 return buf;
1067 /* --------------------------------------------------------------------------------------------- */
1069 * If filename is NULL, then we just append PATH_SEP to the dir
1072 char *
1073 concat_dir_and_file (const char *dir, const char *file)
1075 int i = strlen (dir);
1077 if (dir[i - 1] == PATH_SEP)
1078 return g_strconcat (dir, file, (char *) NULL);
1079 else
1080 return g_strconcat (dir, PATH_SEP_STR, file, (char *) NULL);
1083 /* --------------------------------------------------------------------------------------------- */
1085 * Append text to GList, remove all entries with the same text
1088 GList *
1089 list_append_unique (GList * list, char *text)
1091 GList *lc_link;
1094 * Go to the last position and traverse the list backwards
1095 * starting from the second last entry to make sure that we
1096 * are not removing the current link.
1098 list = g_list_append (list, text);
1099 list = g_list_last (list);
1100 lc_link = g_list_previous (list);
1102 while (lc_link != NULL)
1104 GList *newlink;
1106 newlink = g_list_previous (lc_link);
1107 if (strcmp ((char *) lc_link->data, text) == 0)
1109 GList *tmp;
1111 g_free (lc_link->data);
1112 tmp = g_list_remove_link (list, lc_link);
1113 g_list_free_1 (lc_link);
1115 lc_link = newlink;
1118 return list;
1121 /* --------------------------------------------------------------------------------------------- */
1122 /* Following code heavily borrows from libiberty, mkstemps.c */
1124 * Arguments:
1125 * pname (output) - pointer to the name of the temp file (needs g_free).
1126 * NULL if the function fails.
1127 * prefix - part of the filename before the random part.
1128 * Prepend $TMPDIR or /tmp if there are no path separators.
1129 * suffix - if not NULL, part of the filename after the random part.
1131 * Result:
1132 * handle of the open file or -1 if couldn't open any.
1136 mc_mkstemps (char **pname, const char *prefix, const char *suffix)
1138 static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1139 static unsigned long value;
1140 struct timeval tv;
1141 char *tmpbase;
1142 char *tmpname;
1143 char *XXXXXX;
1144 int count;
1146 if (strchr (prefix, PATH_SEP) == NULL)
1148 /* Add prefix first to find the position of XXXXXX */
1149 tmpbase = concat_dir_and_file (mc_tmpdir (), prefix);
1151 else
1153 tmpbase = g_strdup (prefix);
1156 tmpname = g_strconcat (tmpbase, "XXXXXX", suffix, (char *) NULL);
1157 *pname = tmpname;
1158 XXXXXX = &tmpname[strlen (tmpbase)];
1159 g_free (tmpbase);
1161 /* Get some more or less random data. */
1162 gettimeofday (&tv, NULL);
1163 value += (tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
1165 for (count = 0; count < TMP_MAX; ++count)
1167 unsigned long v = value;
1168 int fd;
1170 /* Fill in the random bits. */
1171 XXXXXX[0] = letters[v % 62];
1172 v /= 62;
1173 XXXXXX[1] = letters[v % 62];
1174 v /= 62;
1175 XXXXXX[2] = letters[v % 62];
1176 v /= 62;
1177 XXXXXX[3] = letters[v % 62];
1178 v /= 62;
1179 XXXXXX[4] = letters[v % 62];
1180 v /= 62;
1181 XXXXXX[5] = letters[v % 62];
1183 fd = open (tmpname, O_RDWR | O_CREAT | O_TRUNC | O_EXCL, S_IRUSR | S_IWUSR);
1184 if (fd >= 0)
1186 /* Successfully created. */
1187 return fd;
1190 /* This is a random value. It is only necessary that the next
1191 TMP_MAX values generated by adding 7777 to VALUE are different
1192 with (module 2^32). */
1193 value += 7777;
1196 /* Unsuccessful. Free the filename. */
1197 g_free (tmpname);
1198 *pname = NULL;
1200 return -1;
1203 /* --------------------------------------------------------------------------------------------- */
1205 * Read and restore position for the given filename.
1206 * If there is no stored data, return line 1 and col 0.
1209 void
1210 load_file_position (const char *filename, long *line, long *column, off_t * offset,
1211 GArray ** bookmarks)
1213 char *fn;
1214 FILE *f;
1215 char buf[MC_MAXPATHLEN + 100];
1216 const size_t len = strlen (filename);
1218 /* defaults */
1219 *line = 1;
1220 *column = 0;
1221 *offset = 0;
1223 /* open file with positions */
1224 fn = g_build_filename (mc_config_get_cache_path (), MC_FILEPOS_FILE, NULL);
1225 f = fopen (fn, "r");
1226 g_free (fn);
1227 if (f == NULL)
1228 return;
1230 /* prepare array for serialized bookmarks */
1231 *bookmarks = g_array_sized_new (FALSE, FALSE, sizeof (size_t), MAX_SAVED_BOOKMARKS);
1233 while (fgets (buf, sizeof (buf), f) != NULL)
1235 const char *p;
1236 gchar **pos_tokens;
1238 /* check if the filename matches the beginning of string */
1239 if (strncmp (buf, filename, len) != 0)
1240 continue;
1242 /* followed by single space */
1243 if (buf[len] != ' ')
1244 continue;
1246 /* and string without spaces */
1247 p = &buf[len + 1];
1248 if (strchr (p, ' ') != NULL)
1249 continue;
1251 pos_tokens = g_strsplit (p, ";", 3 + MAX_SAVED_BOOKMARKS);
1252 if (pos_tokens[0] == NULL)
1254 *line = 1;
1255 *column = 0;
1256 *offset = 0;
1258 else
1260 *line = strtol (pos_tokens[0], NULL, 10);
1261 if (pos_tokens[1] == NULL)
1263 *column = 0;
1264 *offset = 0;
1266 else
1268 *column = strtol (pos_tokens[1], NULL, 10);
1269 if (pos_tokens[2] == NULL)
1270 *offset = 0;
1271 else
1273 size_t i;
1275 *offset = strtoll (pos_tokens[2], NULL, 10);
1277 for (i = 0; i < MAX_SAVED_BOOKMARKS && pos_tokens[3 + i] != NULL; i++)
1279 size_t val;
1281 val = strtoul (pos_tokens[3 + i], NULL, 10);
1282 g_array_append_val (*bookmarks, val);
1288 g_strfreev (pos_tokens);
1291 fclose (f);
1294 /* --------------------------------------------------------------------------------------------- */
1296 * Save position for the given file
1299 void
1300 save_file_position (const char *filename, long line, long column, off_t offset, GArray * bookmarks)
1302 static size_t filepos_max_saved_entries = 0;
1303 char *fn, *tmp_fn;
1304 FILE *f, *tmp_f;
1305 char buf[MC_MAXPATHLEN + 100];
1306 size_t i;
1307 const size_t len = strlen (filename);
1308 gboolean src_error = FALSE;
1310 if (filepos_max_saved_entries == 0)
1311 filepos_max_saved_entries = mc_config_get_int (mc_main_config, CONFIG_APP_SECTION,
1312 "filepos_max_saved_entries", 1024);
1314 fn = g_build_filename (mc_config_get_cache_path (), MC_FILEPOS_FILE, NULL);
1315 if (fn == NULL)
1316 goto early_error;
1318 mc_util_make_backup_if_possible (fn, TMP_SUFFIX);
1320 /* open file */
1321 f = fopen (fn, "w");
1322 if (f == NULL)
1323 goto open_target_error;
1325 tmp_fn = g_strdup_printf ("%s" TMP_SUFFIX, fn);
1326 tmp_f = fopen (tmp_fn, "r");
1327 if (tmp_f == NULL)
1329 src_error = TRUE;
1330 goto open_source_error;
1333 /* put the new record */
1334 if (line != 1 || column != 0 || bookmarks != NULL)
1336 if (fprintf (f, "%s %ld;%ld;%" PRIuMAX, filename, line, column, (uintmax_t) offset) < 0)
1337 goto write_position_error;
1338 if (bookmarks != NULL)
1339 for (i = 0; i < bookmarks->len && i < MAX_SAVED_BOOKMARKS; i++)
1340 if (fprintf (f, ";%zu", g_array_index (bookmarks, size_t, i)) < 0)
1341 goto write_position_error;
1343 if (fprintf (f, "\n") < 0)
1344 goto write_position_error;
1347 i = 1;
1348 while (fgets (buf, sizeof (buf), tmp_f) != NULL)
1350 if (buf[len] == ' ' && strncmp (buf, filename, len) == 0
1351 && strchr (&buf[len + 1], ' ') == NULL)
1352 continue;
1354 fprintf (f, "%s", buf);
1355 if (++i > filepos_max_saved_entries)
1356 break;
1359 write_position_error:
1360 fclose (tmp_f);
1361 open_source_error:
1362 g_free (tmp_fn);
1363 fclose (f);
1364 if (src_error)
1365 mc_util_restore_from_backup_if_possible (fn, TMP_SUFFIX);
1366 else
1367 mc_util_unlink_backup_if_possible (fn, TMP_SUFFIX);
1368 open_target_error:
1369 g_free (fn);
1370 early_error:
1371 if (bookmarks != NULL)
1372 g_array_free (bookmarks, TRUE);
1375 /* --------------------------------------------------------------------------------------------- */
1377 extern int
1378 ascii_alpha_to_cntrl (int ch)
1380 if ((ch >= ASCII_A && ch <= ASCII_Z) || (ch >= ASCII_a && ch <= ASCII_z))
1382 ch &= 0x1f;
1384 return ch;
1387 /* --------------------------------------------------------------------------------------------- */
1389 const char *
1390 Q_ (const char *s)
1392 const char *result, *sep;
1394 result = _(s);
1395 sep = strchr (result, '|');
1396 return (sep != NULL) ? sep + 1 : result;
1399 /* --------------------------------------------------------------------------------------------- */
1401 gboolean
1402 mc_util_make_backup_if_possible (const char *file_name, const char *backup_suffix)
1404 struct stat stat_buf;
1405 char *backup_path;
1406 gboolean ret;
1407 if (!exist_file (file_name))
1408 return FALSE;
1410 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1412 if (backup_path == NULL)
1413 return FALSE;
1415 ret = mc_util_write_backup_content (file_name, backup_path);
1417 if (ret)
1419 /* Backup file will have same ownership with main file. */
1420 if (stat (file_name, &stat_buf) == 0)
1421 chmod (backup_path, stat_buf.st_mode);
1422 else
1423 chmod (backup_path, S_IRUSR | S_IWUSR);
1426 g_free (backup_path);
1428 return ret;
1431 /* --------------------------------------------------------------------------------------------- */
1433 gboolean
1434 mc_util_restore_from_backup_if_possible (const char *file_name, const char *backup_suffix)
1436 gboolean ret;
1437 char *backup_path;
1439 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1440 if (backup_path == NULL)
1441 return FALSE;
1443 ret = mc_util_write_backup_content (backup_path, file_name);
1444 g_free (backup_path);
1446 return ret;
1449 /* --------------------------------------------------------------------------------------------- */
1451 gboolean
1452 mc_util_unlink_backup_if_possible (const char *file_name, const char *backup_suffix)
1454 char *backup_path;
1456 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1457 if (backup_path == NULL)
1458 return FALSE;
1460 if (exist_file (backup_path))
1461 mc_unlink (backup_path);
1463 g_free (backup_path);
1464 return TRUE;
1467 /* --------------------------------------------------------------------------------------------- */
1469 * partly taken from dcigettext.c, returns "" for default locale
1470 * value should be freed by calling function g_free()
1473 char *
1474 guess_message_value (void)
1476 static const char *const var[] = {
1477 /* Setting of LC_ALL overwrites all other. */
1478 /* Do not use LANGUAGE for check user locale and drowing hints */
1479 "LC_ALL",
1480 /* Next comes the name of the desired category. */
1481 "LC_MESSAGES",
1482 /* Last possibility is the LANG environment variable. */
1483 "LANG",
1484 /* NULL exit loops */
1485 NULL
1488 unsigned i = 0;
1489 const char *locale = NULL;
1491 while (var[i] != NULL)
1493 locale = getenv (var[i]);
1494 if (locale != NULL && locale[0] != '\0')
1495 break;
1496 i++;
1499 if (locale == NULL)
1500 locale = "";
1502 return g_strdup (locale);
1505 /* --------------------------------------------------------------------------------------------- */