Corrected a misdeleted row that created a crash on f1-f1 key pressing...
[midnight-commander.git] / src / util.c
blob950bf04a5d48ba8e2bf12d018fbd877f0358d7a2
1 /* Various utilities
2 Copyright (C) 1994, 1995, 1996 the Free Software Foundation.
3 Written 1994, 1995, 1996 by:
4 Miguel de Icaza, Janne Kukonlehto, Dugan Porter,
5 Jakub Jelinek, Mauricio Plaza.
7 The file_date routine is mostly from GNU's fileutils package,
8 written by Richard Stallman and David MacKenzie.
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
24 #include <config.h>
25 #include <stdio.h>
26 #include <sys/types.h>
27 #ifdef HAVE_UNISTD_H
28 # include <unistd.h>
29 #endif
30 #include <fcntl.h>
31 #include <signal.h> /* my_system */
32 #include <limits.h> /* INT_MAX */
33 #include <sys/stat.h>
34 #include <stdarg.h>
35 #include <errno.h> /* my_system */
36 #include <string.h>
37 #include <ctype.h>
39 #include "global.h"
40 #include "profile.h"
41 #include "main.h" /* mc_home */
42 #include "cmd.h" /* guess_message_value */
43 #include "../vfs/vfs.h"
44 #include "mountlist.h"
46 #ifdef HAVE_CHARSET
47 #include "charsets.h"
48 #endif
50 /* "$Id$" */
52 static const char app_text [] = "Midnight-Commander";
53 int easy_patterns = 1;
55 #ifndef VFS_STANDALONE
56 int is_printable (int c)
58 static const unsigned char xterm_printable[] = {
59 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
60 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
61 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
62 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
63 1,1,1,1,0,0,1,1,0,1,1,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,0,0,0,0,0,0,
64 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
65 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
66 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
69 extern int xterm_flag;
71 c &= 0xff;
72 #ifdef HAVE_CHARSET
73 if (xterm_flag)
74 return xterm_printable[c];
75 else
76 return (c > 31 && c != 127 && c != 155);
77 #else
78 if (eight_bit_clean){
79 if (full_eight_bits){
80 if (xterm_flag)
81 return xterm_printable [c];
82 else
83 return (c > 31 && c != 127 && c != 155);
84 } else
85 return ((c >31 && c < 127) || c >= 160);
86 } else
87 return (c > 31 && c < 127);
88 #endif /* !HAVE_CHARSET */
91 /* Returns the message dimensions (lines and columns) */
92 int msglen (char *text, int *lines)
94 int max = 0;
95 int line_len = 0;
97 for (*lines = 1;*text; text++){
98 if (*text == '\n'){
99 line_len = 0;
100 (*lines)++;
101 } else {
102 line_len++;
103 if (line_len > max)
104 max = line_len;
107 return max;
110 char *trim (char *s, char *d, int len)
112 int source_len = strlen (s);
114 if (source_len > len){
115 strcpy (d, s+(source_len-len));
116 d [0] = '.';
117 d [1] = '.';
118 d [2] = '.';
119 } else
120 strcpy (d, s);
121 return d;
123 #endif /* !VFS_STANDALONE */
125 char *
126 name_quote (const char *s, int quote_percent)
128 char *ret, *d;
130 d = ret = g_malloc (strlen (s)*2 + 2 + 1);
131 if (*s == '-') {
132 *d++ = '.';
133 *d++ = '/';
136 for (; *s; s++, d++) {
137 switch (*s)
139 case '%':
140 if (quote_percent)
141 *d++ = '%';
142 break;
143 case '\'':
144 case '\\':
145 case '\r':
146 case '\n':
147 case '\t':
148 case '"':
149 case ':':
150 case ';':
151 case ' ':
152 case '?':
153 case '|':
154 case '[':
155 case ']':
156 case '{':
157 case '}':
158 case '<':
159 case '>':
160 case '`':
161 case '~':
162 case '!':
163 case '@':
164 case '#':
165 case '$':
166 case '^':
167 case '&':
168 case '*':
169 case '(':
170 case ')':
171 *d++ = '\\';
173 *d = *s;
175 *d = '\0';
176 return ret;
179 #ifndef VFS_STANDALONE
180 char *
181 fake_name_quote (const char *s, int quote_percent)
183 return g_strdup (s);
186 /* If passed an empty txt (this usually means that there is an error)
187 * in the upper layers, we return "/"
189 char *name_trunc (const char *txt, int trunc_len)
191 static char x [MC_MAXPATHLEN+MC_MAXPATHLEN];
192 int txt_len;
193 char *p;
194 const int tilde_trunc = 1;
196 if (!txt)
197 txt = PATH_SEP_STR;
199 if (trunc_len > sizeof (x)-1){
200 fprintf (stderr, _("name_trunc: too big"));
201 trunc_len = sizeof (x)-1;
203 txt_len = strlen (txt);
204 if (txt_len <= trunc_len)
205 strcpy (x, txt);
206 else if (tilde_trunc){
207 int y = (trunc_len/2) + (trunc_len % 2);
208 strncpy (x, txt, y);
209 strncpy (x+y, txt+txt_len-(trunc_len/2), trunc_len/2);
210 x [y] = '~';
211 } else {
212 strncpy (x, txt, trunc_len-1);
213 x [trunc_len-1] = '>';
215 x [trunc_len] = 0;
216 for (p = x; *p; p++)
217 if (!is_printable (*p))
218 *p = '?';
219 return x;
222 char *size_trunc (double size)
224 static char x [BUF_TINY];
225 long int divisor = 1;
226 char *xtra = "";
228 if (size > 999999999L){
229 divisor = 1024;
230 xtra = "Kb";
231 if (size/divisor > 999999999L){
232 divisor = 1024*1024;
233 xtra = "Mb";
236 g_snprintf (x, sizeof (x), "%.0f%s", (size/divisor), xtra);
237 return x;
240 char *size_trunc_sep (double size)
242 static char x [60];
243 int count;
244 char *p, *d, *y;
246 p = y = size_trunc (size);
247 p += strlen (p) - 1;
248 d = x + sizeof (x) - 1;
249 *d-- = 0;
250 while (p >= y && isalpha ((unsigned char) *p))
251 *d-- = *p--;
252 for (count = 0; p >= y; count++){
253 if (count == 3){
254 *d-- = ',';
255 count = 0;
257 *d-- = *p--;
259 d++;
260 if (*d == ',')
261 d++;
262 return d;
266 * Print file SIZE to BUFFER, but don't exceed LEN characters,
267 * not including trailing 0. BUFFER should be at least LEN+1 long.
268 * This function is called for every file on panels, so avoid
269 * floating point by any means.
271 * Units: size units (filesystem sizes are 1K blocks)
272 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
274 void
275 size_trunc_len (char *buffer, int len, off_t size, int units)
277 /* Avoid taking power for every file. */
278 static const off_t power10 [] =
279 {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
280 1000000000};
281 static const char * const suffix [] =
282 {"", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL};
283 int j = 0;
285 /* Don't print more than 9 digits - use suffix. */
286 if (len == 0 || len > 9)
287 len = 9;
289 for (j = units; suffix [j] != NULL; j++) {
290 if (size == 0) {
291 if (j == units) {
292 /* Empty files will print "0" even with minimal width. */
293 g_snprintf (buffer, len + 1, "0");
294 break;
297 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
298 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s",
299 (j > 1) ? suffix[j - 1] : "B");
300 break;
303 if (size < power10 [len - (j > 0)]) {
304 g_snprintf (buffer, len + 1, "%lu%s", (unsigned long) size, suffix[j]);
305 break;
308 /* Powers of 1024, with rounding. */
309 size = (size + 512) >> 10;
313 int is_exe (mode_t mode)
315 if ((S_IXUSR & mode) || (S_IXGRP & mode) || (S_IXOTH & mode))
316 return 1;
317 return 0;
320 #define ismode(n,m) ((n & m) == m)
322 char *
323 string_perm (mode_t mode_bits)
325 static char mode[11];
327 strcpy (mode, "----------");
328 if (S_ISDIR (mode_bits))
329 mode[0] = 'd';
330 if (S_ISCHR (mode_bits))
331 mode[0] = 'c';
332 if (S_ISBLK (mode_bits))
333 mode[0] = 'b';
334 if (S_ISLNK (mode_bits))
335 mode[0] = 'l';
336 if (S_ISFIFO (mode_bits))
337 mode[0] = 'p';
338 if (S_ISSOCK (mode_bits))
339 mode[0] = 's';
340 if (S_ISDOOR (mode_bits))
341 mode[0] = 'D';
342 if (ismode (mode_bits, S_IXOTH))
343 mode[9] = 'x';
344 if (ismode (mode_bits, S_IWOTH))
345 mode[8] = 'w';
346 if (ismode (mode_bits, S_IROTH))
347 mode[7] = 'r';
348 if (ismode (mode_bits, S_IXGRP))
349 mode[6] = 'x';
350 if (ismode (mode_bits, S_IWGRP))
351 mode[5] = 'w';
352 if (ismode (mode_bits, S_IRGRP))
353 mode[4] = 'r';
354 if (ismode (mode_bits, S_IXUSR))
355 mode[3] = 'x';
356 if (ismode (mode_bits, S_IWUSR))
357 mode[2] = 'w';
358 if (ismode (mode_bits, S_IRUSR))
359 mode[1] = 'r';
360 #ifdef S_ISUID
361 if (ismode (mode_bits, S_ISUID))
362 mode[3] = (mode[3] == 'x') ? 's' : 'S';
363 #endif /* S_ISUID */
364 #ifdef S_ISGID
365 if (ismode (mode_bits, S_ISGID))
366 mode[6] = (mode[6] == 'x') ? 's' : 'S';
367 #endif /* S_ISGID */
368 #ifdef S_ISVTX
369 if (ismode (mode_bits, S_ISVTX))
370 mode[9] = (mode[9] == 'x') ? 't' : 'T';
371 #endif /* S_ISVTX */
372 return mode;
375 /* p: string which might contain an url with a password (this parameter is
376 modified in place).
377 has_prefix = 0: The first parameter is an url without a prefix
378 (user[:pass]@]machine[:port][remote-dir). Delete
379 the password.
380 has_prefix = 1: Search p for known url prefixes. If found delete
381 the password from the url.
382 Cavevat: only the first url is found
384 char *
385 strip_password (char *p, int has_prefix)
387 static const struct {
388 char *name;
389 size_t len;
390 } prefixes[] = { {"/#ftp:", 6},
391 {"/#mc:", 5},
392 {"ftp://", 6},
393 {"/#smb:", 6},
395 char *at, *inner_colon, *dir;
396 int i;
397 char *result = p;
399 for (i = 0; i < sizeof (prefixes)/sizeof (prefixes[0]); i++) {
400 char *q;
402 if (has_prefix) {
403 if((q = strstr (p, prefixes[i].name)) == 0)
404 continue;
405 else
406 p = q + prefixes[i].len;
409 if ((dir = strchr (p, PATH_SEP)) != NULL)
410 *dir = '\0';
411 /* search for any possible user */
412 at = strchr (p, '@');
414 /* We have a username */
415 if (at) {
416 *at = 0;
417 inner_colon = strchr (p, ':');
418 *at = '@';
419 if (inner_colon)
420 strcpy (inner_colon, at);
422 if (dir)
423 *dir = PATH_SEP;
424 break;
426 return (result);
429 char *strip_home_and_password(char *dir)
431 size_t len;
432 static char newdir [MC_MAXPATHLEN];
434 if (home_dir && !strncmp (dir, home_dir, len = strlen (home_dir)) &&
435 (dir[len] == PATH_SEP || dir[len] == '\0')){
436 newdir [0] = '~';
437 strcpy (&newdir [1], &dir [strlen (home_dir)]);
438 return newdir;
441 /* We do not strip homes in /#ftp tree, I do not like ~'s there
442 (see ftpfs.c why) */
443 strcpy (newdir, dir);
444 strip_password (newdir, 1);
445 return newdir;
448 static char *maybe_start_group (char *d, int do_group, int *was_wildcard)
450 if (!do_group)
451 return d;
452 if (*was_wildcard)
453 return d;
454 *was_wildcard = 1;
455 *d++ = '\\';
456 *d++ = '(';
457 return d;
460 static char *maybe_end_group (char *d, int do_group, int *was_wildcard)
462 if (!do_group)
463 return d;
464 if (!*was_wildcard)
465 return d;
466 *was_wildcard = 0;
467 *d++ = '\\';
468 *d++ = ')';
469 return d;
472 /* If shell patterns are on converts a shell pattern to a regular
473 expression. Called by regexp_match and mask_rename. */
474 /* Shouldn't we support [a-fw] type wildcards as well ?? */
475 char *convert_pattern (char *pattern, int match_type, int do_group)
477 char *s, *d;
478 char *new_pattern;
479 int was_wildcard = 0;
481 if (easy_patterns){
482 new_pattern = g_malloc (MC_MAXPATHLEN);
483 d = new_pattern;
484 if (match_type == match_file)
485 *d++ = '^';
486 for (s = pattern; *s; s++, d++){
487 switch (*s){
488 case '*':
489 d = maybe_start_group (d, do_group, &was_wildcard);
490 *d++ = '.';
491 *d = '*';
492 break;
494 case '?':
495 d = maybe_start_group (d, do_group, &was_wildcard);
496 *d = '.';
497 break;
499 case '.':
500 d = maybe_end_group (d, do_group, &was_wildcard);
501 *d++ = '\\';
502 *d = '.';
503 break;
505 default:
506 d = maybe_end_group (d, do_group, &was_wildcard);
507 *d = *s;
508 break;
511 d = maybe_end_group (d, do_group, &was_wildcard);
512 if (match_type == match_file)
513 *d++ = '$';
514 *d = 0;
515 return new_pattern;
516 } else
517 return g_strdup (pattern);
520 int regexp_match (char *pattern, char *string, int match_type)
522 static regex_t r;
523 static char *old_pattern = NULL;
524 static int old_type;
525 int rval;
527 if (!old_pattern || STRCOMP (old_pattern, pattern) || old_type != match_type){
528 if (old_pattern){
529 regfree (&r);
530 g_free (old_pattern);
531 old_pattern = NULL;
533 pattern = convert_pattern (pattern, match_type, 0);
534 if (regcomp (&r, pattern, REG_EXTENDED|REG_NOSUB|MC_ARCH_FLAGS)) {
535 g_free (pattern);
536 return -1;
538 old_pattern = pattern;
539 old_type = match_type;
541 rval = !regexec (&r, string, 0, NULL, 0);
542 return rval;
545 char *extension (char *filename)
547 char *d;
549 if (!(*filename))
550 return "";
552 d = filename + strlen (filename) - 1;
553 for (;d >= filename; d--){
554 if (*d == '.')
555 return d+1;
557 return "";
560 int get_int (char *file, char *key, int def)
562 return GetPrivateProfileInt (app_text, key, def, file);
565 int set_int (char *file, char *key, int value)
567 char buffer [BUF_TINY];
569 g_snprintf (buffer, sizeof (buffer), "%d", value);
570 return WritePrivateProfileString (app_text, key, buffer, file);
573 int exist_file (char *name)
575 return access (name, R_OK) == 0;
578 char *load_file (char *filename)
580 FILE *data_file;
581 struct stat s;
582 char *data;
583 long read_size;
585 if ((data_file = fopen (filename, "r")) == NULL){
586 return 0;
588 if (fstat (fileno (data_file), &s) != 0){
589 fclose (data_file);
590 return 0;
592 data = (char *) g_malloc (s.st_size+1);
593 read_size = fread (data, 1, s.st_size, data_file);
594 data [read_size] = 0;
595 fclose (data_file);
597 if (read_size > 0)
598 return data;
599 else {
600 g_free (data);
601 return 0;
605 char *load_mc_home_file (const char *filename, char ** allocated_filename)
607 char *hintfile_base, *hintfile;
608 char *lang;
609 char *data;
611 hintfile_base = concat_dir_and_file (mc_home, filename);
612 lang = guess_message_value ();
614 hintfile = g_strdup_printf ("%s.%s", hintfile_base, lang);
615 data = load_file (hintfile);
617 if (!data) {
618 g_free (hintfile);
619 hintfile = g_strdup_printf ("%s.%.2s", hintfile_base, lang);
620 data = load_file (hintfile);
622 if (!data) {
623 g_free (hintfile);
624 hintfile = hintfile_base;
625 data = load_file (hintfile_base);
629 g_free (lang);
631 if (hintfile != hintfile_base)
632 g_free (hintfile_base);
634 if (allocated_filename)
635 *allocated_filename = hintfile;
636 else
637 g_free (hintfile);
639 return data;
642 /* Check strftime() results. Some systems (i.e. Solaris) have different
643 short-month-name sizes for different locales */
644 size_t i18n_checktimelength (void)
646 size_t length, a, b;
647 char buf [MAX_I18NTIMELENGTH + 1];
648 time_t testtime = time (NULL);
650 a = strftime (buf, sizeof(buf)-1, _("%b %e %H:%M"), localtime(&testtime));
651 b = strftime (buf, sizeof(buf)-1, _("%b %e %Y"), localtime(&testtime));
653 length = max (a, b);
655 /* Don't handle big differences. Use standard value (email bug, please) */
656 if ( length > MAX_I18NTIMELENGTH || length < MIN_I18NTIMELENGTH )
657 length = STD_I18NTIMELENGTH;
659 return length;
662 char *file_date (time_t when)
664 static char timebuf [MAX_I18NTIMELENGTH + 1];
665 time_t current_time = time ((time_t) 0);
666 static size_t i18n_timelength = 0;
667 static char *fmtyear, *fmttime;
668 char *fmt;
670 if (i18n_timelength == 0){
671 i18n_timelength = i18n_checktimelength() + 1;
673 /* strftime() format string for old dates */
674 fmtyear = _("%b %e %Y");
675 /* strftime() format string for recent dates */
676 fmttime = _("%b %e %H:%M");
679 if (current_time > when + 6L * 30L * 24L * 60L * 60L /* Old. */
680 || current_time < when - 60L * 60L) /* In the future. */
681 /* The file is fairly old or in the future.
682 POSIX says the cutoff is 6 months old;
683 approximate this by 6*30 days.
684 Allow a 1 hour slop factor for what is considered "the future",
685 to allow for NFS server/client clock disagreement.
686 Show the year instead of the time of day. */
688 fmt = fmtyear;
689 else
690 fmt = fmttime;
692 strftime (timebuf, i18n_timelength, fmt, localtime(&when));
693 return timebuf;
696 char *extract_line (char *s, char *top)
698 static char tmp_line [BUF_MEDIUM];
699 char *t = tmp_line;
701 while (*s && *s != '\n' && (t - tmp_line) < sizeof (tmp_line)-1 && s < top)
702 *t++ = *s++;
703 *t = 0;
704 return tmp_line;
707 /* FIXME: I should write a faster version of this (Aho-Corasick stuff) */
708 char * _icase_search (char *text, char *data, int *lng)
710 char *d = text;
711 char *e = data;
712 int dlng = 0;
714 if (lng)
715 *lng = 0;
716 for (;*e; e++) {
717 while (*(e+1) == '\b' && *(e+2)) {
718 e += 2;
719 dlng += 2;
721 if (toupper((unsigned char) *d) == toupper((unsigned char) *e))
722 d++;
723 else {
724 e -= d - text;
725 d = text;
726 dlng = 0;
728 if (!*d) {
729 if (lng)
730 *lng = strlen (text) + dlng;
731 return e+1;
734 return 0;
737 /* The basename routine */
738 char *x_basename (char *s)
740 char *where;
741 return ((where = strrchr (s, PATH_SEP))) ? where + 1 : s;
744 #endif /* !VFS_STANDALONE */
746 char *unix_error_string (int error_num)
748 static char buffer [BUF_LARGE];
750 g_snprintf (buffer, sizeof (buffer), "%s (%d)",
751 g_strerror (error_num), error_num);
752 return buffer;
755 #ifndef VFS_STANDALONE
756 char *skip_separators (char *s)
758 for (;*s; s++)
759 if (*s != ' ' && *s != '\t' && *s != ',')
760 break;
761 return s;
764 char *skip_numbers (char *s)
766 for (;*s; s++)
767 if (!isdigit ((unsigned int) *s))
768 break;
769 return s;
772 /* Remove all control sequences from the argument string. We define
773 * "control sequence", in a sort of pidgin BNF, as follows:
775 * control-seq = Esc non-'['
776 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
778 * This scheme works for all the terminals described in my termcap /
779 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
780 * terminals. If I hear from a single person who uses such a terminal
781 * with MC, I'll be glad to add support for it. (Dugan)
782 * Non-printable characters are also removed.
785 char *strip_ctrl_codes (char *s)
787 char *w; /* Current position where the stripped data is written */
788 char *r; /* Current position where the original data is read */
790 if (!s)
791 return 0;
793 for (w = s, r = s; *r; ) {
794 if (*r == ESC_CHAR) {
795 /* Skip the control sequence's arguments */ ;
796 if (*(++r) == '[') {
797 /* strchr() matches trailing binary 0 */
798 while (*(++r) && strchr ("0123456789;?", *r));
802 * Now we are at the last character of the sequence.
803 * Skip it unless it's binary 0.
805 if (*r)
806 r++;
807 continue;
810 if (is_printable(*r))
811 *w++ = *r;
812 ++r;
814 *w = 0;
815 return s;
818 #endif /* !VFS_STANDALONE */
820 #ifndef USE_VFS
821 char *get_current_wd (char *buffer, int size)
823 char *p;
824 int len;
826 p = g_get_current_dir ();
827 len = strlen(p) + 1;
829 if (len > size) {
830 g_free (p);
831 return NULL;
834 strncpy (buffer, p, len);
835 g_free (p);
837 return buffer;
839 #endif /* !USE_VFS */
841 /* This function returns 0 if the file is not in not compressed by
842 * one of the supported compressors (gzip, bzip, bzip2). Otherwise,
843 * the compression type is returned, as defined in util.h
844 * Warning: this function moves the current file pointer */
845 int get_compression_type (int fd)
847 unsigned char magic[4];
849 /* Read the magic signature */
850 if (mc_read (fd, &magic[0], 4) != 4)
851 return COMPRESSION_NONE;
853 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
854 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236)) {
855 return COMPRESSION_GZIP;
858 /* PKZIP_MAGIC */
859 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003
860 && magic[3] == 004) {
861 /* Read compression type */
862 mc_lseek (fd, 8, SEEK_SET);
863 if (mc_read (fd, &magic[0], 2) != 2)
864 return COMPRESSION_NONE;
866 /* Gzip can handle only deflated (8) or stored (0) files */
867 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
868 return COMPRESSION_NONE;
870 /* Compatible with gzip */
871 return COMPRESSION_GZIP;
874 /* PACK_MAGIC and LZH_MAGIC and compress magic */
875 if (magic[0] == 037
876 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235)) {
877 /* Compatible with gzip */
878 return COMPRESSION_GZIP;
881 /* BZIP and BZIP2 files */
882 if ((magic[0] == 'B') && (magic[1] == 'Z') &&
883 (magic[3] >= '1') && (magic[3] <= '9')) {
884 switch (magic[2]) {
885 case '0':
886 return COMPRESSION_BZIP;
887 case 'h':
888 return COMPRESSION_BZIP2;
891 return 0;
894 char *
895 decompress_extension (int type)
897 switch (type){
898 case COMPRESSION_GZIP: return "#ugz";
899 case COMPRESSION_BZIP: return "#ubz";
900 case COMPRESSION_BZIP2: return "#ubz2";
902 /* Should never reach this place */
903 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
904 return 0;
907 #ifndef VFS_STANDALONE
908 /* Hooks */
909 void add_hook (Hook **hook_list, void (*hook_fn)(void *), void *data)
911 Hook *new_hook = g_new (Hook, 1);
913 new_hook->hook_fn = hook_fn;
914 new_hook->next = *hook_list;
915 new_hook->hook_data = data;
917 *hook_list = new_hook;
920 void execute_hooks (Hook *hook_list)
922 Hook *new_hook = 0;
923 Hook *p;
925 /* We copy the hook list first so tahat we let the hook
926 * function call delete_hook
929 while (hook_list){
930 add_hook (&new_hook, hook_list->hook_fn, hook_list->hook_data);
931 hook_list = hook_list->next;
933 p = new_hook;
935 while (new_hook){
936 (*new_hook->hook_fn)(new_hook->hook_data);
937 new_hook = new_hook->next;
940 for (hook_list = p; hook_list;){
941 p = hook_list;
942 hook_list = hook_list->next;
943 g_free (p);
947 void delete_hook (Hook **hook_list, void (*hook_fn)(void *))
949 Hook *current, *new_list, *next;
951 new_list = 0;
953 for (current = *hook_list; current; current = next){
954 next = current->next;
955 if (current->hook_fn == hook_fn)
956 g_free (current);
957 else
958 add_hook (&new_list, current->hook_fn, current->hook_data);
960 *hook_list = new_list;
963 int hook_present (Hook *hook_list, void (*hook_fn)(void *))
965 Hook *p;
967 for (p = hook_list; p; p = p->next)
968 if (p->hook_fn == hook_fn)
969 return 1;
970 return 0;
973 void wipe_password (char *passwd)
975 char *p = passwd;
977 if (!p)
978 return;
979 for (;*p ; p++)
980 *p = 0;
981 g_free (passwd);
984 /* Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key */
985 /* Returns a newly allocated string */
986 char *convert_controls (char *s)
988 char *valcopy = g_strdup (s);
989 char *p, *q;
991 /* Parse the escape special character */
992 for (p = s, q = valcopy; *p;){
993 if (*p == '\\'){
994 p++;
995 if ((*p == 'e') || (*p == 'E')){
996 p++;
997 *q++ = ESC_CHAR;
999 } else {
1000 if (*p == '^'){
1001 p++;
1002 if (*p == '^')
1003 *q++ = *p++;
1004 else {
1005 *p = (*p | 0x20);
1006 if (*p >= 'a' && *p <= 'z') {
1007 *q++ = *p++ - 'a' + 1;
1008 } else
1009 p++;
1011 } else
1012 *q++ = *p++;
1015 *q = 0;
1016 return valcopy;
1019 /* Reverse the string */
1020 char *reverse_string (char *string)
1022 char *str_beg = string;
1023 char *str_end = string + strlen (string);
1025 while (--str_end > str_beg){
1026 char c = *str_end;
1028 *str_end = *str_beg;
1029 *str_beg++ = c;
1031 return string;
1034 static char *resolve_symlinks (char *path)
1036 char *buf, *buf2, *p, *q, *r, c;
1037 int len;
1038 struct stat mybuf;
1040 if (*path != PATH_SEP)
1041 return NULL;
1042 r = buf = g_malloc (MC_MAXPATHLEN);
1043 buf2 = g_malloc (MC_MAXPATHLEN);
1044 *r++ = PATH_SEP;
1045 *r = 0;
1046 p = path;
1047 for (;;) {
1048 q = strchr (p + 1, PATH_SEP);
1049 if (!q) {
1050 q = strchr (p + 1, 0);
1051 if (q == p + 1)
1052 break;
1054 c = *q;
1055 *q = 0;
1056 if (mc_lstat (path, &mybuf) < 0) {
1057 g_free (buf);
1058 g_free (buf2);
1059 *q = c;
1060 return NULL;
1062 if (!S_ISLNK (mybuf.st_mode))
1063 strcpy (r, p + 1);
1064 else {
1065 len = mc_readlink (path, buf2, MC_MAXPATHLEN);
1066 if (len < 0) {
1067 g_free (buf);
1068 g_free (buf2);
1069 *q = c;
1070 return NULL;
1072 buf2 [len] = 0;
1073 if (*buf2 == PATH_SEP)
1074 strcpy (buf, buf2);
1075 else
1076 strcpy (r, buf2);
1078 canonicalize_pathname (buf);
1079 r = strchr (buf, 0);
1080 if (!*r || *(r - 1) != PATH_SEP) {
1081 *r++ = PATH_SEP;
1082 *r = 0;
1084 *q = c;
1085 p = q;
1086 if (!c)
1087 break;
1089 if (!*buf)
1090 strcpy (buf, PATH_SEP_STR);
1091 else if (*(r - 1) == PATH_SEP && r != buf + 1)
1092 *(r - 1) = 0;
1093 g_free (buf2);
1094 return buf;
1097 /* Finds out a relative path from first to second, i.e. goes as many ..
1098 * as needed up in first and then goes down using second */
1099 char *diff_two_paths (char *first, char *second)
1101 char *p, *q, *r, *s, *buf = 0;
1102 int i, j, prevlen = -1, currlen;
1104 first = resolve_symlinks (first);
1105 if (first == NULL)
1106 return NULL;
1107 for (j = 0; j < 2; j++) {
1108 p = first;
1109 if (j) {
1110 second = resolve_symlinks (second);
1111 if (second == NULL) {
1112 g_free (first);
1113 return buf;
1116 q = second;
1117 for (;;) {
1118 r = strchr (p, PATH_SEP);
1119 s = strchr (q, PATH_SEP);
1120 if (!r || !s)
1121 break;
1122 *r = 0; *s = 0;
1123 if (strcmp (p, q)) {
1124 *r = PATH_SEP; *s = PATH_SEP;
1125 break;
1126 } else {
1127 *r = PATH_SEP; *s = PATH_SEP;
1129 p = r + 1;
1130 q = s + 1;
1132 p--;
1133 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++);
1134 currlen = (i + 1) * 3 + strlen (q) + 1;
1135 if (j) {
1136 if (currlen < prevlen)
1137 g_free (buf);
1138 else {
1139 g_free (first);
1140 g_free (second);
1141 return buf;
1144 p = buf = g_malloc (currlen);
1145 prevlen = currlen;
1146 for (; i >= 0; i--, p += 3)
1147 strcpy (p, "../");
1148 strcpy (p, q);
1150 g_free (first);
1151 g_free (second);
1152 return buf;
1154 #endif /* !VFS_STANDALONE */
1156 /* If filename is NULL, then we just append PATH_SEP to the dir */
1157 char *
1158 concat_dir_and_file (const char *dir, const char *file)
1160 int i = strlen (dir);
1162 if (dir [i-1] == PATH_SEP)
1163 return g_strconcat (dir, file, NULL);
1164 else
1165 return g_strconcat (dir, PATH_SEP_STR, file, NULL);
1168 /* Following code heavily borrows from libiberty, mkstemps.c */
1170 /* Number of attempts to create a temporary file */
1171 #ifndef TMP_MAX
1172 #define TMP_MAX 16384
1173 #endif /* !TMP_MAX */
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.
1186 int mc_mkstemps(char **pname, const char *prefix, const char *suffix)
1188 static const char letters[]
1189 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1190 static unsigned long value;
1191 struct timeval tv;
1192 char *tmpbase;
1193 char *tmpname;
1194 char *XXXXXX;
1195 int count;
1197 if (strchr(prefix, PATH_SEP) == NULL) {
1198 char *tmpdir;
1200 tmpdir = getenv("TMPDIR");
1201 if (!tmpdir) {
1202 tmpdir = TMPDIR_DEFAULT;
1205 /* Add prefix first to find the position of XXXXXX */
1206 tmpbase = concat_dir_and_file (tmpdir, prefix);
1207 } else {
1208 tmpbase = g_strdup (prefix);
1211 tmpname = g_strconcat (tmpbase, "XXXXXX", suffix, NULL);
1212 *pname = tmpname;
1213 XXXXXX = &tmpname[strlen (tmpbase)];
1214 g_free(tmpbase);
1216 /* Get some more or less random data. */
1217 gettimeofday (&tv, NULL);
1218 value += (tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
1220 for (count = 0; count < TMP_MAX; ++count) {
1221 unsigned long v = value;
1222 int fd;
1224 /* Fill in the random bits. */
1225 XXXXXX[0] = letters[v % 62];
1226 v /= 62;
1227 XXXXXX[1] = letters[v % 62];
1228 v /= 62;
1229 XXXXXX[2] = letters[v % 62];
1230 v /= 62;
1231 XXXXXX[3] = letters[v % 62];
1232 v /= 62;
1233 XXXXXX[4] = letters[v % 62];
1234 v /= 62;
1235 XXXXXX[5] = letters[v % 62];
1237 fd = open (tmpname, O_RDWR|O_CREAT|O_EXCL, 0600);
1238 if (fd >= 0) {
1239 /* Successfully created. */
1240 return fd;
1243 /* This is a random value. It is only necessary that the next
1244 TMP_MAX values generated by adding 7777 to VALUE are different
1245 with (module 2^32). */
1246 value += 7777;
1249 /* Unsuccessful. Free the filename. */
1250 g_free (tmpname);
1251 tmpname = NULL;
1253 return -1;