small typo
[midnight-commander.git] / src / util.c
blobc45a41de45742be5816701e6ef4690a7aa8eedc7
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 int is_printable (int c)
57 static const unsigned char xterm_printable[] = {
58 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,
59 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,
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,0,
62 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,
63 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,
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
68 extern int xterm_flag;
70 c &= 0xff;
71 #ifdef HAVE_CHARSET
72 if (xterm_flag)
73 return xterm_printable[c];
74 else
75 return (c > 31 && c != 127 && c != 155);
76 #else
77 if (eight_bit_clean){
78 if (full_eight_bits){
79 if (xterm_flag)
80 return xterm_printable [c];
81 else
82 return (c > 31 && c != 127 && c != 155);
83 } else
84 return ((c >31 && c < 127) || c >= 160);
85 } else
86 return (c > 31 && c < 127);
87 #endif /* !HAVE_CHARSET */
90 /* Returns the message dimensions (lines and columns) */
91 int msglen (char *text, int *lines)
93 int max = 0;
94 int line_len = 0;
96 for (*lines = 1;*text; text++){
97 if (*text == '\n'){
98 line_len = 0;
99 (*lines)++;
100 } else {
101 line_len++;
102 if (line_len > max)
103 max = line_len;
106 return max;
110 * Copy from s to d, and trim the beginning if necessary, and prepend
111 * "..." in this case. The destination string can have at most len
112 * bytes, not counting trailing 0.
114 char *
115 trim (char *s, char *d, int len)
117 int source_len;
119 /* Sanity check */
120 len = max (len, 0);
122 source_len = strlen (s);
123 if (source_len > len) {
124 /* Cannot fit the whole line */
125 if (len <= 3) {
126 /* We only have room for the dots */
127 memset (d, '.', len);
128 d[len] = 0;
129 return d;
130 } else {
131 /* Begin with ... and add the rest of the source string */
132 memset (d, '.', 3);
133 strcpy (d + 3, s + 3 + source_len - len);
135 } else
136 /* We can copy the whole line */
137 strcpy (d, s);
138 return d;
141 char *
142 name_quote (const char *s, int quote_percent)
144 char *ret, *d;
146 d = ret = g_malloc (strlen (s)*2 + 2 + 1);
147 if (*s == '-') {
148 *d++ = '.';
149 *d++ = '/';
152 for (; *s; s++, d++) {
153 switch (*s)
155 case '%':
156 if (quote_percent)
157 *d++ = '%';
158 break;
159 case '\'':
160 case '\\':
161 case '\r':
162 case '\n':
163 case '\t':
164 case '"':
165 case ':':
166 case ';':
167 case ' ':
168 case '?':
169 case '|':
170 case '[':
171 case ']':
172 case '{':
173 case '}':
174 case '<':
175 case '>':
176 case '`':
177 case '~':
178 case '!':
179 case '@':
180 case '#':
181 case '$':
182 case '^':
183 case '&':
184 case '*':
185 case '(':
186 case ')':
187 *d++ = '\\';
189 *d = *s;
191 *d = '\0';
192 return ret;
195 char *
196 fake_name_quote (const char *s, int quote_percent)
198 return g_strdup (s);
201 /* If passed an empty txt (this usually means that there is an error)
202 * in the upper layers, we return "/"
204 char *name_trunc (const char *txt, int trunc_len)
206 static char x [MC_MAXPATHLEN+MC_MAXPATHLEN];
207 int txt_len;
208 char *p;
209 const int tilde_trunc = 1;
211 if (!txt)
212 txt = PATH_SEP_STR;
214 if (trunc_len > sizeof (x)-1){
215 fprintf (stderr, _("name_trunc: too big"));
216 trunc_len = sizeof (x)-1;
218 txt_len = strlen (txt);
219 if (txt_len <= trunc_len)
220 strcpy (x, txt);
221 else if (tilde_trunc){
222 int y = (trunc_len/2) + (trunc_len % 2);
223 strncpy (x, txt, y);
224 strncpy (x+y, txt+txt_len-(trunc_len/2), trunc_len/2);
225 x [y] = '~';
226 } else {
227 strncpy (x, txt, trunc_len-1);
228 x [trunc_len-1] = '>';
230 x [trunc_len] = 0;
231 for (p = x; *p; p++)
232 if (!is_printable (*p))
233 *p = '?';
234 return x;
237 char *size_trunc (double size)
239 static char x [BUF_TINY];
240 long int divisor = 1;
241 char *xtra = "";
243 if (size > 999999999L){
244 divisor = 1024;
245 xtra = "Kb";
246 if (size/divisor > 999999999L){
247 divisor = 1024*1024;
248 xtra = "Mb";
251 g_snprintf (x, sizeof (x), "%.0f%s", (size/divisor), xtra);
252 return x;
255 char *size_trunc_sep (double size)
257 static char x [60];
258 int count;
259 char *p, *d, *y;
261 p = y = size_trunc (size);
262 p += strlen (p) - 1;
263 d = x + sizeof (x) - 1;
264 *d-- = 0;
265 while (p >= y && isalpha ((unsigned char) *p))
266 *d-- = *p--;
267 for (count = 0; p >= y; count++){
268 if (count == 3){
269 *d-- = ',';
270 count = 0;
272 *d-- = *p--;
274 d++;
275 if (*d == ',')
276 d++;
277 return d;
281 * Print file SIZE to BUFFER, but don't exceed LEN characters,
282 * not including trailing 0. BUFFER should be at least LEN+1 long.
283 * This function is called for every file on panels, so avoid
284 * floating point by any means.
286 * Units: size units (filesystem sizes are 1K blocks)
287 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
289 void
290 size_trunc_len (char *buffer, int len, off_t size, int units)
292 /* Avoid taking power for every file. */
293 static const off_t power10 [] =
294 {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
295 1000000000};
296 static const char * const suffix [] =
297 {"", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL};
298 int j = 0;
300 /* Don't print more than 9 digits - use suffix. */
301 if (len == 0 || len > 9)
302 len = 9;
304 for (j = units; suffix [j] != NULL; j++) {
305 if (size == 0) {
306 if (j == units) {
307 /* Empty files will print "0" even with minimal width. */
308 g_snprintf (buffer, len + 1, "0");
309 break;
312 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
313 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s",
314 (j > 1) ? suffix[j - 1] : "B");
315 break;
318 if (size < power10 [len - (j > 0)]) {
319 g_snprintf (buffer, len + 1, "%lu%s", (unsigned long) size, suffix[j]);
320 break;
323 /* Powers of 1024, with rounding. */
324 size = (size + 512) >> 10;
328 int is_exe (mode_t mode)
330 if ((S_IXUSR & mode) || (S_IXGRP & mode) || (S_IXOTH & mode))
331 return 1;
332 return 0;
335 #define ismode(n,m) ((n & m) == m)
337 char *
338 string_perm (mode_t mode_bits)
340 static char mode[11];
342 strcpy (mode, "----------");
343 if (S_ISDIR (mode_bits))
344 mode[0] = 'd';
345 if (S_ISCHR (mode_bits))
346 mode[0] = 'c';
347 if (S_ISBLK (mode_bits))
348 mode[0] = 'b';
349 if (S_ISLNK (mode_bits))
350 mode[0] = 'l';
351 if (S_ISFIFO (mode_bits))
352 mode[0] = 'p';
353 if (S_ISSOCK (mode_bits))
354 mode[0] = 's';
355 if (S_ISDOOR (mode_bits))
356 mode[0] = 'D';
357 if (ismode (mode_bits, S_IXOTH))
358 mode[9] = 'x';
359 if (ismode (mode_bits, S_IWOTH))
360 mode[8] = 'w';
361 if (ismode (mode_bits, S_IROTH))
362 mode[7] = 'r';
363 if (ismode (mode_bits, S_IXGRP))
364 mode[6] = 'x';
365 if (ismode (mode_bits, S_IWGRP))
366 mode[5] = 'w';
367 if (ismode (mode_bits, S_IRGRP))
368 mode[4] = 'r';
369 if (ismode (mode_bits, S_IXUSR))
370 mode[3] = 'x';
371 if (ismode (mode_bits, S_IWUSR))
372 mode[2] = 'w';
373 if (ismode (mode_bits, S_IRUSR))
374 mode[1] = 'r';
375 #ifdef S_ISUID
376 if (ismode (mode_bits, S_ISUID))
377 mode[3] = (mode[3] == 'x') ? 's' : 'S';
378 #endif /* S_ISUID */
379 #ifdef S_ISGID
380 if (ismode (mode_bits, S_ISGID))
381 mode[6] = (mode[6] == 'x') ? 's' : 'S';
382 #endif /* S_ISGID */
383 #ifdef S_ISVTX
384 if (ismode (mode_bits, S_ISVTX))
385 mode[9] = (mode[9] == 'x') ? 't' : 'T';
386 #endif /* S_ISVTX */
387 return mode;
390 /* p: string which might contain an url with a password (this parameter is
391 modified in place).
392 has_prefix = 0: The first parameter is an url without a prefix
393 (user[:pass]@]machine[:port][remote-dir). Delete
394 the password.
395 has_prefix = 1: Search p for known url prefixes. If found delete
396 the password from the url.
397 Cavevat: only the first url is found
399 char *
400 strip_password (char *p, int has_prefix)
402 static const struct {
403 char *name;
404 size_t len;
405 } prefixes[] = { {"/#ftp:", 6},
406 {"/#mc:", 5},
407 {"ftp://", 6},
408 {"/#smb:", 6},
410 char *at, *inner_colon, *dir;
411 int i;
412 char *result = p;
414 for (i = 0; i < sizeof (prefixes)/sizeof (prefixes[0]); i++) {
415 char *q;
417 if (has_prefix) {
418 if((q = strstr (p, prefixes[i].name)) == 0)
419 continue;
420 else
421 p = q + prefixes[i].len;
424 if ((dir = strchr (p, PATH_SEP)) != NULL)
425 *dir = '\0';
426 /* search for any possible user */
427 at = strchr (p, '@');
429 /* We have a username */
430 if (at) {
431 *at = 0;
432 inner_colon = strchr (p, ':');
433 *at = '@';
434 if (inner_colon)
435 strcpy (inner_colon, at);
437 if (dir)
438 *dir = PATH_SEP;
439 break;
441 return (result);
444 char *strip_home_and_password(char *dir)
446 size_t len;
447 static char newdir [MC_MAXPATHLEN];
449 if (home_dir && !strncmp (dir, home_dir, len = strlen (home_dir)) &&
450 (dir[len] == PATH_SEP || dir[len] == '\0')){
451 newdir [0] = '~';
452 strcpy (&newdir [1], &dir [strlen (home_dir)]);
453 return newdir;
456 /* We do not strip homes in /#ftp tree, I do not like ~'s there
457 (see ftpfs.c why) */
458 strcpy (newdir, dir);
459 strip_password (newdir, 1);
460 return newdir;
463 static char *maybe_start_group (char *d, int do_group, int *was_wildcard)
465 if (!do_group)
466 return d;
467 if (*was_wildcard)
468 return d;
469 *was_wildcard = 1;
470 *d++ = '\\';
471 *d++ = '(';
472 return d;
475 static char *maybe_end_group (char *d, int do_group, int *was_wildcard)
477 if (!do_group)
478 return d;
479 if (!*was_wildcard)
480 return d;
481 *was_wildcard = 0;
482 *d++ = '\\';
483 *d++ = ')';
484 return d;
487 /* If shell patterns are on converts a shell pattern to a regular
488 expression. Called by regexp_match and mask_rename. */
489 /* Shouldn't we support [a-fw] type wildcards as well ?? */
490 char *convert_pattern (char *pattern, int match_type, int do_group)
492 char *s, *d;
493 char *new_pattern;
494 int was_wildcard = 0;
496 if (easy_patterns){
497 new_pattern = g_malloc (MC_MAXPATHLEN);
498 d = new_pattern;
499 if (match_type == match_file)
500 *d++ = '^';
501 for (s = pattern; *s; s++, d++){
502 switch (*s){
503 case '*':
504 d = maybe_start_group (d, do_group, &was_wildcard);
505 *d++ = '.';
506 *d = '*';
507 break;
509 case '?':
510 d = maybe_start_group (d, do_group, &was_wildcard);
511 *d = '.';
512 break;
514 case '.':
515 d = maybe_end_group (d, do_group, &was_wildcard);
516 *d++ = '\\';
517 *d = '.';
518 break;
520 default:
521 d = maybe_end_group (d, do_group, &was_wildcard);
522 *d = *s;
523 break;
526 d = maybe_end_group (d, do_group, &was_wildcard);
527 if (match_type == match_file)
528 *d++ = '$';
529 *d = 0;
530 return new_pattern;
531 } else
532 return g_strdup (pattern);
535 int regexp_match (char *pattern, char *string, int match_type)
537 static regex_t r;
538 static char *old_pattern = NULL;
539 static int old_type;
540 int rval;
542 if (!old_pattern || STRCOMP (old_pattern, pattern) || old_type != match_type){
543 if (old_pattern){
544 regfree (&r);
545 g_free (old_pattern);
546 old_pattern = NULL;
548 pattern = convert_pattern (pattern, match_type, 0);
549 if (regcomp (&r, pattern, REG_EXTENDED|REG_NOSUB|MC_ARCH_FLAGS)) {
550 g_free (pattern);
551 return -1;
553 old_pattern = pattern;
554 old_type = match_type;
556 rval = !regexec (&r, string, 0, NULL, 0);
557 return rval;
560 char *extension (char *filename)
562 char *d;
564 if (!(*filename))
565 return "";
567 d = filename + strlen (filename) - 1;
568 for (;d >= filename; d--){
569 if (*d == '.')
570 return d+1;
572 return "";
575 int get_int (char *file, char *key, int def)
577 return GetPrivateProfileInt (app_text, key, def, file);
580 int set_int (char *file, char *key, int value)
582 char buffer [BUF_TINY];
584 g_snprintf (buffer, sizeof (buffer), "%d", value);
585 return WritePrivateProfileString (app_text, key, buffer, file);
588 int exist_file (char *name)
590 return access (name, R_OK) == 0;
593 char *load_file (char *filename)
595 FILE *data_file;
596 struct stat s;
597 char *data;
598 long read_size;
600 if ((data_file = fopen (filename, "r")) == NULL){
601 return 0;
603 if (fstat (fileno (data_file), &s) != 0){
604 fclose (data_file);
605 return 0;
607 data = (char *) g_malloc (s.st_size+1);
608 read_size = fread (data, 1, s.st_size, data_file);
609 data [read_size] = 0;
610 fclose (data_file);
612 if (read_size > 0)
613 return data;
614 else {
615 g_free (data);
616 return 0;
620 char *load_mc_home_file (const char *filename, char ** allocated_filename)
622 char *hintfile_base, *hintfile;
623 char *lang;
624 char *data;
626 hintfile_base = concat_dir_and_file (mc_home, filename);
627 lang = guess_message_value ();
629 hintfile = g_strdup_printf ("%s.%s", hintfile_base, lang);
630 data = load_file (hintfile);
632 if (!data) {
633 g_free (hintfile);
634 hintfile = g_strdup_printf ("%s.%.2s", hintfile_base, lang);
635 data = load_file (hintfile);
637 if (!data) {
638 g_free (hintfile);
639 hintfile = hintfile_base;
640 data = load_file (hintfile_base);
644 g_free (lang);
646 if (hintfile != hintfile_base)
647 g_free (hintfile_base);
649 if (allocated_filename)
650 *allocated_filename = hintfile;
651 else
652 g_free (hintfile);
654 return data;
657 /* Check strftime() results. Some systems (i.e. Solaris) have different
658 short-month-name sizes for different locales */
659 size_t i18n_checktimelength (void)
661 size_t length, a, b;
662 char buf [MAX_I18NTIMELENGTH + 1];
663 time_t testtime = time (NULL);
665 a = strftime (buf, sizeof(buf)-1, _("%b %e %H:%M"), localtime(&testtime));
666 b = strftime (buf, sizeof(buf)-1, _("%b %e %Y"), localtime(&testtime));
668 length = max (a, b);
670 /* Don't handle big differences. Use standard value (email bug, please) */
671 if ( length > MAX_I18NTIMELENGTH || length < MIN_I18NTIMELENGTH )
672 length = STD_I18NTIMELENGTH;
674 return length;
677 char *file_date (time_t when)
679 static char timebuf [MAX_I18NTIMELENGTH + 1];
680 time_t current_time = time ((time_t) 0);
681 static size_t i18n_timelength = 0;
682 static char *fmtyear, *fmttime;
683 char *fmt;
685 if (i18n_timelength == 0){
686 i18n_timelength = i18n_checktimelength() + 1;
688 /* strftime() format string for old dates */
689 fmtyear = _("%b %e %Y");
690 /* strftime() format string for recent dates */
691 fmttime = _("%b %e %H:%M");
694 if (current_time > when + 6L * 30L * 24L * 60L * 60L /* Old. */
695 || current_time < when - 60L * 60L) /* In the future. */
696 /* The file is fairly old or in the future.
697 POSIX says the cutoff is 6 months old;
698 approximate this by 6*30 days.
699 Allow a 1 hour slop factor for what is considered "the future",
700 to allow for NFS server/client clock disagreement.
701 Show the year instead of the time of day. */
703 fmt = fmtyear;
704 else
705 fmt = fmttime;
707 strftime (timebuf, i18n_timelength, fmt, localtime(&when));
708 return timebuf;
711 char *extract_line (char *s, char *top)
713 static char tmp_line [BUF_MEDIUM];
714 char *t = tmp_line;
716 while (*s && *s != '\n' && (t - tmp_line) < sizeof (tmp_line)-1 && s < top)
717 *t++ = *s++;
718 *t = 0;
719 return tmp_line;
722 /* FIXME: I should write a faster version of this (Aho-Corasick stuff) */
723 char * _icase_search (char *text, char *data, int *lng)
725 char *d = text;
726 char *e = data;
727 int dlng = 0;
729 if (lng)
730 *lng = 0;
731 for (;*e; e++) {
732 while (*(e+1) == '\b' && *(e+2)) {
733 e += 2;
734 dlng += 2;
736 if (toupper((unsigned char) *d) == toupper((unsigned char) *e))
737 d++;
738 else {
739 e -= d - text;
740 d = text;
741 dlng = 0;
743 if (!*d) {
744 if (lng)
745 *lng = strlen (text) + dlng;
746 return e+1;
749 return 0;
752 /* The basename routine */
753 char *x_basename (char *s)
755 char *where;
756 return ((where = strrchr (s, PATH_SEP))) ? where + 1 : s;
760 char *unix_error_string (int error_num)
762 static char buffer [BUF_LARGE];
764 g_snprintf (buffer, sizeof (buffer), "%s (%d)",
765 g_strerror (error_num), error_num);
766 return buffer;
769 char *skip_separators (char *s)
771 for (;*s; s++)
772 if (*s != ' ' && *s != '\t' && *s != ',')
773 break;
774 return s;
777 char *skip_numbers (char *s)
779 for (;*s; s++)
780 if (!isdigit ((unsigned int) *s))
781 break;
782 return s;
785 /* Remove all control sequences from the argument string. We define
786 * "control sequence", in a sort of pidgin BNF, as follows:
788 * control-seq = Esc non-'['
789 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
791 * This scheme works for all the terminals described in my termcap /
792 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
793 * terminals. If I hear from a single person who uses such a terminal
794 * with MC, I'll be glad to add support for it. (Dugan)
795 * Non-printable characters are also removed.
798 char *strip_ctrl_codes (char *s)
800 char *w; /* Current position where the stripped data is written */
801 char *r; /* Current position where the original data is read */
803 if (!s)
804 return 0;
806 for (w = s, r = s; *r; ) {
807 if (*r == ESC_CHAR) {
808 /* Skip the control sequence's arguments */ ;
809 if (*(++r) == '[') {
810 /* strchr() matches trailing binary 0 */
811 while (*(++r) && strchr ("0123456789;?", *r));
815 * Now we are at the last character of the sequence.
816 * Skip it unless it's binary 0.
818 if (*r)
819 r++;
820 continue;
823 if (is_printable(*r))
824 *w++ = *r;
825 ++r;
827 *w = 0;
828 return s;
832 #ifndef USE_VFS
833 char *get_current_wd (char *buffer, int size)
835 char *p;
836 int len;
838 p = g_get_current_dir ();
839 len = strlen(p) + 1;
841 if (len > size) {
842 g_free (p);
843 return NULL;
846 strncpy (buffer, p, len);
847 g_free (p);
849 return buffer;
851 #endif /* !USE_VFS */
853 /* This function returns 0 if the file is not in not compressed by
854 * one of the supported compressors (gzip, bzip, bzip2). Otherwise,
855 * the compression type is returned, as defined in util.h
856 * Warning: this function moves the current file pointer */
857 int get_compression_type (int fd)
859 unsigned char magic[4];
861 /* Read the magic signature */
862 if (mc_read (fd, &magic[0], 4) != 4)
863 return COMPRESSION_NONE;
865 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
866 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236)) {
867 return COMPRESSION_GZIP;
870 /* PKZIP_MAGIC */
871 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003
872 && magic[3] == 004) {
873 /* Read compression type */
874 mc_lseek (fd, 8, SEEK_SET);
875 if (mc_read (fd, &magic[0], 2) != 2)
876 return COMPRESSION_NONE;
878 /* Gzip can handle only deflated (8) or stored (0) files */
879 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
880 return COMPRESSION_NONE;
882 /* Compatible with gzip */
883 return COMPRESSION_GZIP;
886 /* PACK_MAGIC and LZH_MAGIC and compress magic */
887 if (magic[0] == 037
888 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235)) {
889 /* Compatible with gzip */
890 return COMPRESSION_GZIP;
893 /* BZIP and BZIP2 files */
894 if ((magic[0] == 'B') && (magic[1] == 'Z') &&
895 (magic[3] >= '1') && (magic[3] <= '9')) {
896 switch (magic[2]) {
897 case '0':
898 return COMPRESSION_BZIP;
899 case 'h':
900 return COMPRESSION_BZIP2;
903 return 0;
906 char *
907 decompress_extension (int type)
909 switch (type){
910 case COMPRESSION_GZIP: return "#ugz";
911 case COMPRESSION_BZIP: return "#ubz";
912 case COMPRESSION_BZIP2: return "#ubz2";
914 /* Should never reach this place */
915 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
916 return 0;
919 /* Hooks */
920 void add_hook (Hook **hook_list, void (*hook_fn)(void *), void *data)
922 Hook *new_hook = g_new (Hook, 1);
924 new_hook->hook_fn = hook_fn;
925 new_hook->next = *hook_list;
926 new_hook->hook_data = data;
928 *hook_list = new_hook;
931 void execute_hooks (Hook *hook_list)
933 Hook *new_hook = 0;
934 Hook *p;
936 /* We copy the hook list first so tahat we let the hook
937 * function call delete_hook
940 while (hook_list){
941 add_hook (&new_hook, hook_list->hook_fn, hook_list->hook_data);
942 hook_list = hook_list->next;
944 p = new_hook;
946 while (new_hook){
947 (*new_hook->hook_fn)(new_hook->hook_data);
948 new_hook = new_hook->next;
951 for (hook_list = p; hook_list;){
952 p = hook_list;
953 hook_list = hook_list->next;
954 g_free (p);
958 void delete_hook (Hook **hook_list, void (*hook_fn)(void *))
960 Hook *current, *new_list, *next;
962 new_list = 0;
964 for (current = *hook_list; current; current = next){
965 next = current->next;
966 if (current->hook_fn == hook_fn)
967 g_free (current);
968 else
969 add_hook (&new_list, current->hook_fn, current->hook_data);
971 *hook_list = new_list;
974 int hook_present (Hook *hook_list, void (*hook_fn)(void *))
976 Hook *p;
978 for (p = hook_list; p; p = p->next)
979 if (p->hook_fn == hook_fn)
980 return 1;
981 return 0;
984 void 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 /* Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key */
996 /* Returns a newly allocated string */
997 char *convert_controls (char *s)
999 char *valcopy = g_strdup (s);
1000 char *p, *q;
1002 /* Parse the escape special character */
1003 for (p = s, q = valcopy; *p;){
1004 if (*p == '\\'){
1005 p++;
1006 if ((*p == 'e') || (*p == 'E')){
1007 p++;
1008 *q++ = ESC_CHAR;
1010 } else {
1011 if (*p == '^'){
1012 p++;
1013 if (*p == '^')
1014 *q++ = *p++;
1015 else {
1016 *p = (*p | 0x20);
1017 if (*p >= 'a' && *p <= 'z') {
1018 *q++ = *p++ - 'a' + 1;
1019 } else
1020 p++;
1022 } else
1023 *q++ = *p++;
1026 *q = 0;
1027 return valcopy;
1030 /* Reverse the string */
1031 char *reverse_string (char *string)
1033 char *str_beg = string;
1034 char *str_end = string + strlen (string);
1036 while (--str_end > str_beg){
1037 char c = *str_end;
1039 *str_end = *str_beg;
1040 *str_beg++ = c;
1042 return string;
1045 static char *resolve_symlinks (char *path)
1047 char *buf, *buf2, *p, *q, *r, c;
1048 int len;
1049 struct stat mybuf;
1051 if (*path != PATH_SEP)
1052 return NULL;
1053 r = buf = g_malloc (MC_MAXPATHLEN);
1054 buf2 = g_malloc (MC_MAXPATHLEN);
1055 *r++ = PATH_SEP;
1056 *r = 0;
1057 p = path;
1058 for (;;) {
1059 q = strchr (p + 1, PATH_SEP);
1060 if (!q) {
1061 q = strchr (p + 1, 0);
1062 if (q == p + 1)
1063 break;
1065 c = *q;
1066 *q = 0;
1067 if (mc_lstat (path, &mybuf) < 0) {
1068 g_free (buf);
1069 g_free (buf2);
1070 *q = c;
1071 return NULL;
1073 if (!S_ISLNK (mybuf.st_mode))
1074 strcpy (r, p + 1);
1075 else {
1076 len = mc_readlink (path, buf2, MC_MAXPATHLEN);
1077 if (len < 0) {
1078 g_free (buf);
1079 g_free (buf2);
1080 *q = c;
1081 return NULL;
1083 buf2 [len] = 0;
1084 if (*buf2 == PATH_SEP)
1085 strcpy (buf, buf2);
1086 else
1087 strcpy (r, buf2);
1089 canonicalize_pathname (buf);
1090 r = strchr (buf, 0);
1091 if (!*r || *(r - 1) != PATH_SEP) {
1092 *r++ = PATH_SEP;
1093 *r = 0;
1095 *q = c;
1096 p = q;
1097 if (!c)
1098 break;
1100 if (!*buf)
1101 strcpy (buf, PATH_SEP_STR);
1102 else if (*(r - 1) == PATH_SEP && r != buf + 1)
1103 *(r - 1) = 0;
1104 g_free (buf2);
1105 return buf;
1108 /* Finds out a relative path from first to second, i.e. goes as many ..
1109 * as needed up in first and then goes down using second */
1110 char *diff_two_paths (char *first, char *second)
1112 char *p, *q, *r, *s, *buf = 0;
1113 int i, j, prevlen = -1, currlen;
1115 first = resolve_symlinks (first);
1116 if (first == NULL)
1117 return NULL;
1118 for (j = 0; j < 2; j++) {
1119 p = first;
1120 if (j) {
1121 second = resolve_symlinks (second);
1122 if (second == NULL) {
1123 g_free (first);
1124 return buf;
1127 q = second;
1128 for (;;) {
1129 r = strchr (p, PATH_SEP);
1130 s = strchr (q, PATH_SEP);
1131 if (!r || !s)
1132 break;
1133 *r = 0; *s = 0;
1134 if (strcmp (p, q)) {
1135 *r = PATH_SEP; *s = PATH_SEP;
1136 break;
1137 } else {
1138 *r = PATH_SEP; *s = PATH_SEP;
1140 p = r + 1;
1141 q = s + 1;
1143 p--;
1144 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++);
1145 currlen = (i + 1) * 3 + strlen (q) + 1;
1146 if (j) {
1147 if (currlen < prevlen)
1148 g_free (buf);
1149 else {
1150 g_free (first);
1151 g_free (second);
1152 return buf;
1155 p = buf = g_malloc (currlen);
1156 prevlen = currlen;
1157 for (; i >= 0; i--, p += 3)
1158 strcpy (p, "../");
1159 strcpy (p, q);
1161 g_free (first);
1162 g_free (second);
1163 return buf;
1166 /* If filename is NULL, then we just append PATH_SEP to the dir */
1167 char *
1168 concat_dir_and_file (const char *dir, const char *file)
1170 int i = strlen (dir);
1172 if (dir [i-1] == PATH_SEP)
1173 return g_strconcat (dir, file, NULL);
1174 else
1175 return g_strconcat (dir, PATH_SEP_STR, file, NULL);
1178 /* Following code heavily borrows from libiberty, mkstemps.c */
1180 /* Number of attempts to create a temporary file */
1181 #ifndef TMP_MAX
1182 #define TMP_MAX 16384
1183 #endif /* !TMP_MAX */
1186 * Arguments:
1187 * pname (output) - pointer to the name of the temp file (needs g_free).
1188 * NULL if the function fails.
1189 * prefix - part of the filename before the random part.
1190 * Prepend $TMPDIR or /tmp if there are no path separators.
1191 * suffix - if not NULL, part of the filename after the random part.
1193 * Result:
1194 * handle of the open file or -1 if couldn't open any.
1196 int mc_mkstemps(char **pname, const char *prefix, const char *suffix)
1198 static const char letters[]
1199 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1200 static unsigned long value;
1201 struct timeval tv;
1202 char *tmpbase;
1203 char *tmpname;
1204 char *XXXXXX;
1205 int count;
1207 if (strchr(prefix, PATH_SEP) == NULL) {
1208 /* Add prefix first to find the position of XXXXXX */
1209 tmpbase = concat_dir_and_file (mc_tmpdir (), prefix);
1210 } else {
1211 tmpbase = g_strdup (prefix);
1214 tmpname = g_strconcat (tmpbase, "XXXXXX", suffix, NULL);
1215 *pname = tmpname;
1216 XXXXXX = &tmpname[strlen (tmpbase)];
1217 g_free(tmpbase);
1219 /* Get some more or less random data. */
1220 gettimeofday (&tv, NULL);
1221 value += (tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
1223 for (count = 0; count < TMP_MAX; ++count) {
1224 unsigned long v = value;
1225 int fd;
1227 /* Fill in the random bits. */
1228 XXXXXX[0] = letters[v % 62];
1229 v /= 62;
1230 XXXXXX[1] = letters[v % 62];
1231 v /= 62;
1232 XXXXXX[2] = letters[v % 62];
1233 v /= 62;
1234 XXXXXX[3] = letters[v % 62];
1235 v /= 62;
1236 XXXXXX[4] = letters[v % 62];
1237 v /= 62;
1238 XXXXXX[5] = letters[v % 62];
1240 fd = open (tmpname, O_RDWR|O_CREAT|O_EXCL, 0600);
1241 if (fd >= 0) {
1242 /* Successfully created. */
1243 return fd;
1246 /* This is a random value. It is only necessary that the next
1247 TMP_MAX values generated by adding 7777 to VALUE are different
1248 with (module 2^32). */
1249 value += 7777;
1252 /* Unsuccessful. Free the filename. */
1253 g_free (tmpname);
1254 tmpname = NULL;
1256 return -1;