Fixed searching the start of word
[midnight-commander.git] / lib / util.c
blob1aef248bd2dba1d4fabd3c1b390a720df33febc2
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/mc-vfs/vfs.h"
48 #include "lib/strutil.h"
49 #include "lib/util.h"
51 #include "src/filemanager/filegui.h"
52 #include "src/filemanager/file.h" /* copy_file_file() */
53 #include "src/main.h" /* home_dir, eight_bit_clean */
55 /*** global variables ****************************************************************************/
57 /*** file scope macro definitions ****************************************************************/
59 #define ismode(n,m) ((n & m) == m)
61 /* Number of attempts to create a temporary file */
62 #ifndef TMP_MAX
63 #define TMP_MAX 16384
64 #endif /* !TMP_MAX */
66 #define TMP_SUFFIX ".tmp"
68 #define ASCII_A (0x40 + 1)
69 #define ASCII_Z (0x40 + 26)
70 #define ASCII_a (0x60 + 1)
71 #define ASCII_z (0x60 + 26)
73 /*** file scope type declarations ****************************************************************/
75 /*** file scope variables ************************************************************************/
77 /*** file scope functions ************************************************************************/
78 /* --------------------------------------------------------------------------------------------- */
80 static inline int
81 is_7bit_printable (unsigned char c)
83 return (c > 31 && c < 127);
86 /* --------------------------------------------------------------------------------------------- */
88 static inline int
89 is_iso_printable (unsigned char c)
91 return ((c > 31 && c < 127) || c >= 160);
94 /* --------------------------------------------------------------------------------------------- */
96 static inline int
97 is_8bit_printable (unsigned char c)
99 /* "Full 8 bits output" doesn't work on xterm */
100 if (xterm_flag)
101 return is_iso_printable (c);
103 return (c > 31 && c != 127 && c != 155);
106 /* --------------------------------------------------------------------------------------------- */
108 static char *
109 resolve_symlinks (const char *path)
111 char *buf, *buf2, *q, *r, c;
112 int len;
113 struct stat mybuf;
114 const char *p;
116 if (*path != PATH_SEP)
117 return NULL;
118 r = buf = g_malloc (MC_MAXPATHLEN);
119 buf2 = g_malloc (MC_MAXPATHLEN);
120 *r++ = PATH_SEP;
121 *r = 0;
122 p = path;
123 for (;;)
125 q = strchr (p + 1, PATH_SEP);
126 if (!q)
128 q = strchr (p + 1, 0);
129 if (q == p + 1)
130 break;
132 c = *q;
133 *q = 0;
134 if (mc_lstat (path, &mybuf) < 0)
136 g_free (buf);
137 g_free (buf2);
138 *q = c;
139 return NULL;
141 if (!S_ISLNK (mybuf.st_mode))
142 strcpy (r, p + 1);
143 else
145 len = mc_readlink (path, buf2, MC_MAXPATHLEN - 1);
146 if (len < 0)
148 g_free (buf);
149 g_free (buf2);
150 *q = c;
151 return NULL;
153 buf2[len] = 0;
154 if (*buf2 == PATH_SEP)
155 strcpy (buf, buf2);
156 else
157 strcpy (r, buf2);
159 canonicalize_pathname (buf);
160 r = strchr (buf, 0);
161 if (!*r || *(r - 1) != PATH_SEP)
163 *r++ = PATH_SEP;
164 *r = 0;
166 *q = c;
167 p = q;
168 if (!c)
169 break;
171 if (!*buf)
172 strcpy (buf, PATH_SEP_STR);
173 else if (*(r - 1) == PATH_SEP && r != buf + 1)
174 *(r - 1) = 0;
175 g_free (buf2);
176 return buf;
179 /* --------------------------------------------------------------------------------------------- */
181 static gboolean
182 mc_util_write_backup_content (const char *from_file_name, const char *to_file_name)
184 FILE *backup_fd;
185 char *contents;
186 gsize length;
187 gboolean ret1 = TRUE;
189 if (!g_file_get_contents (from_file_name, &contents, &length, NULL))
190 return FALSE;
192 backup_fd = fopen (to_file_name, "w");
193 if (backup_fd == NULL)
195 g_free (contents);
196 return FALSE;
199 if (fwrite ((const void *) contents, 1, length, backup_fd) != length)
200 ret1 = FALSE;
202 int ret2;
203 ret2 = fflush (backup_fd);
204 ret2 = fclose (backup_fd);
206 g_free (contents);
207 return ret1;
210 /* --------------------------------------------------------------------------------------------- */
211 /*** public functions ****************************************************************************/
212 /* --------------------------------------------------------------------------------------------- */
215 is_printable (int c)
217 c &= 0xff;
219 #ifdef HAVE_CHARSET
220 /* "Display bits" is ignored, since the user controls the output
221 by setting the output codepage */
222 return is_8bit_printable (c);
223 #else
224 if (!eight_bit_clean)
225 return is_7bit_printable (c);
227 if (full_eight_bits)
229 return is_8bit_printable (c);
231 else
232 return is_iso_printable (c);
233 #endif /* !HAVE_CHARSET */
236 /* --------------------------------------------------------------------------------------------- */
238 * Quote the filename for the purpose of inserting it into the command
239 * line. If quote_percent is 1, replace "%" with "%%" - the percent is
240 * processed by the mc command line.
242 char *
243 name_quote (const char *s, int quote_percent)
245 char *ret, *d;
247 d = ret = g_malloc (strlen (s) * 2 + 2 + 1);
248 if (*s == '-')
250 *d++ = '.';
251 *d++ = '/';
254 for (; *s; s++, d++)
256 switch (*s)
258 case '%':
259 if (quote_percent)
260 *d++ = '%';
261 break;
262 case '\'':
263 case '\\':
264 case '\r':
265 case '\n':
266 case '\t':
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 case '&':
282 case '*':
283 case '(':
284 case ')':
285 *d++ = '\\';
286 break;
287 case '~':
288 case '#':
289 if (d == ret)
290 *d++ = '\\';
291 break;
293 *d = *s;
295 *d = '\0';
296 return ret;
299 /* --------------------------------------------------------------------------------------------- */
301 char *
302 fake_name_quote (const char *s, int quote_percent)
304 (void) quote_percent;
305 return g_strdup (s);
308 /* --------------------------------------------------------------------------------------------- */
310 * path_trunc() is the same as str_trunc() but
311 * it deletes possible password from path for security
312 * reasons.
315 const char *
316 path_trunc (const char *path, size_t trunc_len)
318 char *secure_path = strip_password (g_strdup (path), 1);
320 const char *ret = str_trunc (secure_path, trunc_len);
321 g_free (secure_path);
323 return ret;
326 /* --------------------------------------------------------------------------------------------- */
328 const char *
329 size_trunc (uintmax_t size, gboolean use_si)
331 static char x[BUF_TINY];
332 uintmax_t divisor = 1;
333 const char *xtra = "";
335 if (size > 999999999UL)
337 divisor = use_si ? 1000 : 1024;
338 xtra = use_si ? "k" : "K";
340 if (size / divisor > 999999999UL)
342 divisor = use_si ? (1000 * 1000) : (1024 * 1024);
343 xtra = use_si ? "m" : "M";
345 if (size / divisor > 999999999UL)
347 divisor = use_si ? (1000 * 1000 * 1000) : (1024 * 1024 * 1024);
348 xtra = use_si ? "g" : "G";
352 g_snprintf (x, sizeof (x), "%.0f%s", 1.0 * size / divisor, xtra);
353 return x;
356 /* --------------------------------------------------------------------------------------------- */
358 const char *
359 size_trunc_sep (uintmax_t size, gboolean use_si)
361 static char x[60];
362 int count;
363 const char *p, *y;
364 char *d;
366 p = y = size_trunc (size, use_si);
367 p += strlen (p) - 1;
368 d = x + sizeof (x) - 1;
369 *d-- = '\0';
370 while (p >= y && isalpha ((unsigned char) *p))
371 *d-- = *p--;
372 for (count = 0; p >= y; count++)
374 if (count == 3)
376 *d-- = ',';
377 count = 0;
379 *d-- = *p--;
381 d++;
382 if (*d == ',')
383 d++;
384 return d;
387 /* --------------------------------------------------------------------------------------------- */
389 * Print file SIZE to BUFFER, but don't exceed LEN characters,
390 * not including trailing 0. BUFFER should be at least LEN+1 long.
391 * This function is called for every file on panels, so avoid
392 * floating point by any means.
394 * Units: size units (filesystem sizes are 1K blocks)
395 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
398 void
399 size_trunc_len (char *buffer, unsigned int len, uintmax_t size, int units, gboolean use_si)
401 /* Avoid taking power for every file. */
402 static const uintmax_t power10[] = {
403 /* we hope that size of uintmax_t is 4 bytes at least */
404 1ULL,
405 10ULL,
406 100ULL,
407 1000ULL,
408 10000ULL,
409 100000ULL,
410 1000000ULL,
411 10000000ULL,
412 100000000ULL,
413 1000000000ULL
414 /* maximmum value of uintmax_t (in case of 4 bytes) is
415 4294967295
417 #if SIZEOF_UINTMAX_T == 8
419 10000000000ULL,
420 100000000000ULL,
421 1000000000000ULL,
422 10000000000000ULL,
423 100000000000000ULL,
424 1000000000000000ULL,
425 10000000000000000ULL,
426 100000000000000000ULL,
427 1000000000000000000ULL,
428 10000000000000000000ULL
429 /* maximmum value of uintmax_t (in case of 8 bytes) is
430 18447644073710439615
432 #endif
434 static const char *const suffix[] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL };
435 static const char *const suffix_lc[] = { "", "k", "m", "g", "t", "p", "e", "z", "y", NULL };
436 int j = 0;
437 int size_remain;
439 if (len == 0)
440 len = 9;
441 #if SIZEOF_UINTMAX_T == 8
442 /* 20 decimal digits are required to represent 8 bytes */
443 else if (len > 19)
444 len = 19;
445 #else
446 /* 10 decimal digits are required to represent 4 bytes */
447 else if (len > 9)
448 len = 9;
449 #endif
452 * recalculate from 1024 base to 1000 base if units>0
453 * We can't just multiply by 1024 - that might cause overflow
454 * if off_t type is too small
456 if (use_si)
457 for (j = 0; j < units; j++)
459 size_remain = ((size % 125) * 1024) / 1000; /* size mod 125, recalculated */
460 size = size / 125; /* 128/125 = 1024/1000 */
461 size = size * 128; /* This will convert size from multiple of 1024 to multiple of 1000 */
462 size += size_remain; /* Re-add remainder lost by division/multiplication */
465 for (j = units; suffix[j] != NULL; j++)
467 if (size == 0)
469 if (j == units)
471 /* Empty files will print "0" even with minimal width. */
472 g_snprintf (buffer, len + 1, "0");
473 break;
476 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
477 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s",
478 (j > 1) ? (use_si ? suffix_lc[j - 1] : suffix[j - 1]) : "B");
479 break;
482 if (size < power10[len - (j > 0 ? 1 : 0)])
484 g_snprintf (buffer, len + 1, "%" PRIuMAX "%s", size, use_si ? suffix_lc[j] : suffix[j]);
485 break;
488 /* Powers of 1000 or 1024, with rounding. */
489 if (use_si)
490 size = (size + 500) / 1000;
491 else
492 size = (size + 512) >> 10;
496 /* --------------------------------------------------------------------------------------------- */
498 const char *
499 string_perm (mode_t mode_bits)
501 static char mode[11];
503 strcpy (mode, "----------");
504 if (S_ISDIR (mode_bits))
505 mode[0] = 'd';
506 if (S_ISCHR (mode_bits))
507 mode[0] = 'c';
508 if (S_ISBLK (mode_bits))
509 mode[0] = 'b';
510 if (S_ISLNK (mode_bits))
511 mode[0] = 'l';
512 if (S_ISFIFO (mode_bits))
513 mode[0] = 'p';
514 if (S_ISNAM (mode_bits))
515 mode[0] = 'n';
516 if (S_ISSOCK (mode_bits))
517 mode[0] = 's';
518 if (S_ISDOOR (mode_bits))
519 mode[0] = 'D';
520 if (ismode (mode_bits, S_IXOTH))
521 mode[9] = 'x';
522 if (ismode (mode_bits, S_IWOTH))
523 mode[8] = 'w';
524 if (ismode (mode_bits, S_IROTH))
525 mode[7] = 'r';
526 if (ismode (mode_bits, S_IXGRP))
527 mode[6] = 'x';
528 if (ismode (mode_bits, S_IWGRP))
529 mode[5] = 'w';
530 if (ismode (mode_bits, S_IRGRP))
531 mode[4] = 'r';
532 if (ismode (mode_bits, S_IXUSR))
533 mode[3] = 'x';
534 if (ismode (mode_bits, S_IWUSR))
535 mode[2] = 'w';
536 if (ismode (mode_bits, S_IRUSR))
537 mode[1] = 'r';
538 #ifdef S_ISUID
539 if (ismode (mode_bits, S_ISUID))
540 mode[3] = (mode[3] == 'x') ? 's' : 'S';
541 #endif /* S_ISUID */
542 #ifdef S_ISGID
543 if (ismode (mode_bits, S_ISGID))
544 mode[6] = (mode[6] == 'x') ? 's' : 'S';
545 #endif /* S_ISGID */
546 #ifdef S_ISVTX
547 if (ismode (mode_bits, S_ISVTX))
548 mode[9] = (mode[9] == 'x') ? 't' : 'T';
549 #endif /* S_ISVTX */
550 return mode;
553 /* --------------------------------------------------------------------------------------------- */
555 * p: string which might contain an url with a password (this parameter is
556 * modified in place).
557 * has_prefix = 0: The first parameter is an url without a prefix
558 * (user[:pass]@]machine[:port][remote-dir). Delete
559 * the password.
560 * has_prefix = 1: Search p for known url prefixes. If found delete
561 * the password from the url.
562 * Caveat: only the first url is found
565 char *
566 strip_password (char *p, int has_prefix)
568 static const struct
570 const char *name;
571 size_t len;
572 } prefixes[] =
574 /* *INDENT-OFF* */
575 { "/#ftp:", 6 },
576 { "ftp://", 6 },
577 { "/#smb:", 6 },
578 { "smb://", 6 },
579 { "/#sh:", 5 },
580 { "sh://", 5 },
581 { "ssh://", 6 }
582 /* *INDENT-ON* */
585 char *at, *inner_colon, *dir;
586 size_t i;
587 char *result = p;
589 for (i = 0; i < sizeof (prefixes) / sizeof (prefixes[0]); i++)
591 char *q;
593 if (has_prefix)
595 q = strstr (p, prefixes[i].name);
596 if (q == NULL)
597 continue;
598 else
599 p = q + prefixes[i].len;
602 dir = strchr (p, PATH_SEP);
603 if (dir != NULL)
604 *dir = '\0';
606 /* search for any possible user */
607 at = strrchr (p, '@');
609 if (dir)
610 *dir = PATH_SEP;
612 /* We have a username */
613 if (at)
615 inner_colon = memchr (p, ':', at - p);
616 if (inner_colon)
617 memmove (inner_colon, at, strlen (at) + 1);
619 break;
621 return (result);
624 /* --------------------------------------------------------------------------------------------- */
626 const char *
627 strip_home_and_password (const char *dir)
629 size_t len;
630 static char newdir[MC_MAXPATHLEN];
632 len = strlen (home_dir);
633 if (home_dir != NULL && strncmp (dir, home_dir, len) == 0 &&
634 (dir[len] == PATH_SEP || dir[len] == '\0'))
636 newdir[0] = '~';
637 g_strlcpy (&newdir[1], &dir[len], sizeof (newdir) - 1);
638 return newdir;
641 /* We do not strip homes in /#ftp tree, I do not like ~'s there
642 (see ftpfs.c why) */
643 g_strlcpy (newdir, dir, sizeof (newdir));
644 strip_password (newdir, 1);
645 return newdir;
648 /* --------------------------------------------------------------------------------------------- */
650 const char *
651 extension (const char *filename)
653 const char *d = strrchr (filename, '.');
654 return (d != NULL) ? d + 1 : "";
657 /* --------------------------------------------------------------------------------------------- */
660 check_for_default (const char *default_file, const char *file)
662 if (!exist_file (file))
664 FileOpContext *ctx;
665 FileOpTotalContext *tctx;
667 if (!exist_file (default_file))
668 return -1;
670 ctx = file_op_context_new (OP_COPY);
671 tctx = file_op_total_context_new ();
672 file_op_context_create_ui (ctx, 0, FALSE);
673 copy_file_file (tctx, ctx, default_file, file);
674 file_op_total_context_destroy (tctx);
675 file_op_context_destroy (ctx);
678 return 0;
681 /* --------------------------------------------------------------------------------------------- */
683 char *
684 load_mc_home_file (const char *from, const char *filename, char **allocated_filename)
686 char *hintfile_base, *hintfile;
687 char *lang;
688 char *data;
690 hintfile_base = g_build_filename (from, filename, (char *) NULL);
691 lang = guess_message_value ();
693 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
694 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
696 /* Fall back to the two-letter language code */
697 if (lang[0] != '\0' && lang[1] != '\0')
698 lang[2] = '\0';
699 g_free (hintfile);
700 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
701 if (!g_file_get_contents (hintfile, &data, NULL, NULL))
703 g_free (hintfile);
704 hintfile = hintfile_base;
705 g_file_get_contents (hintfile_base, &data, NULL, NULL);
709 g_free (lang);
711 if (hintfile != hintfile_base)
712 g_free (hintfile_base);
714 if (allocated_filename != NULL)
715 *allocated_filename = hintfile;
716 else
717 g_free (hintfile);
719 return data;
722 /* --------------------------------------------------------------------------------------------- */
724 const char *
725 extract_line (const char *s, const char *top)
727 static char tmp_line[BUF_MEDIUM];
728 char *t = tmp_line;
730 while (*s && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line) - 1 && s < top)
731 *t++ = *s++;
732 *t = 0;
733 return tmp_line;
736 /* --------------------------------------------------------------------------------------------- */
738 * The basename routine
741 const char *
742 x_basename (const char *s)
744 const char *where;
745 return ((where = strrchr (s, PATH_SEP))) ? where + 1 : s;
748 /* --------------------------------------------------------------------------------------------- */
750 const char *
751 unix_error_string (int error_num)
753 static char buffer[BUF_LARGE];
754 gchar *strerror_currentlocale;
756 strerror_currentlocale = g_locale_from_utf8 (g_strerror (error_num), -1, NULL, NULL, NULL);
757 g_snprintf (buffer, sizeof (buffer), "%s (%d)", strerror_currentlocale, error_num);
758 g_free (strerror_currentlocale);
760 return buffer;
763 /* --------------------------------------------------------------------------------------------- */
765 const char *
766 skip_separators (const char *s)
768 const char *su = s;
770 for (; *su; str_cnext_char (&su))
771 if (*su != ' ' && *su != '\t' && *su != ',')
772 break;
774 return su;
777 /* --------------------------------------------------------------------------------------------- */
779 const char *
780 skip_numbers (const char *s)
782 const char *su = s;
784 for (; *su; str_cnext_char (&su))
785 if (!str_isdigit (su))
786 break;
788 return su;
791 /* --------------------------------------------------------------------------------------------- */
793 * Remove all control sequences from the argument string. We define
794 * "control sequence", in a sort of pidgin BNF, as follows:
796 * control-seq = Esc non-'['
797 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
799 * This scheme works for all the terminals described in my termcap /
800 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
801 * terminals. If I hear from a single person who uses such a terminal
802 * with MC, I'll be glad to add support for it. (Dugan)
803 * Non-printable characters are also removed.
806 char *
807 strip_ctrl_codes (char *s)
809 char *w; /* Current position where the stripped data is written */
810 char *r; /* Current position where the original data is read */
811 char *n;
813 if (!s)
814 return 0;
816 for (w = s, r = s; *r;)
818 if (*r == ESC_CHAR)
820 /* Skip the control sequence's arguments */ ;
821 /* '(' need to avoid strange 'B' letter in *Suse (if mc runs under root user) */
822 if (*(++r) == '[' || *r == '(')
824 /* strchr() matches trailing binary 0 */
825 while (*(++r) && strchr ("0123456789;?", *r));
827 else if (*r == ']')
830 * Skip xterm's OSC (Operating System Command)
831 * http://www.xfree86.org/current/ctlseqs.html
832 * OSC P s ; P t ST
833 * OSC P s ; P t BEL
835 char *new_r = r;
837 for (; *new_r; ++new_r)
839 switch (*new_r)
841 /* BEL */
842 case '\a':
843 r = new_r;
844 goto osc_out;
845 case ESC_CHAR:
846 /* ST */
847 if (*(new_r + 1) == '\\')
849 r = new_r + 1;
850 goto osc_out;
854 osc_out:;
858 * Now we are at the last character of the sequence.
859 * Skip it unless it's binary 0.
861 if (*r)
862 r++;
863 continue;
866 n = str_get_next_char (r);
867 if (str_isprint (r))
869 memmove (w, r, n - r);
870 w += n - r;
872 r = n;
874 *w = 0;
875 return s;
878 /* --------------------------------------------------------------------------------------------- */
880 enum compression_type
881 get_compression_type (int fd, const char *name)
883 unsigned char magic[16];
884 size_t str_len;
886 /* Read the magic signature */
887 if (mc_read (fd, (char *) magic, 4) != 4)
888 return COMPRESSION_NONE;
890 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
891 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236))
893 return COMPRESSION_GZIP;
896 /* PKZIP_MAGIC */
897 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003 && magic[3] == 004)
899 /* Read compression type */
900 mc_lseek (fd, 8, SEEK_SET);
901 if (mc_read (fd, (char *) magic, 2) != 2)
902 return COMPRESSION_NONE;
904 /* Gzip can handle only deflated (8) or stored (0) files */
905 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
906 return COMPRESSION_NONE;
908 /* Compatible with gzip */
909 return COMPRESSION_GZIP;
912 /* PACK_MAGIC and LZH_MAGIC and compress magic */
913 if (magic[0] == 037 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235))
915 /* Compatible with gzip */
916 return COMPRESSION_GZIP;
919 /* BZIP and BZIP2 files */
920 if ((magic[0] == 'B') && (magic[1] == 'Z') && (magic[3] >= '1') && (magic[3] <= '9'))
922 switch (magic[2])
924 case '0':
925 return COMPRESSION_BZIP;
926 case 'h':
927 return COMPRESSION_BZIP2;
931 /* Support for LZMA (only utils format with magic in header).
932 * This is the default format of LZMA utils 4.32.1 and later. */
934 if (mc_read (fd, (char *) magic + 4, 2) != 2)
935 return COMPRESSION_NONE;
937 /* LZMA utils format */
938 if (magic[0] == 0xFF
939 && magic[1] == 'L'
940 && magic[2] == 'Z' && magic[3] == 'M' && magic[4] == 'A' && magic[5] == 0x00)
941 return COMPRESSION_LZMA;
943 /* XZ compression magic */
944 if (magic[0] == 0xFD
945 && magic[1] == 0x37
946 && magic[2] == 0x7A && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00)
947 return COMPRESSION_XZ;
949 str_len = strlen (name);
950 /* HACK: we must belive to extention of LZMA file :) ... */
951 if ((str_len > 5 && strcmp (&name[str_len - 5], ".lzma") == 0) ||
952 (str_len > 4 && strcmp (&name[str_len - 4], ".tlz") == 0))
953 return COMPRESSION_LZMA;
955 return COMPRESSION_NONE;
958 /* --------------------------------------------------------------------------------------------- */
960 const char *
961 decompress_extension (int type)
963 switch (type)
965 case COMPRESSION_GZIP:
966 return "#ugz";
967 case COMPRESSION_BZIP:
968 return "#ubz";
969 case COMPRESSION_BZIP2:
970 return "#ubz2";
971 case COMPRESSION_LZMA:
972 return "#ulzma";
973 case COMPRESSION_XZ:
974 return "#uxz";
976 /* Should never reach this place */
977 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
978 return 0;
981 /* --------------------------------------------------------------------------------------------- */
983 void
984 wipe_password (char *passwd)
986 char *p = passwd;
988 if (!p)
989 return;
990 for (; *p; p++)
991 *p = 0;
992 g_free (passwd);
995 /* --------------------------------------------------------------------------------------------- */
997 * Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key
998 * @returns a newly allocated string
1001 char *
1002 convert_controls (const char *p)
1004 char *valcopy = g_strdup (p);
1005 char *q;
1007 /* Parse the escape special character */
1008 for (q = valcopy; *p;)
1010 if (*p == '\\')
1012 p++;
1013 if ((*p == 'e') || (*p == 'E'))
1015 p++;
1016 *q++ = ESC_CHAR;
1019 else
1021 if (*p == '^')
1023 p++;
1024 if (*p == '^')
1025 *q++ = *p++;
1026 else
1028 char c = (*p | 0x20);
1029 if (c >= 'a' && c <= 'z')
1031 *q++ = c - 'a' + 1;
1032 p++;
1034 else if (*p)
1035 p++;
1038 else
1039 *q++ = *p++;
1042 *q = 0;
1043 return valcopy;
1046 /* --------------------------------------------------------------------------------------------- */
1048 * Finds out a relative path from first to second, i.e. goes as many ..
1049 * as needed up in first and then goes down using second
1052 char *
1053 diff_two_paths (const char *first, const char *second)
1055 char *p, *q, *r, *s, *buf = NULL;
1056 int i, j, prevlen = -1, currlen;
1057 char *my_first = NULL, *my_second = NULL;
1059 my_first = resolve_symlinks (first);
1060 if (my_first == NULL)
1061 return NULL;
1062 my_second = resolve_symlinks (second);
1063 if (my_second == NULL)
1065 g_free (my_first);
1066 return NULL;
1068 for (j = 0; j < 2; j++)
1070 p = my_first;
1071 q = my_second;
1072 for (;;)
1074 r = strchr (p, PATH_SEP);
1075 s = strchr (q, PATH_SEP);
1076 if (!r || !s)
1077 break;
1078 *r = 0;
1079 *s = 0;
1080 if (strcmp (p, q))
1082 *r = PATH_SEP;
1083 *s = PATH_SEP;
1084 break;
1086 else
1088 *r = PATH_SEP;
1089 *s = PATH_SEP;
1091 p = r + 1;
1092 q = s + 1;
1094 p--;
1095 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++);
1096 currlen = (i + 1) * 3 + strlen (q) + 1;
1097 if (j)
1099 if (currlen < prevlen)
1100 g_free (buf);
1101 else
1103 g_free (my_first);
1104 g_free (my_second);
1105 return buf;
1108 p = buf = g_malloc (currlen);
1109 prevlen = currlen;
1110 for (; i >= 0; i--, p += 3)
1111 strcpy (p, "../");
1112 strcpy (p, q);
1114 g_free (my_first);
1115 g_free (my_second);
1116 return buf;
1119 /* --------------------------------------------------------------------------------------------- */
1121 * If filename is NULL, then we just append PATH_SEP to the dir
1124 char *
1125 concat_dir_and_file (const char *dir, const char *file)
1127 int i = strlen (dir);
1129 if (dir[i - 1] == PATH_SEP)
1130 return g_strconcat (dir, file, (char *) NULL);
1131 else
1132 return g_strconcat (dir, PATH_SEP_STR, file, (char *) NULL);
1135 /* --------------------------------------------------------------------------------------------- */
1137 * Append text to GList, remove all entries with the same text
1140 GList *
1141 list_append_unique (GList * list, char *text)
1143 GList *lc_link;
1146 * Go to the last position and traverse the list backwards
1147 * starting from the second last entry to make sure that we
1148 * are not removing the current link.
1150 list = g_list_append (list, text);
1151 list = g_list_last (list);
1152 lc_link = g_list_previous (list);
1154 while (lc_link != NULL)
1156 GList *newlink;
1158 newlink = g_list_previous (lc_link);
1159 if (strcmp ((char *) lc_link->data, text) == 0)
1161 GList *tmp;
1163 g_free (lc_link->data);
1164 tmp = g_list_remove_link (list, lc_link);
1165 g_list_free_1 (lc_link);
1167 lc_link = newlink;
1170 return list;
1173 /* --------------------------------------------------------------------------------------------- */
1174 /* Following code heavily borrows from libiberty, mkstemps.c */
1176 * Arguments:
1177 * pname (output) - pointer to the name of the temp file (needs g_free).
1178 * NULL if the function fails.
1179 * prefix - part of the filename before the random part.
1180 * Prepend $TMPDIR or /tmp if there are no path separators.
1181 * suffix - if not NULL, part of the filename after the random part.
1183 * Result:
1184 * handle of the open file or -1 if couldn't open any.
1188 mc_mkstemps (char **pname, const char *prefix, const char *suffix)
1190 static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1191 static unsigned long value;
1192 struct timeval tv;
1193 char *tmpbase;
1194 char *tmpname;
1195 char *XXXXXX;
1196 int count;
1198 if (strchr (prefix, PATH_SEP) == NULL)
1200 /* Add prefix first to find the position of XXXXXX */
1201 tmpbase = concat_dir_and_file (mc_tmpdir (), prefix);
1203 else
1205 tmpbase = g_strdup (prefix);
1208 tmpname = g_strconcat (tmpbase, "XXXXXX", suffix, (char *) NULL);
1209 *pname = tmpname;
1210 XXXXXX = &tmpname[strlen (tmpbase)];
1211 g_free (tmpbase);
1213 /* Get some more or less random data. */
1214 gettimeofday (&tv, NULL);
1215 value += (tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
1217 for (count = 0; count < TMP_MAX; ++count)
1219 unsigned long v = value;
1220 int fd;
1222 /* Fill in the random bits. */
1223 XXXXXX[0] = letters[v % 62];
1224 v /= 62;
1225 XXXXXX[1] = letters[v % 62];
1226 v /= 62;
1227 XXXXXX[2] = letters[v % 62];
1228 v /= 62;
1229 XXXXXX[3] = letters[v % 62];
1230 v /= 62;
1231 XXXXXX[4] = letters[v % 62];
1232 v /= 62;
1233 XXXXXX[5] = letters[v % 62];
1235 fd = open (tmpname, O_RDWR | O_CREAT | O_TRUNC | O_EXCL, S_IRUSR | S_IWUSR);
1236 if (fd >= 0)
1238 /* Successfully created. */
1239 return fd;
1242 /* This is a random value. It is only necessary that the next
1243 TMP_MAX values generated by adding 7777 to VALUE are different
1244 with (module 2^32). */
1245 value += 7777;
1248 /* Unsuccessful. Free the filename. */
1249 g_free (tmpname);
1250 *pname = NULL;
1252 return -1;
1255 /* --------------------------------------------------------------------------------------------- */
1257 * Read and restore position for the given filename.
1258 * If there is no stored data, return line 1 and col 0.
1261 void
1262 load_file_position (const char *filename, long *line, long *column, off_t * offset,
1263 GArray ** bookmarks)
1265 char *fn;
1266 FILE *f;
1267 char buf[MC_MAXPATHLEN + 100];
1268 const size_t len = strlen (filename);
1270 /* defaults */
1271 *line = 1;
1272 *column = 0;
1273 *offset = 0;
1275 /* open file with positions */
1276 fn = g_build_filename (home_dir, MC_USERCONF_DIR, MC_FILEPOS_FILE, NULL);
1277 f = fopen (fn, "r");
1278 g_free (fn);
1279 if (f == NULL)
1280 return;
1282 /* prepare array for serialized bookmarks */
1283 *bookmarks = g_array_sized_new (FALSE, FALSE, sizeof (size_t), MAX_SAVED_BOOKMARKS);
1285 while (fgets (buf, sizeof (buf), f) != NULL)
1287 const char *p;
1288 gchar **pos_tokens;
1290 /* check if the filename matches the beginning of string */
1291 if (strncmp (buf, filename, len) != 0)
1292 continue;
1294 /* followed by single space */
1295 if (buf[len] != ' ')
1296 continue;
1298 /* and string without spaces */
1299 p = &buf[len + 1];
1300 if (strchr (p, ' ') != NULL)
1301 continue;
1303 pos_tokens = g_strsplit (p, ";", 3 + MAX_SAVED_BOOKMARKS);
1304 if (pos_tokens[0] == NULL)
1306 *line = 1;
1307 *column = 0;
1308 *offset = 0;
1310 else
1312 *line = strtol (pos_tokens[0], NULL, 10);
1313 if (pos_tokens[1] == NULL)
1315 *column = 0;
1316 *offset = 0;
1318 else
1320 *column = strtol (pos_tokens[1], NULL, 10);
1321 if (pos_tokens[2] == NULL)
1322 *offset = 0;
1323 else
1325 size_t i;
1327 *offset = strtoll (pos_tokens[2], NULL, 10);
1329 for (i = 0; i < MAX_SAVED_BOOKMARKS && pos_tokens[3 + i] != NULL; i++)
1331 size_t val;
1333 val = strtoul (pos_tokens[3 + i], NULL, 10);
1334 g_array_append_val (*bookmarks, val);
1340 g_strfreev (pos_tokens);
1343 fclose (f);
1346 /* --------------------------------------------------------------------------------------------- */
1348 * Save position for the given file
1351 void
1352 save_file_position (const char *filename, long line, long column, off_t offset, GArray * bookmarks)
1354 static size_t filepos_max_saved_entries = 0;
1355 char *fn, *tmp_fn;
1356 FILE *f, *tmp_f;
1357 char buf[MC_MAXPATHLEN + 100];
1358 size_t i;
1359 const size_t len = strlen (filename);
1360 gboolean src_error = FALSE;
1362 if (filepos_max_saved_entries == 0)
1363 filepos_max_saved_entries = mc_config_get_int (mc_main_config, CONFIG_APP_SECTION,
1364 "filepos_max_saved_entries", 1024);
1366 fn = g_build_filename (home_dir, MC_USERCONF_DIR, MC_FILEPOS_FILE, NULL);
1367 if (fn == NULL)
1368 goto early_error;
1370 mc_util_make_backup_if_possible (fn, TMP_SUFFIX);
1372 /* open file */
1373 f = fopen (fn, "w");
1374 if (f == NULL)
1375 goto open_target_error;
1377 tmp_fn = g_strdup_printf ("%s" TMP_SUFFIX, fn);
1378 tmp_f = fopen (tmp_fn, "r");
1379 if (tmp_f == NULL)
1381 src_error = TRUE;
1382 goto open_source_error;
1385 /* put the new record */
1386 if (line != 1 || column != 0 || bookmarks != NULL)
1388 if (fprintf (f, "%s %ld;%ld;%" PRIuMAX, filename, line, column, (uintmax_t) offset) < 0)
1389 goto write_position_error;
1390 if (bookmarks != NULL)
1391 for (i = 0; i < bookmarks->len && i < MAX_SAVED_BOOKMARKS; i++)
1392 if (fprintf (f, ";%zu", g_array_index (bookmarks, size_t, i)) < 0)
1393 goto write_position_error;
1395 if (fprintf (f, "\n") < 0)
1396 goto write_position_error;
1399 i = 1;
1400 while (fgets (buf, sizeof (buf), tmp_f) != NULL)
1402 if (buf[len] == ' ' && strncmp (buf, filename, len) == 0
1403 && strchr (&buf[len + 1], ' ') == NULL)
1404 continue;
1406 fprintf (f, "%s", buf);
1407 if (++i > filepos_max_saved_entries)
1408 break;
1411 write_position_error:
1412 fclose (tmp_f);
1413 open_source_error:
1414 g_free (tmp_fn);
1415 fclose (f);
1416 if (src_error)
1417 mc_util_restore_from_backup_if_possible (fn, TMP_SUFFIX);
1418 else
1419 mc_util_unlink_backup_if_possible (fn, TMP_SUFFIX);
1420 open_target_error:
1421 g_free (fn);
1422 early_error:
1423 if (bookmarks != NULL)
1424 g_array_free (bookmarks, TRUE);
1427 /* --------------------------------------------------------------------------------------------- */
1429 extern int
1430 ascii_alpha_to_cntrl (int ch)
1432 if ((ch >= ASCII_A && ch <= ASCII_Z) || (ch >= ASCII_a && ch <= ASCII_z))
1434 ch &= 0x1f;
1436 return ch;
1439 /* --------------------------------------------------------------------------------------------- */
1441 const char *
1442 Q_ (const char *s)
1444 const char *result, *sep;
1446 result = _(s);
1447 sep = strchr (result, '|');
1448 return (sep != NULL) ? sep + 1 : result;
1451 /* --------------------------------------------------------------------------------------------- */
1453 gboolean
1454 mc_util_make_backup_if_possible (const char *file_name, const char *backup_suffix)
1456 struct stat stat_buf;
1457 char *backup_path;
1458 gboolean ret;
1459 if (!exist_file (file_name))
1460 return FALSE;
1462 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1464 if (backup_path == NULL)
1465 return FALSE;
1467 ret = mc_util_write_backup_content (file_name, backup_path);
1469 if (ret)
1471 /* Backup file will have same ownership with main file. */
1472 if (stat (file_name, &stat_buf) == 0)
1473 chmod (backup_path, stat_buf.st_mode);
1474 else
1475 chmod (backup_path, S_IRUSR | S_IWUSR);
1478 g_free (backup_path);
1480 return ret;
1483 /* --------------------------------------------------------------------------------------------- */
1485 gboolean
1486 mc_util_restore_from_backup_if_possible (const char *file_name, const char *backup_suffix)
1488 gboolean ret;
1489 char *backup_path;
1491 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1492 if (backup_path == NULL)
1493 return FALSE;
1495 ret = mc_util_write_backup_content (backup_path, file_name);
1496 g_free (backup_path);
1498 return ret;
1501 /* --------------------------------------------------------------------------------------------- */
1503 gboolean
1504 mc_util_unlink_backup_if_possible (const char *file_name, const char *backup_suffix)
1506 char *backup_path;
1508 backup_path = g_strdup_printf ("%s%s", file_name, backup_suffix);
1509 if (backup_path == NULL)
1510 return FALSE;
1512 if (exist_file (backup_path))
1513 mc_unlink (backup_path);
1515 g_free (backup_path);
1516 return TRUE;
1519 /* --------------------------------------------------------------------------------------------- */
1521 * partly taken from dcigettext.c, returns "" for default locale
1522 * value should be freed by calling function g_free()
1525 char *
1526 guess_message_value (void)
1528 static const char *const var[] = {
1529 /* Setting of LC_ALL overwrites all other. */
1530 /* Do not use LANGUAGE for check user locale and drowing hints */
1531 "LC_ALL",
1532 /* Next comes the name of the desired category. */
1533 "LC_MESSAGES",
1534 /* Last possibility is the LANG environment variable. */
1535 "LANG",
1536 /* NULL exit loops */
1537 NULL
1540 unsigned i = 0;
1541 const char *locale = NULL;
1543 while (var[i] != NULL)
1545 locale = getenv (var[i]);
1546 if (locale != NULL && locale[0] != '\0')
1547 break;
1548 i++;
1551 if (locale == NULL)
1552 locale = "";
1554 return g_strdup (locale);
1557 /* --------------------------------------------------------------------------------------------- */