Try fix of compile warnings about assigned but unused variables
[midnight-commander.git] / lib / util.c
blob327f52a8a2271db9ec3631fbe4195d3ef1c8368c
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 The file_date routine is mostly from GNU's fileutils package,
16 written by Richard Stallman and David MacKenzie.
18 This file is part of the Midnight Commander.
20 The Midnight Commander is free software: you can redistribute it
21 and/or modify it under the terms of the GNU General Public License as
22 published by the Free Software Foundation, either version 3 of the License,
23 or (at your option) any later version.
25 The Midnight Commander is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
30 You should have received a copy of the GNU General Public License
31 along with this program. If not, see <http://www.gnu.org/licenses/>.
34 /** \file
35 * \brief Source: various utilities
38 #include <config.h>
40 #include <ctype.h>
41 #include <limits.h>
42 #include <stdarg.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <fcntl.h>
47 #include <sys/time.h>
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <unistd.h>
52 #include "lib/global.h"
53 #include "lib/mcconfig.h"
54 #include "lib/fileloc.h"
55 #include "lib/vfs/vfs.h"
56 #include "lib/strutil.h"
57 #include "lib/util.h"
59 /*** global variables ****************************************************************************/
61 /*** file scope macro definitions ****************************************************************/
63 #define ismode(n,m) ((n & m) == m)
65 /* Number of attempts to create a temporary file */
66 #ifndef TMP_MAX
67 #define TMP_MAX 16384
68 #endif /* !TMP_MAX */
70 #define TMP_SUFFIX ".tmp"
72 #define ASCII_A (0x40 + 1)
73 #define ASCII_Z (0x40 + 26)
74 #define ASCII_a (0x60 + 1)
75 #define ASCII_z (0x60 + 26)
77 /*** file scope type declarations ****************************************************************/
79 /*** file scope variables ************************************************************************/
81 /*** file scope functions ************************************************************************/
82 /* --------------------------------------------------------------------------------------------- */
84 static inline int
85 is_7bit_printable (unsigned char c)
87 return (c > 31 && c < 127);
90 /* --------------------------------------------------------------------------------------------- */
92 static inline int
93 is_iso_printable (unsigned char c)
95 return ((c > 31 && c < 127) || c >= 160);
98 /* --------------------------------------------------------------------------------------------- */
100 static inline int
101 is_8bit_printable (unsigned char c)
103 /* "Full 8 bits output" doesn't work on xterm */
104 if (mc_global.tty.xterm_flag)
105 return is_iso_printable (c);
107 return (c > 31 && c != 127 && c != 155);
110 /* --------------------------------------------------------------------------------------------- */
112 static char *
113 resolve_symlinks (const vfs_path_t *vpath)
115 char *p, *p2;
116 char *buf, *buf2, *q, *r, c;
117 struct stat mybuf;
119 if (vpath->relative)
120 return NULL;
122 p = p2 = vfs_path_to_str (vpath);
123 r = buf = g_malloc (MC_MAXPATHLEN);
124 buf2 = g_malloc (MC_MAXPATHLEN);
125 *r++ = PATH_SEP;
126 *r = 0;
130 q = strchr (p + 1, PATH_SEP);
131 if (!q)
133 q = strchr (p + 1, 0);
134 if (q == p + 1)
135 break;
137 c = *q;
138 *q = 0;
139 if (mc_lstat (vpath, &mybuf) < 0)
141 g_free (buf);
142 buf = NULL;
143 goto ret;
145 if (!S_ISLNK (mybuf.st_mode))
146 strcpy (r, p + 1);
147 else
149 int len;
151 len = mc_readlink (vpath, buf2, MC_MAXPATHLEN - 1);
152 if (len < 0)
154 g_free (buf);
155 buf = NULL;
156 goto ret;
158 buf2[len] = 0;
159 if (*buf2 == PATH_SEP)
160 strcpy (buf, buf2);
161 else
162 strcpy (r, buf2);
164 canonicalize_pathname (buf);
165 r = strchr (buf, 0);
166 if (!*r || *(r - 1) != PATH_SEP)
167 /* FIXME: this condition is always true because r points to the EOL */
169 *r++ = PATH_SEP;
170 *r = 0;
172 *q = c;
173 p = q;
175 while (c != '\0');
177 if (!*buf)
178 strcpy (buf, PATH_SEP_STR);
179 else if (*(r - 1) == PATH_SEP && r != buf + 1)
180 *(r - 1) = 0;
182 ret:
183 g_free (buf2);
184 g_free (p2);
185 return buf;
188 /* --------------------------------------------------------------------------------------------- */
190 static gboolean
191 mc_util_write_backup_content (const char *from_file_name, const char *to_file_name)
193 FILE *backup_fd;
194 char *contents;
195 gsize length;
196 gboolean ret1 = TRUE;
198 if (!g_file_get_contents (from_file_name, &contents, &length, NULL))
199 return FALSE;
201 backup_fd = fopen (to_file_name, "w");
202 if (backup_fd == NULL)
204 g_free (contents);
205 return FALSE;
208 if (fwrite ((const void *) contents, 1, length, backup_fd) != length)
209 ret1 = FALSE;
211 (void) fflush (backup_fd);
212 (void) fclose (backup_fd);
214 g_free (contents);
215 return ret1;
218 /* --------------------------------------------------------------------------------------------- */
219 /*** public functions ****************************************************************************/
220 /* --------------------------------------------------------------------------------------------- */
223 is_printable (int c)
225 c &= 0xff;
227 #ifdef HAVE_CHARSET
228 /* "Display bits" is ignored, since the user controls the output
229 by setting the output codepage */
230 return is_8bit_printable (c);
231 #else
232 if (!mc_global.eight_bit_clean)
233 return is_7bit_printable (c);
235 if (mc_global.full_eight_bits)
237 return is_8bit_printable (c);
239 else
240 return is_iso_printable (c);
241 #endif /* !HAVE_CHARSET */
244 /* --------------------------------------------------------------------------------------------- */
246 * Quote the filename for the purpose of inserting it into the command
247 * line. If quote_percent is 1, replace "%" with "%%" - the percent is
248 * processed by the mc command line.
250 char *
251 name_quote (const char *s, int quote_percent)
253 char *ret, *d;
255 d = ret = g_malloc (strlen (s) * 2 + 2 + 1);
256 if (*s == '-')
258 *d++ = '.';
259 *d++ = '/';
262 for (; *s; s++, d++)
264 switch (*s)
266 case '%':
267 if (quote_percent)
268 *d++ = '%';
269 break;
270 case '\'':
271 case '\\':
272 case '\r':
273 case '\n':
274 case '\t':
275 case '"':
276 case ';':
277 case ' ':
278 case '?':
279 case '|':
280 case '[':
281 case ']':
282 case '{':
283 case '}':
284 case '<':
285 case '>':
286 case '`':
287 case '!':
288 case '$':
289 case '&':
290 case '*':
291 case '(':
292 case ')':
293 *d++ = '\\';
294 break;
295 case '~':
296 case '#':
297 if (d == ret)
298 *d++ = '\\';
299 break;
301 *d = *s;
303 *d = '\0';
304 return ret;
307 /* --------------------------------------------------------------------------------------------- */
309 char *
310 fake_name_quote (const char *s, int quote_percent)
312 (void) quote_percent;
313 return g_strdup (s);
316 /* --------------------------------------------------------------------------------------------- */
318 * path_trunc() is the same as str_trunc() but
319 * it deletes possible password from path for security
320 * reasons.
323 const char *
324 path_trunc (const char *path, size_t trunc_len)
326 vfs_path_t *vpath;
327 char *secure_path;
328 const char *ret;
330 vpath = vfs_path_from_str (path);
331 secure_path = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
332 vfs_path_free (vpath);
334 ret = str_trunc (secure_path, trunc_len);
335 g_free (secure_path);
337 return ret;
340 /* --------------------------------------------------------------------------------------------- */
342 const char *
343 size_trunc (uintmax_t size, gboolean use_si)
345 static char x[BUF_TINY];
346 uintmax_t divisor = 1;
347 const char *xtra = "";
349 if (size > 999999999UL)
351 divisor = use_si ? 1000 : 1024;
352 xtra = use_si ? "k" : "K";
354 if (size / divisor > 999999999UL)
356 divisor = use_si ? (1000 * 1000) : (1024 * 1024);
357 xtra = use_si ? "m" : "M";
359 if (size / divisor > 999999999UL)
361 divisor = use_si ? (1000 * 1000 * 1000) : (1024 * 1024 * 1024);
362 xtra = use_si ? "g" : "G";
366 g_snprintf (x, sizeof (x), "%.0f%s", 1.0 * size / divisor, xtra);
367 return x;
370 /* --------------------------------------------------------------------------------------------- */
372 const char *
373 size_trunc_sep (uintmax_t size, gboolean use_si)
375 static char x[60];
376 int count;
377 const char *p, *y;
378 char *d;
380 p = y = size_trunc (size, use_si);
381 p += strlen (p) - 1;
382 d = x + sizeof (x) - 1;
383 *d-- = '\0';
384 while (p >= y && isalpha ((unsigned char) *p))
385 *d-- = *p--;
386 for (count = 0; p >= y; count++)
388 if (count == 3)
390 *d-- = ',';
391 count = 0;
393 *d-- = *p--;
395 d++;
396 if (*d == ',')
397 d++;
398 return d;
401 /* --------------------------------------------------------------------------------------------- */
403 * Print file SIZE to BUFFER, but don't exceed LEN characters,
404 * not including trailing 0. BUFFER should be at least LEN+1 long.
405 * This function is called for every file on panels, so avoid
406 * floating point by any means.
408 * Units: size units (filesystem sizes are 1K blocks)
409 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
412 void
413 size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gboolean use_si)
415 /* Avoid taking power for every file. */
416 /* *INDENT-OFF* */
417 static const uintmax_t power10[] = {
418 /* we hope that size of uintmax_t is 4 bytes at least */
419 1ULL,
420 10ULL,
421 100ULL,
422 1000ULL,
423 10000ULL,
424 100000ULL,
425 1000000ULL,
426 10000000ULL,
427 100000000ULL,
428 1000000000ULL
429 /* maximmum 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 /* maximmum 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
467 * recalculate from 1024 base to 1000 base if units>0
468 * We can't just multiply by 1024 - that might cause overflow
469 * if off_t type is too small
471 if (use_si)
472 for (j = 0; j < units; j++)
474 uintmax_t size_remain;
476 size_remain = ((size % 125) * 1024) / 1000; /* size mod 125, recalculated */
477 size = size / 125; /* 128/125 = 1024/1000 */
478 size = size * 128; /* This will convert size from multiple of 1024 to multiple of 1000 */
479 size += size_remain; /* Re-add remainder lost by division/multiplication */
482 for (j = units; suffix[j] != NULL; j++)
484 if (size == 0)
486 if (j == units)
488 /* Empty files will print "0" even with minimal width. */
489 g_snprintf (buffer, len + 1, "0");
490 break;
493 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
494 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s",
495 (j > 1) ? (use_si ? suffix_lc[j - 1] : suffix[j - 1]) : "B");
496 break;
499 if (size < power10[len - (j > 0 ? 1 : 0)])
501 g_snprintf (buffer, len + 1, "%" PRIuMAX "%s", size, use_si ? suffix_lc[j] : suffix[j]);
502 break;
505 /* Powers of 1000 or 1024, with rounding. */
506 if (use_si)
507 size = (size + 500) / 1000;
508 else
509 size = (size + 512) >> 10;
513 /* --------------------------------------------------------------------------------------------- */
515 const char *
516 string_perm (mode_t mode_bits)
518 static char mode[11];
520 strcpy (mode, "----------");
521 if (S_ISDIR (mode_bits))
522 mode[0] = 'd';
523 if (S_ISCHR (mode_bits))
524 mode[0] = 'c';
525 if (S_ISBLK (mode_bits))
526 mode[0] = 'b';
527 if (S_ISLNK (mode_bits))
528 mode[0] = 'l';
529 if (S_ISFIFO (mode_bits))
530 mode[0] = 'p';
531 if (S_ISNAM (mode_bits))
532 mode[0] = 'n';
533 if (S_ISSOCK (mode_bits))
534 mode[0] = 's';
535 if (S_ISDOOR (mode_bits))
536 mode[0] = 'D';
537 if (ismode (mode_bits, S_IXOTH))
538 mode[9] = 'x';
539 if (ismode (mode_bits, S_IWOTH))
540 mode[8] = 'w';
541 if (ismode (mode_bits, S_IROTH))
542 mode[7] = 'r';
543 if (ismode (mode_bits, S_IXGRP))
544 mode[6] = 'x';
545 if (ismode (mode_bits, S_IWGRP))
546 mode[5] = 'w';
547 if (ismode (mode_bits, S_IRGRP))
548 mode[4] = 'r';
549 if (ismode (mode_bits, S_IXUSR))
550 mode[3] = 'x';
551 if (ismode (mode_bits, S_IWUSR))
552 mode[2] = 'w';
553 if (ismode (mode_bits, S_IRUSR))
554 mode[1] = 'r';
555 #ifdef S_ISUID
556 if (ismode (mode_bits, S_ISUID))
557 mode[3] = (mode[3] == 'x') ? 's' : 'S';
558 #endif /* S_ISUID */
559 #ifdef S_ISGID
560 if (ismode (mode_bits, S_ISGID))
561 mode[6] = (mode[6] == 'x') ? 's' : 'S';
562 #endif /* S_ISGID */
563 #ifdef S_ISVTX
564 if (ismode (mode_bits, S_ISVTX))
565 mode[9] = (mode[9] == 'x') ? 't' : 'T';
566 #endif /* S_ISVTX */
567 return mode;
570 /* --------------------------------------------------------------------------------------------- */
572 const char *
573 extension (const char *filename)
575 const char *d = strrchr (filename, '.');
576 return (d != NULL) ? d + 1 : "";
579 /* --------------------------------------------------------------------------------------------- */
581 char *
582 load_mc_home_file (const char *from, const char *filename, char **allocated_filename)
584 char *hintfile_base, *hintfile;
585 char *lang;
586 char *data;
588 hintfile_base = g_build_filename (from, filename, (char *) NULL);
589 lang = guess_message_value ();
591 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
592 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
594 /* Fall back to the two-letter language code */
595 if (lang[0] != '\0' && lang[1] != '\0')
596 lang[2] = '\0';
597 g_free (hintfile);
598 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
599 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
601 g_free (hintfile);
602 hintfile = hintfile_base;
603 g_file_get_contents (hintfile_base, &data, NULL, NULL);
607 g_free (lang);
609 if (hintfile != hintfile_base)
610 g_free (hintfile_base);
612 if (allocated_filename != NULL)
613 *allocated_filename = hintfile;
614 else
615 g_free (hintfile);
617 return data;
620 /* --------------------------------------------------------------------------------------------- */
622 const char *
623 extract_line (const char *s, const char *top)
625 static char tmp_line[BUF_MEDIUM];
626 char *t = tmp_line;
628 while (*s && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line) - 1 && s < top)
629 *t++ = *s++;
630 *t = 0;
631 return tmp_line;
634 /* --------------------------------------------------------------------------------------------- */
636 * The basename routine
639 const char *
640 x_basename (const char *s)
642 const char *url_delim, *path_sep;
644 url_delim = g_strrstr (s, VFS_PATH_URL_DELIMITER);
645 path_sep = strrchr (s, PATH_SEP);
647 if (url_delim == NULL
648 || url_delim < path_sep - strlen (VFS_PATH_URL_DELIMITER)
649 || url_delim - s + strlen (VFS_PATH_URL_DELIMITER) < strlen (s))
651 /* avoid trailing PATH_SEP, if present */
652 if (s[strlen (s) - 1] == PATH_SEP)
654 while (--path_sep > s && *path_sep != PATH_SEP);
655 return (path_sep != s) ? path_sep + 1 : s;
657 else
658 return (path_sep != NULL) ? path_sep + 1 : s;
661 while (--url_delim > s && *url_delim != PATH_SEP);
662 while (--url_delim > s && *url_delim != PATH_SEP);
664 return (url_delim == s) ? s : url_delim + 1;
667 /* --------------------------------------------------------------------------------------------- */
669 const char *
670 unix_error_string (int error_num)
672 static char buffer[BUF_LARGE];
673 gchar *strerror_currentlocale;
675 strerror_currentlocale = g_locale_from_utf8 (g_strerror (error_num), -1, NULL, NULL, NULL);
676 g_snprintf (buffer, sizeof (buffer), "%s (%d)", strerror_currentlocale, error_num);
677 g_free (strerror_currentlocale);
679 return buffer;
682 /* --------------------------------------------------------------------------------------------- */
684 const char *
685 skip_separators (const char *s)
687 const char *su = s;
689 for (; *su; str_cnext_char (&su))
690 if (*su != ' ' && *su != '\t' && *su != ',')
691 break;
693 return su;
696 /* --------------------------------------------------------------------------------------------- */
698 const char *
699 skip_numbers (const char *s)
701 const char *su = s;
703 for (; *su; str_cnext_char (&su))
704 if (!str_isdigit (su))
705 break;
707 return su;
710 /* --------------------------------------------------------------------------------------------- */
712 * Remove all control sequences from the argument string. We define
713 * "control sequence", in a sort of pidgin BNF, as follows:
715 * control-seq = Esc non-'['
716 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
718 * This scheme works for all the terminals described in my termcap /
719 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
720 * terminals. If I hear from a single person who uses such a terminal
721 * with MC, I'll be glad to add support for it. (Dugan)
722 * Non-printable characters are also removed.
725 char *
726 strip_ctrl_codes (char *s)
728 char *w; /* Current position where the stripped data is written */
729 char *r; /* Current position where the original data is read */
730 char *n;
732 if (!s)
733 return 0;
735 for (w = s, r = s; *r;)
737 if (*r == ESC_CHAR)
739 /* Skip the control sequence's arguments */ ;
740 /* '(' need to avoid strange 'B' letter in *Suse (if mc runs under root user) */
741 if (*(++r) == '[' || *r == '(')
743 /* strchr() matches trailing binary 0 */
744 while (*(++r) && strchr ("0123456789;?", *r));
746 else if (*r == ']')
749 * Skip xterm's OSC (Operating System Command)
750 * http://www.xfree86.org/current/ctlseqs.html
751 * OSC P s ; P t ST
752 * OSC P s ; P t BEL
754 char *new_r = r;
756 for (; *new_r; ++new_r)
758 switch (*new_r)
760 /* BEL */
761 case '\a':
762 r = new_r;
763 goto osc_out;
764 case ESC_CHAR:
765 /* ST */
766 if (*(new_r + 1) == '\\')
768 r = new_r + 1;
769 goto osc_out;
773 osc_out:;
777 * Now we are at the last character of the sequence.
778 * Skip it unless it's binary 0.
780 if (*r)
781 r++;
782 continue;
785 n = str_get_next_char (r);
786 if (str_isprint (r))
788 memmove (w, r, n - r);
789 w += n - r;
791 r = n;
793 *w = 0;
794 return s;
797 /* --------------------------------------------------------------------------------------------- */
799 enum compression_type
800 get_compression_type (int fd, const char *name)
802 unsigned char magic[16];
803 size_t str_len;
805 /* Read the magic signature */
806 if (mc_read (fd, (char *) magic, 4) != 4)
807 return COMPRESSION_NONE;
809 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
810 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236))
812 return COMPRESSION_GZIP;
815 /* PKZIP_MAGIC */
816 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003 && magic[3] == 004)
818 /* Read compression type */
819 mc_lseek (fd, 8, SEEK_SET);
820 if (mc_read (fd, (char *) magic, 2) != 2)
821 return COMPRESSION_NONE;
823 /* Gzip can handle only deflated (8) or stored (0) files */
824 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
825 return COMPRESSION_NONE;
827 /* Compatible with gzip */
828 return COMPRESSION_GZIP;
831 /* PACK_MAGIC and LZH_MAGIC and compress magic */
832 if (magic[0] == 037 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235))
834 /* Compatible with gzip */
835 return COMPRESSION_GZIP;
838 /* BZIP and BZIP2 files */
839 if ((magic[0] == 'B') && (magic[1] == 'Z') && (magic[3] >= '1') && (magic[3] <= '9'))
841 switch (magic[2])
843 case '0':
844 return COMPRESSION_BZIP;
845 case 'h':
846 return COMPRESSION_BZIP2;
850 /* Support for LZMA (only utils format with magic in header).
851 * This is the default format of LZMA utils 4.32.1 and later. */
853 if (mc_read (fd, (char *) magic + 4, 2) != 2)
854 return COMPRESSION_NONE;
856 /* LZMA utils format */
857 if (magic[0] == 0xFF
858 && magic[1] == 'L'
859 && magic[2] == 'Z' && magic[3] == 'M' && magic[4] == 'A' && magic[5] == 0x00)
860 return COMPRESSION_LZMA;
862 /* XZ compression magic */
863 if (magic[0] == 0xFD
864 && magic[1] == 0x37
865 && magic[2] == 0x7A && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00)
866 return COMPRESSION_XZ;
868 str_len = strlen (name);
869 /* HACK: we must belive to extention of LZMA file :) ... */
870 if ((str_len > 5 && strcmp (&name[str_len - 5], ".lzma") == 0) ||
871 (str_len > 4 && strcmp (&name[str_len - 4], ".tlz") == 0))
872 return COMPRESSION_LZMA;
874 return COMPRESSION_NONE;
877 /* --------------------------------------------------------------------------------------------- */
879 const char *
880 decompress_extension (int type)
882 switch (type)
884 case COMPRESSION_GZIP:
885 return "/ugz" VFS_PATH_URL_DELIMITER;
886 case COMPRESSION_BZIP:
887 return "/ubz" VFS_PATH_URL_DELIMITER;
888 case COMPRESSION_BZIP2:
889 return "/ubz2" VFS_PATH_URL_DELIMITER;
890 case COMPRESSION_LZMA:
891 return "/ulzma" VFS_PATH_URL_DELIMITER;
892 case COMPRESSION_XZ:
893 return "/uxz" VFS_PATH_URL_DELIMITER;
895 /* Should never reach this place */
896 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
897 return 0;
900 /* --------------------------------------------------------------------------------------------- */
902 void
903 wipe_password (char *passwd)
905 char *p = passwd;
907 if (!p)
908 return;
909 for (; *p; p++)
910 *p = 0;
911 g_free (passwd);
914 /* --------------------------------------------------------------------------------------------- */
916 * Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key
917 * @returns a newly allocated string
920 char *
921 convert_controls (const char *p)
923 char *valcopy = g_strdup (p);
924 char *q;
926 /* Parse the escape special character */
927 for (q = valcopy; *p;)
929 if (*p == '\\')
931 p++;
932 if ((*p == 'e') || (*p == 'E'))
934 p++;
935 *q++ = ESC_CHAR;
938 else
940 if (*p == '^')
942 p++;
943 if (*p == '^')
944 *q++ = *p++;
945 else
947 char c = (*p | 0x20);
948 if (c >= 'a' && c <= 'z')
950 *q++ = c - 'a' + 1;
951 p++;
953 else if (*p)
954 p++;
957 else
958 *q++ = *p++;
961 *q = 0;
962 return valcopy;
965 /* --------------------------------------------------------------------------------------------- */
967 * Finds out a relative path from first to second, i.e. goes as many ..
968 * as needed up in first and then goes down using second
971 char *
972 diff_two_paths (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
974 char *p, *q, *r, *s, *buf = NULL;
975 int i, j, prevlen = -1, currlen;
976 char *my_first = NULL, *my_second = NULL;
978 my_first = resolve_symlinks (vpath1);
979 if (my_first == NULL)
980 goto ret;
982 my_second = resolve_symlinks (vpath2);
983 if (my_second == NULL)
984 goto ret;
986 for (j = 0; j < 2; j++)
988 p = my_first;
989 q = my_second;
990 for (;;)
992 r = strchr (p, PATH_SEP);
993 s = strchr (q, PATH_SEP);
994 if (!r || !s)
995 break;
996 *r = 0;
997 *s = 0;
998 if (strcmp (p, q))
1000 *r = PATH_SEP;
1001 *s = PATH_SEP;
1002 break;
1004 else
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++);
1014 currlen = (i + 1) * 3 + strlen (q) + 1;
1015 if (j)
1017 if (currlen < prevlen)
1018 g_free (buf);
1019 else
1020 goto ret;
1022 p = buf = g_malloc (currlen);
1023 prevlen = currlen;
1024 for (; i >= 0; i--, p += 3)
1025 strcpy (p, "../");
1026 strcpy (p, q);
1029 ret:
1030 g_free (my_first);
1031 g_free (my_second);
1032 return buf;
1035 /* --------------------------------------------------------------------------------------------- */
1037 * Append text to GList, remove all entries with the same text
1040 GList *
1041 list_append_unique (GList * list, char *text)
1043 GList *lc_link;
1046 * Go to the last position and traverse the list backwards
1047 * starting from the second last entry to make sure that we
1048 * are not removing the current link.
1050 list = g_list_append (list, text);
1051 list = g_list_last (list);
1052 lc_link = g_list_previous (list);
1054 while (lc_link != NULL)
1056 GList *newlink;
1058 newlink = g_list_previous (lc_link);
1059 if (strcmp ((char *) lc_link->data, text) == 0)
1061 GList *tmp;
1063 g_free (lc_link->data);
1064 tmp = g_list_remove_link (list, lc_link);
1065 g_list_free_1 (lc_link);
1067 lc_link = newlink;
1070 return list;
1073 /* --------------------------------------------------------------------------------------------- */
1075 * Read and restore position for the given filename.
1076 * If there is no stored data, return line 1 and col 0.
1079 void
1080 load_file_position (const vfs_path_t * filename_vpath, long *line, long *column, off_t * offset,
1081 GArray ** bookmarks)
1083 char *fn;
1084 FILE *f;
1085 char buf[MC_MAXPATHLEN + 100];
1086 const size_t len = vfs_path_len (filename_vpath);
1087 char *filename;
1089 /* defaults */
1090 *line = 1;
1091 *column = 0;
1092 *offset = 0;
1094 /* open file with positions */
1095 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1096 f = fopen (fn, "r");
1097 g_free (fn);
1098 if (f == NULL)
1099 return;
1101 /* prepare array for serialized bookmarks */
1102 *bookmarks = g_array_sized_new (FALSE, FALSE, sizeof (size_t), MAX_SAVED_BOOKMARKS);
1103 filename = vfs_path_to_str (filename_vpath);
1105 while (fgets (buf, sizeof (buf), f) != NULL)
1107 const char *p;
1108 gchar **pos_tokens;
1110 /* check if the filename matches the beginning of string */
1111 if (strncmp (buf, filename, len) != 0)
1112 continue;
1114 /* followed by single space */
1115 if (buf[len] != ' ')
1116 continue;
1118 /* and string without spaces */
1119 p = &buf[len + 1];
1120 if (strchr (p, ' ') != NULL)
1121 continue;
1123 pos_tokens = g_strsplit (p, ";", 3 + MAX_SAVED_BOOKMARKS);
1124 if (pos_tokens[0] == NULL)
1126 *line = 1;
1127 *column = 0;
1128 *offset = 0;
1130 else
1132 *line = strtol (pos_tokens[0], NULL, 10);
1133 if (pos_tokens[1] == NULL)
1135 *column = 0;
1136 *offset = 0;
1138 else
1140 *column = strtol (pos_tokens[1], NULL, 10);
1141 if (pos_tokens[2] == NULL)
1142 *offset = 0;
1143 else
1145 size_t i;
1147 *offset = strtoll (pos_tokens[2], NULL, 10);
1149 for (i = 0; i < MAX_SAVED_BOOKMARKS && pos_tokens[3 + i] != NULL; i++)
1151 size_t val;
1153 val = strtoul (pos_tokens[3 + i], NULL, 10);
1154 g_array_append_val (*bookmarks, val);
1160 g_strfreev (pos_tokens);
1163 g_free (filename);
1164 fclose (f);
1167 /* --------------------------------------------------------------------------------------------- */
1169 * Save position for the given file
1172 void
1173 save_file_position (const vfs_path_t * filename_vpath, long line, long column, off_t offset,
1174 GArray * bookmarks)
1176 static size_t filepos_max_saved_entries = 0;
1177 char *fn, *tmp_fn;
1178 FILE *f, *tmp_f;
1179 char buf[MC_MAXPATHLEN + 100];
1180 size_t i;
1181 const size_t len = vfs_path_len (filename_vpath);
1182 gboolean src_error = FALSE;
1183 char *filename;
1185 if (filepos_max_saved_entries == 0)
1186 filepos_max_saved_entries = mc_config_get_int (mc_main_config, CONFIG_APP_SECTION,
1187 "filepos_max_saved_entries", 1024);
1189 fn = mc_config_get_full_path (MC_FILEPOS_FILE);
1190 if (fn == NULL)
1191 goto early_error;
1193 mc_util_make_backup_if_possible (fn, TMP_SUFFIX);
1195 /* open file */
1196 f = fopen (fn, "w");
1197 if (f == NULL)
1198 goto open_target_error;
1200 tmp_fn = g_strdup_printf ("%s" TMP_SUFFIX, fn);
1201 tmp_f = fopen (tmp_fn, "r");
1202 if (tmp_f == NULL)
1204 src_error = TRUE;
1205 goto open_source_error;
1208 filename = vfs_path_to_str (filename_vpath);
1209 /* put the new record */
1210 if (line != 1 || column != 0 || bookmarks != NULL)
1212 if (fprintf (f, "%s %ld;%ld;%" PRIuMAX, filename, line, column, (uintmax_t) offset) < 0)
1213 goto write_position_error;
1214 if (bookmarks != NULL)
1215 for (i = 0; i < bookmarks->len && i < MAX_SAVED_BOOKMARKS; i++)
1216 if (fprintf (f, ";%zu", g_array_index (bookmarks, size_t, i)) < 0)
1217 goto write_position_error;
1219 if (fprintf (f, "\n") < 0)
1220 goto write_position_error;
1223 i = 1;
1224 while (fgets (buf, sizeof (buf), tmp_f) != NULL)
1226 if (buf[len] == ' ' && strncmp (buf, filename, len) == 0
1227 && strchr (&buf[len + 1], ' ') == NULL)
1228 continue;
1230 fprintf (f, "%s", buf);
1231 if (++i > filepos_max_saved_entries)
1232 break;
1235 write_position_error:
1236 g_free (filename);
1237 fclose (tmp_f);
1238 open_source_error:
1239 g_free (tmp_fn);
1240 fclose (f);
1241 if (src_error)
1242 mc_util_restore_from_backup_if_possible (fn, TMP_SUFFIX);
1243 else
1244 mc_util_unlink_backup_if_possible (fn, TMP_SUFFIX);
1245 open_target_error:
1246 g_free (fn);
1247 early_error:
1248 if (bookmarks != NULL)
1249 g_array_free (bookmarks, TRUE);
1252 /* --------------------------------------------------------------------------------------------- */
1254 extern int
1255 ascii_alpha_to_cntrl (int ch)
1257 if ((ch >= ASCII_A && ch <= ASCII_Z) || (ch >= ASCII_a && ch <= ASCII_z))
1259 ch &= 0x1f;
1261 return ch;
1264 /* --------------------------------------------------------------------------------------------- */
1266 const char *
1267 Q_ (const char *s)
1269 const char *result, *sep;
1271 result = _(s);
1272 sep = strchr (result, '|');
1273 return (sep != NULL) ? sep + 1 : result;
1276 /* --------------------------------------------------------------------------------------------- */
1278 gboolean
1279 mc_util_make_backup_if_possible (const char *file_name, const char *backup_suffix)
1281 struct stat stat_buf;
1282 char *backup_path;
1283 gboolean ret;
1284 if (!exist_file (file_name))
1285 return FALSE;
1287 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1289 if (backup_path == NULL)
1290 return FALSE;
1292 ret = mc_util_write_backup_content (file_name, backup_path);
1294 if (ret)
1296 /* Backup file will have same ownership with main file. */
1297 if (stat (file_name, &stat_buf) == 0)
1298 chmod (backup_path, stat_buf.st_mode);
1299 else
1300 chmod (backup_path, S_IRUSR | S_IWUSR);
1303 g_free (backup_path);
1305 return ret;
1308 /* --------------------------------------------------------------------------------------------- */
1310 gboolean
1311 mc_util_restore_from_backup_if_possible (const char *file_name, const char *backup_suffix)
1313 gboolean ret;
1314 char *backup_path;
1316 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1317 if (backup_path == NULL)
1318 return FALSE;
1320 ret = mc_util_write_backup_content (backup_path, file_name);
1321 g_free (backup_path);
1323 return ret;
1326 /* --------------------------------------------------------------------------------------------- */
1328 gboolean
1329 mc_util_unlink_backup_if_possible (const char *file_name, const char *backup_suffix)
1331 char *backup_path;
1333 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1334 if (backup_path == NULL)
1335 return FALSE;
1337 if (exist_file (backup_path))
1339 vfs_path_t *vpath;
1341 vpath = vfs_path_from_str (backup_path);
1342 mc_unlink (vpath);
1343 vfs_path_free (vpath);
1346 g_free (backup_path);
1347 return TRUE;
1350 /* --------------------------------------------------------------------------------------------- */
1352 * partly taken from dcigettext.c, returns "" for default locale
1353 * value should be freed by calling function g_free()
1356 char *
1357 guess_message_value (void)
1359 static const char *const var[] = {
1360 /* Setting of LC_ALL overwrites all other. */
1361 /* Do not use LANGUAGE for check user locale and drowing hints */
1362 "LC_ALL",
1363 /* Next comes the name of the desired category. */
1364 "LC_MESSAGES",
1365 /* Last possibility is the LANG environment variable. */
1366 "LANG",
1367 /* NULL exit loops */
1368 NULL
1371 unsigned i = 0;
1372 const char *locale = NULL;
1374 while (var[i] != NULL)
1376 locale = getenv (var[i]);
1377 if (locale != NULL && locale[0] != '\0')
1378 break;
1379 i++;
1382 if (locale == NULL)
1383 locale = "";
1385 return g_strdup (locale);
1388 /* --------------------------------------------------------------------------------------------- */