ooops...
[midnight-commander.git] / src / util.c
blob88f6deeb25e817fbac6f77d52f8851cbb0f44498
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 #if defined(HAVE_RX_H) && defined(HAVE_REGCOMP)
40 #include <rx.h>
41 #else
42 #include "eregex.h"
43 #endif
45 #include "global.h"
46 #include "profile.h"
47 #include "main.h" /* mc_home */
48 #include "cmd.h" /* guess_message_value */
49 #include "../vfs/vfs.h"
50 #include "mountlist.h"
52 #ifdef HAVE_CHARSET
53 #include "charsets.h"
54 #endif
56 /* "$Id$" */
58 char app_text [] = "Midnight-Commander";
60 int easy_patterns = 1;
61 int tilde_trunc = 1;
63 #ifndef VFS_STANDALONE
64 int is_printable (int c)
66 static const unsigned char xterm_printable[] = {
67 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,
68 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 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,
70 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,
71 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,
72 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,
73 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,
74 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
77 extern int xterm_flag;
79 c &= 0xff;
80 #ifdef HAVE_CHARSET
81 if (xterm_flag)
82 return xterm_printable[c];
83 else
84 return (c > 31 && c != 127 && c != 155);
85 #else
86 if (eight_bit_clean){
87 if (full_eight_bits){
88 if (xterm_flag)
89 return xterm_printable [c];
90 else
91 return (c > 31 && c != 127 && c != 155);
92 } else
93 return ((c >31 && c < 127) || c >= 160);
94 } else
95 return (c > 31 && c < 127);
96 #endif /* !HAVE_CHARSET */
99 /* Returns the message dimensions (lines and columns) */
100 int msglen (char *text, int *lines)
102 int max = 0;
103 int line_len = 0;
105 for (*lines = 1;*text; text++){
106 if (*text == '\n'){
107 line_len = 0;
108 (*lines)++;
109 } else {
110 line_len++;
111 if (line_len > max)
112 max = line_len;
115 return max;
118 char *trim (char *s, char *d, int len)
120 int source_len = strlen (s);
122 if (source_len > len){
123 strcpy (d, s+(source_len-len));
124 d [0] = '.';
125 d [1] = '.';
126 d [2] = '.';
127 } else
128 strcpy (d, s);
129 return d;
131 #endif /* !VFS_STANDALONE */
133 char *
134 name_quote (const char *s, int quote_percent)
136 char *ret, *d;
138 d = ret = g_malloc (strlen (s)*2 + 2 + 1);
139 if (*s == '-') {
140 *d++ = '.';
141 *d++ = '/';
144 for (; *s; s++, d++) {
145 switch (*s)
147 case '%':
148 if (quote_percent)
149 *d++ = '%';
150 break;
151 case '\'':
152 case '\\':
153 case '\r':
154 case '\n':
155 case '\t':
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 case '@':
172 case '#':
173 case '$':
174 case '^':
175 case '&':
176 case '*':
177 case '(':
178 case ')':
179 *d++ = '\\';
181 *d = *s;
183 *d = '\0';
184 return ret;
187 #ifndef VFS_STANDALONE
188 char *
189 fake_name_quote (const char *s, int quote_percent)
191 return g_strdup (s);
194 /* If passed an empty txt (this usually means that there is an error)
195 * in the upper layers, we return "/"
197 char *name_trunc (char *txt, int trunc_len)
199 static char x [MC_MAXPATHLEN+MC_MAXPATHLEN];
200 int txt_len;
201 char *p;
203 if (!txt)
204 txt = PATH_SEP_STR;
206 if (trunc_len > sizeof (x)-1){
207 fprintf (stderr, _("name_trunc: too big"));
208 trunc_len = sizeof (x)-1;
210 txt_len = strlen (txt);
211 if (txt_len <= trunc_len)
212 strcpy (x, txt);
213 else if (tilde_trunc){
214 int y = (trunc_len/2) + (trunc_len % 2);
215 strncpy (x, txt, y);
216 strncpy (x+y, txt+txt_len-(trunc_len/2), trunc_len/2);
217 x [y] = '~';
218 } else {
219 strncpy (x, txt, trunc_len-1);
220 x [trunc_len-1] = '>';
222 x [trunc_len] = 0;
223 for (p = x; *p; p++)
224 if (!is_printable (*p))
225 *p = '?';
226 return x;
229 char *size_trunc (double size)
231 static char x [BUF_TINY];
232 long int divisor = 1;
233 char *xtra = "";
235 if (size > 999999999L){
236 divisor = 1024;
237 xtra = "Kb";
238 if (size/divisor > 999999999L){
239 divisor = 1024*1024;
240 xtra = "Mb";
243 g_snprintf (x, sizeof (x), "%.0f%s", (size/divisor), xtra);
244 return x;
247 char *size_trunc_sep (double size)
249 static char x [60];
250 int count;
251 char *p, *d, *y;
253 p = y = size_trunc (size);
254 p += strlen (p) - 1;
255 d = x + sizeof (x) - 1;
256 *d-- = 0;
257 while (p >= y && isalpha (*p))
258 *d-- = *p--;
259 for (count = 0; p >= y; count++){
260 if (count == 3){
261 *d-- = ',';
262 count = 0;
264 *d-- = *p--;
266 d++;
267 if (*d == ',')
268 d++;
269 return d;
273 * Print file SIZE to BUFFER, but don't exceed LEN characters,
274 * not including trailing 0. BUFFER should be at least LEN+1 long.
275 * This function is called for every file on panels, so avoid
276 * floating point by any means.
278 * Units: size units (filesystem sizes are 1K blocks)
279 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
281 void
282 size_trunc_len (char *buffer, int len, off_t size, int units)
284 /* Avoid taking power for every file. */
285 static const off_t power10 [] =
286 {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
287 1000000000};
288 static const char * const suffix [] =
289 {"", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL};
290 int j = 0;
292 /* Don't print more than 9 digits - use suffix. */
293 if (len == 0 || len > 9)
294 len = 9;
296 for (j = units; suffix [j] != NULL; j++) {
297 if (size == 0) {
298 if (j == units) {
299 /* Empty files will print "0" even with minimal width. */
300 g_snprintf (buffer, len + 1, "0");
301 break;
304 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
305 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s",
306 (j > 1) ? suffix[j - 1] : "B");
307 break;
310 if (size < power10 [len - (j > 0)]) {
311 g_snprintf (buffer, len + 1, "%lu%s", (unsigned long) size, suffix[j]);
312 break;
315 /* Powers of 1024, with rounding. */
316 size = (size + 512) >> 10;
320 int is_exe (mode_t mode)
322 if ((S_IXUSR & mode) || (S_IXGRP & mode) || (S_IXOTH & mode))
323 return 1;
324 return 0;
327 #define ismode(n,m) ((n & m) == m)
329 char *string_perm (mode_t mode_bits)
331 static char mode [11];
333 strcpy (mode, "----------");
334 if (ismode (mode_bits, S_IFDIR)) mode [0] = 'd';
335 #ifdef S_IFSOCK
336 if (ismode (mode_bits, S_IFSOCK)) mode [0] = 's';
337 #endif /* S_IFSOCK */
338 if (ismode (mode_bits, S_IXOTH)) mode [9] = 'x';
339 if (ismode (mode_bits, S_IWOTH)) mode [8] = 'w';
340 if (ismode (mode_bits, S_IROTH)) mode [7] = 'r';
341 if (ismode (mode_bits, S_IXGRP)) mode [6] = 'x';
342 if (ismode (mode_bits, S_IWGRP)) mode [5] = 'w';
343 if (ismode (mode_bits, S_IRGRP)) mode [4] = 'r';
344 if (ismode (mode_bits, S_IXUSR)) mode [3] = 'x';
345 if (ismode (mode_bits, S_IWUSR)) mode [2] = 'w';
346 if (ismode (mode_bits, S_IRUSR)) mode [1] = 'r';
347 #ifndef OS2_NT
348 if (ismode (mode_bits, S_ISUID)) mode [3] = (mode [3] == 'x') ? 's' : 'S';
349 if (ismode (mode_bits, S_ISGID)) mode [6] = (mode [6] == 'x') ? 's' : 'S';
350 if (ismode (mode_bits, S_IFCHR)) mode [0] = 'c';
351 if (ismode (mode_bits, S_IFBLK)) mode [0] = 'b';
352 if (ismode (mode_bits, S_ISVTX)) mode [9] = (mode [9] == 'x') ? 't' : 'T';
353 if (ismode (mode_bits, S_IFLNK)) mode [0] = 'l';
354 if (ismode (mode_bits, S_IFIFO)) mode [0] = 'p';
355 #ifdef S_IFDOOR
356 if (ismode (mode_bits, S_IFDOOR)) mode [0] = 'D';
357 #endif /* S_IFDOOR */
358 #endif /* !OS2_NT */
359 return mode;
362 /* p: string which might contain an url with a password (this parameter is
363 modified in place).
364 has_prefix = 0: The first parameter is an url without a prefix
365 (user[:pass]@]machine[:port][remote-dir). Delete
366 the password.
367 has_prefix = 1: Search p for known url prefixes. If found delete
368 the password from the url.
369 Cavevat: only the first url is found
371 char *
372 strip_password (char *p, int has_prefix)
374 static const struct {
375 char *name;
376 size_t len;
377 } prefixes[] = { {"/#ftp:", 6},
378 {"/#mc:", 5},
379 {"ftp://", 6},
380 {"/#smb:", 6},
382 char *at, *inner_colon, *dir;
383 int i;
384 char *result = p;
386 for (i = 0; i < sizeof (prefixes)/sizeof (prefixes[0]); i++) {
387 char *q;
389 if (has_prefix) {
390 if((q = strstr (p, prefixes[i].name)) == 0)
391 continue;
392 else
393 p = q + prefixes[i].len;
396 if ((dir = strchr (p, PATH_SEP)) != NULL)
397 *dir = '\0';
398 /* search for any possible user */
399 at = strchr (p, '@');
401 /* We have a username */
402 if (at) {
403 *at = 0;
404 inner_colon = strchr (p, ':');
405 *at = '@';
406 if (inner_colon)
407 strcpy (inner_colon, at);
409 if (dir)
410 *dir = PATH_SEP;
411 break;
413 return (result);
416 char *strip_home_and_password(char *dir)
418 size_t len;
419 static char newdir [MC_MAXPATHLEN];
421 if (home_dir && !strncmp (dir, home_dir, len = strlen (home_dir)) &&
422 (dir[len] == PATH_SEP || dir[len] == '\0')){
423 newdir [0] = '~';
424 strcpy (&newdir [1], &dir [strlen (home_dir)]);
425 return newdir;
428 /* We do not strip homes in /#ftp tree, I do not like ~'s there
429 (see ftpfs.c why) */
430 strcpy (newdir, dir);
431 strip_password (newdir, 1);
432 return newdir;
435 static char *maybe_start_group (char *d, int do_group, int *was_wildcard)
437 if (!do_group)
438 return d;
439 if (*was_wildcard)
440 return d;
441 *was_wildcard = 1;
442 *d++ = '\\';
443 *d++ = '(';
444 return d;
447 static char *maybe_end_group (char *d, int do_group, int *was_wildcard)
449 if (!do_group)
450 return d;
451 if (!*was_wildcard)
452 return d;
453 *was_wildcard = 0;
454 *d++ = '\\';
455 *d++ = ')';
456 return d;
459 /* If shell patterns are on converts a shell pattern to a regular
460 expression. Called by regexp_match and mask_rename. */
461 /* Shouldn't we support [a-fw] type wildcards as well ?? */
462 char *convert_pattern (char *pattern, int match_type, int do_group)
464 char *s, *d;
465 char *new_pattern;
466 int was_wildcard = 0;
468 if (easy_patterns){
469 new_pattern = g_malloc (MC_MAXPATHLEN);
470 d = new_pattern;
471 if (match_type == match_file)
472 *d++ = '^';
473 for (s = pattern; *s; s++, d++){
474 switch (*s){
475 case '*':
476 d = maybe_start_group (d, do_group, &was_wildcard);
477 *d++ = '.';
478 *d = '*';
479 break;
481 case '?':
482 d = maybe_start_group (d, do_group, &was_wildcard);
483 *d = '.';
484 break;
486 case '.':
487 d = maybe_end_group (d, do_group, &was_wildcard);
488 *d++ = '\\';
489 *d = '.';
490 break;
492 default:
493 d = maybe_end_group (d, do_group, &was_wildcard);
494 *d = *s;
495 break;
498 d = maybe_end_group (d, do_group, &was_wildcard);
499 if (match_type == match_file)
500 *d++ = '$';
501 *d = 0;
502 return new_pattern;
503 } else
504 return g_strdup (pattern);
507 int regexp_match (char *pattern, char *string, int match_type)
509 static regex_t r;
510 static char *old_pattern = NULL;
511 static int old_type;
512 int rval;
514 if (!old_pattern || STRCOMP (old_pattern, pattern) || old_type != match_type){
515 if (old_pattern){
516 regfree (&r);
517 g_free (old_pattern);
518 old_pattern = NULL;
520 pattern = convert_pattern (pattern, match_type, 0);
521 if (regcomp (&r, pattern, REG_EXTENDED|REG_NOSUB|MC_ARCH_FLAGS)) {
522 g_free (pattern);
523 return -1;
525 old_pattern = pattern;
526 old_type = match_type;
528 rval = !regexec (&r, string, 0, NULL, 0);
529 return rval;
532 char *extension (char *filename)
534 char *d;
536 if (!(*filename))
537 return "";
539 d = filename + strlen (filename) - 1;
540 for (;d >= filename; d--){
541 if (*d == '.')
542 return d+1;
544 return "";
547 int get_int (char *file, char *key, int def)
549 return GetPrivateProfileInt (app_text, key, def, file);
552 int set_int (char *file, char *key, int value)
554 char buffer [BUF_TINY];
556 g_snprintf (buffer, sizeof (buffer), "%d", value);
557 return WritePrivateProfileString (app_text, key, buffer, file);
560 int exist_file (char *name)
562 return access (name, R_OK) == 0;
565 char *load_file (char *filename)
567 FILE *data_file;
568 struct stat s;
569 char *data;
570 long read_size;
572 if ((data_file = fopen (filename, "r")) == NULL){
573 return 0;
575 if (fstat (fileno (data_file), &s) != 0){
576 fclose (data_file);
577 return 0;
579 data = (char *) g_malloc (s.st_size+1);
580 read_size = fread (data, 1, s.st_size, data_file);
581 data [read_size] = 0;
582 fclose (data_file);
584 if (read_size > 0)
585 return data;
586 else {
587 g_free (data);
588 return 0;
592 char *load_mc_home_file (const char *filename, char ** allocated_filename)
594 char *hintfile_base, *hintfile;
595 char *lang;
596 char *data;
598 hintfile_base = concat_dir_and_file (mc_home, filename);
599 lang = guess_message_value ();
601 hintfile = g_strdup_printf ("%s.%s", hintfile_base, lang);
602 data = load_file (hintfile);
604 if (!data) {
605 g_free (hintfile);
606 hintfile = g_strdup_printf ("%s.%.2s", hintfile_base, lang);
607 data = load_file (hintfile);
609 if (!data) {
610 g_free (hintfile);
611 hintfile = hintfile_base;
612 data = load_file (hintfile_base);
616 g_free (lang);
618 if (hintfile != hintfile_base)
619 g_free (hintfile_base);
621 if (allocated_filename)
622 *allocated_filename = hintfile;
623 else
624 g_free (hintfile);
626 return data;
629 /* Check strftime() results. Some systems (i.e. Solaris) have different
630 short-month-name sizes for different locales */
631 size_t i18n_checktimelength (void)
633 size_t length, a, b;
634 char buf [MAX_I18NTIMELENGTH + 1];
635 time_t testtime = time (NULL);
637 a = strftime (buf, sizeof(buf)-1, _("%b %e %H:%M"), localtime(&testtime));
638 b = strftime (buf, sizeof(buf)-1, _("%b %e %Y"), localtime(&testtime));
640 length = max (a, b);
642 /* Don't handle big differences. Use standard value (email bug, please) */
643 if ( length > MAX_I18NTIMELENGTH || length < MIN_I18NTIMELENGTH )
644 length = STD_I18NTIMELENGTH;
646 return length;
649 char *file_date (time_t when)
651 static char timebuf [MAX_I18NTIMELENGTH + 1];
652 time_t current_time = time ((time_t) 0);
653 static size_t i18n_timelength = 0;
654 static char *fmtyear, *fmttime;
655 char *fmt;
657 if (i18n_timelength == 0){
658 i18n_timelength = i18n_checktimelength() + 1;
660 /* strftime() format string for old dates */
661 fmtyear = _("%b %e %Y");
662 /* strftime() format string for recent dates */
663 fmttime = _("%b %e %H:%M");
666 if (current_time > when + 6L * 30L * 24L * 60L * 60L /* Old. */
667 || current_time < when - 60L * 60L) /* In the future. */
668 /* The file is fairly old or in the future.
669 POSIX says the cutoff is 6 months old;
670 approximate this by 6*30 days.
671 Allow a 1 hour slop factor for what is considered "the future",
672 to allow for NFS server/client clock disagreement.
673 Show the year instead of the time of day. */
675 fmt = fmtyear;
676 else
677 fmt = fmttime;
679 strftime (timebuf, i18n_timelength, fmt, localtime(&when));
680 return timebuf;
683 /* Like file_date, but packs the data to fit in 10 columns */
684 char *file_date_pck (time_t when)
686 /* FIXME: Should return only 10 chars, not 14 */
687 return file_date (when);
690 char *extract_line (char *s, char *top)
692 static char tmp_line [BUF_MEDIUM];
693 char *t = tmp_line;
695 while (*s && *s != '\n' && (t - tmp_line) < sizeof (tmp_line)-1 && s < top)
696 *t++ = *s++;
697 *t = 0;
698 return tmp_line;
701 /* FIXME: I should write a faster version of this (Aho-Corasick stuff) */
702 char * _icase_search (char *text, char *data, int *lng)
704 char *d = text;
705 char *e = data;
706 int dlng = 0;
708 if (lng)
709 *lng = 0;
710 for (;*e; e++) {
711 while (*(e+1) == '\b' && *(e+2)) {
712 e += 2;
713 dlng += 2;
715 if (toupper((unsigned char) *d) == toupper((unsigned char) *e))
716 d++;
717 else {
718 e -= d - text;
719 d = text;
720 dlng = 0;
722 if (!*d) {
723 if (lng)
724 *lng = strlen (text) + dlng;
725 return e+1;
728 return 0;
731 /* The basename routine */
732 char *x_basename (char *s)
734 char *where;
735 return ((where = strrchr (s, PATH_SEP))) ? where + 1 : s;
738 void my_putenv (char *name, char *data)
740 char *full;
742 full = malloc (strlen (name) + strlen (data) + 2);
743 strcpy (full, name);
744 strcat (full, "=");
745 strcat (full, data);
746 putenv (full);
748 /* WARNING: NEVER FREE THE full VARIABLE!!!!!!!!!!!!!!!!!!!!!!!! */
749 /* It is used by putenv. Freeing it will corrupt the environment */
751 #endif /* !VFS_STANDALONE */
753 char *unix_error_string (int error_num)
755 static char buffer [BUF_LARGE];
757 g_snprintf (buffer, sizeof (buffer), "%s (%d)",
758 g_strerror (error_num), error_num);
759 return buffer;
762 #ifndef VFS_STANDALONE
763 long blocks2kilos (int blocks, int bsize)
765 if (bsize > 1024){
766 return blocks * (bsize / 1024);
767 } else if (bsize < 1024){
768 return blocks / (1024 /bsize);
769 } else
770 return blocks;
773 char *skip_separators (char *s)
775 for (;*s; s++)
776 if (*s != ' ' && *s != '\t' && *s != ',')
777 break;
778 return s;
781 char *skip_numbers (char *s)
783 for (;*s; s++)
784 if (!isdigit (*s))
785 break;
786 return s;
789 /* Remove all control sequences from the argument string. We define
790 * "control sequence", in a sort of pidgin BNF, as follows:
792 * control-seq = Esc non-'['
793 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
795 * This scheme works for all the terminals described in my termcap /
796 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
797 * terminals. If I hear from a single person who uses such a terminal
798 * with MC, I'll be glad to add support for it. (Dugan)
799 * Non-printable characters are also removed.
802 char *strip_ctrl_codes (char *s)
804 char *w; /* Current position where the stripped data is written */
805 char *r; /* Current position where the original data is read */
807 if (!s)
808 return 0;
810 for (w = s, r = s; *r; ++r)
811 if (*r != ESC_CHAR){
812 if (is_printable(*r))
813 *w++ = *r;
814 } else {
815 if (*(++r) == '[') {
816 while (strchr ("0123456789;?", *(++r)))
817 /* Skip the control sequence's arguments */ ;
820 *w = 0;
821 return s;
824 #endif /* !VFS_STANDALONE */
826 #ifndef USE_VFS
827 char *get_current_wd (char *buffer, int size)
829 char *p;
830 int len;
832 p = g_get_current_dir ();
833 len = strlen(p) + 1;
835 if (len > size) {
836 g_free (p);
837 return NULL;
840 strncpy (buffer, p, len);
841 g_free (p);
843 return buffer;
845 #endif /* !USE_VFS */
847 #define CHECK(x) if (x == -1) return 0;
849 static long
850 get_small_endian_long (int fd)
852 unsigned char buffer [4];
854 CHECK (mc_read (fd, buffer, 4));
855 return (buffer [3] << 24) | (buffer [2] << 16) | (buffer [1] << 8) | buffer [0];
859 * This constant makes the magic array on the stack be larger than
860 * it needs because Linux when reading the second byte of /proc/locks
861 * for example will write 2 bytes, even if we only asked for one
863 #define LINUX_HAS_PROBLEMS_WHEN_READING_PROC_LOCKS_ON_SOME_KERNELS 40
865 /* This function returns 0 if the file is not in gunzip format */
866 /* or how much memory must be allocated to load the gziped file */
867 /* Warning: this function moves the current file pointer */
868 long int is_gunzipable (int fd, int *type)
870 unsigned char magic [4+LINUX_HAS_PROBLEMS_WHEN_READING_PROC_LOCKS_ON_SOME_KERNELS];
872 *type = ISGUNZIPABLE_GUNZIP;
874 /* Read the magic signature */
875 CHECK (mc_read (fd, &magic [0], 4));
877 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
878 if (magic [0] == 037 && (magic [1] == 0213 || magic [1] == 0236)){
879 /* Read the uncompressed size of the file */
880 mc_lseek (fd, -4, SEEK_END);
881 return get_small_endian_long (fd);
884 /* PKZIP_MAGIC */
885 if (magic [0] == 0120 && magic [1] == 0113 && magic [2] == 003 && magic [3] == 004){
886 /* Read compression type */
887 mc_lseek (fd, 8, SEEK_SET);
888 CHECK (mc_read (fd, &magic [0], 2));
890 /* Gzip can handle only deflated (8) or stored (0) files */
891 if ((magic [0] != 8 && magic [0] != 0) || magic [1] != 0)
892 return 0;
893 /* Read the uncompressed size of the first file in the archive */
894 mc_lseek (fd, 22, SEEK_SET);
895 return get_small_endian_long (fd);
898 /* PACK_MAGIC and LZH_MAGIC and compress magic */
899 if (magic [0] == 037 && (magic [1] == 036 || magic [1] == 0240 || magic [1] == 0235)){
900 /* In case the file is packed, sco lzhed or compress_magic, the */
901 /* program guesses that the uncompressed size is (at most) four */
902 /* times the length of the compressed size, if the compression */
903 /* ratio is more than 4:1 the end of the file is not displayed */
904 return 4*mc_lseek (fd, 0, SEEK_END);
907 /* BZIP and BZIP2 files */
908 if ((magic[0] == 'B') && (magic[1] == 'Z') &&
909 (magic [3] >= '1') && (magic [3] <= '9')){
910 switch (magic[2]) {
911 case '0':
912 *type = ISGUNZIPABLE_BZIP;
913 return 5*mc_lseek (fd, 0, SEEK_END);
914 case 'h':
915 *type = ISGUNZIPABLE_BZIP2;
916 return 5*mc_lseek (fd, 0, SEEK_END);
919 return 0;
922 char *
923 decompress_extension (int type)
925 switch (type){
926 case ISGUNZIPABLE_GUNZIP: return "#ugz";
927 case ISGUNZIPABLE_BZIP: return "#ubz";
928 case ISGUNZIPABLE_BZIP2: return "#ubz2";
930 /* Should never reach this place */
931 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
932 return 0;
935 char *
936 decompress_command (int type)
938 switch (type){
939 case ISGUNZIPABLE_GUNZIP:
940 return "gzip -cdf";
942 case ISGUNZIPABLE_BZIP:
943 return "bzip -d";
945 case ISGUNZIPABLE_BZIP2:
946 return "bzip2 -d";
948 /* Should never reach this place */
949 fprintf (stderr, "Fatal: decompress_command called with an unknown argument\n");
950 return 0;
953 void
954 decompress_command_and_arg (int type, char **cmd, char **flags)
956 switch (type){
957 case ISGUNZIPABLE_GUNZIP:
958 *cmd = "gzip";
959 *flags = "-cdf";
960 return;
962 case ISGUNZIPABLE_BZIP:
963 *cmd = "bzip";
964 *flags = "-d";
965 return;
968 case ISGUNZIPABLE_BZIP2:
969 *cmd = "bzip2";
970 *flags = "-d";
971 return;
973 *cmd = 0;
974 *flags = 0;
976 /* Should never reach this place */
977 fprintf (stderr, "Fatal: decompress_command called with an unknown argument\n");
980 #ifndef VFS_STANDALONE
981 /* Hooks */
982 void add_hook (Hook **hook_list, void (*hook_fn)(void *), void *data)
984 Hook *new_hook = g_new (Hook, 1);
986 new_hook->hook_fn = hook_fn;
987 new_hook->next = *hook_list;
988 new_hook->hook_data = data;
990 *hook_list = new_hook;
993 void execute_hooks (Hook *hook_list)
995 Hook *new_hook = 0;
996 Hook *p;
998 /* We copy the hook list first so tahat we let the hook
999 * function call delete_hook
1002 while (hook_list){
1003 add_hook (&new_hook, hook_list->hook_fn, hook_list->hook_data);
1004 hook_list = hook_list->next;
1006 p = new_hook;
1008 while (new_hook){
1009 (*new_hook->hook_fn)(new_hook->hook_data);
1010 new_hook = new_hook->next;
1013 for (hook_list = p; hook_list;){
1014 p = hook_list;
1015 hook_list = hook_list->next;
1016 g_free (p);
1020 void delete_hook (Hook **hook_list, void (*hook_fn)(void *))
1022 Hook *current, *new_list, *next;
1024 new_list = 0;
1026 for (current = *hook_list; current; current = next){
1027 next = current->next;
1028 if (current->hook_fn == hook_fn)
1029 g_free (current);
1030 else
1031 add_hook (&new_list, current->hook_fn, current->hook_data);
1033 *hook_list = new_list;
1036 int hook_present (Hook *hook_list, void (*hook_fn)(void *))
1038 Hook *p;
1040 for (p = hook_list; p; p = p->next)
1041 if (p->hook_fn == hook_fn)
1042 return 1;
1043 return 0;
1046 void wipe_password (char *passwd)
1048 char *p = passwd;
1050 if (!p)
1051 return;
1052 for (;*p ; p++)
1053 *p = 0;
1054 g_free (passwd);
1057 /* Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key */
1058 /* Returns a newly allocated string */
1059 char *convert_controls (char *s)
1061 char *valcopy = g_strdup (s);
1062 char *p, *q;
1064 /* Parse the escape special character */
1065 for (p = s, q = valcopy; *p;){
1066 if (*p == '\\'){
1067 p++;
1068 if ((*p == 'e') || (*p == 'E')){
1069 p++;
1070 *q++ = ESC_CHAR;
1072 } else {
1073 if (*p == '^'){
1074 p++;
1075 if (*p == '^')
1076 *q++ = *p++;
1077 else {
1078 *p = (*p | 0x20);
1079 if (*p >= 'a' && *p <= 'z') {
1080 *q++ = *p++ - 'a' + 1;
1081 } else
1082 p++;
1084 } else
1085 *q++ = *p++;
1088 *q++ = 0;
1089 return valcopy;
1092 /* Reverse the string */
1093 char *reverse_string (char *string)
1095 int len = strlen (string);
1096 int i;
1097 const int steps = len/2;
1099 for (i = 0; i < steps; i++){
1100 char c = string [i];
1102 string [i] = string [len-i-1];
1103 string [len-i-1] = c;
1105 return string;
1108 char *resolve_symlinks (char *path)
1110 char *buf, *buf2, *p, *q, *r, c;
1111 int len;
1112 struct stat mybuf;
1114 if (*path != PATH_SEP)
1115 return NULL;
1116 r = buf = g_malloc (MC_MAXPATHLEN);
1117 buf2 = g_malloc (MC_MAXPATHLEN);
1118 *r++ = PATH_SEP;
1119 *r = 0;
1120 p = path;
1121 for (;;) {
1122 q = strchr (p + 1, PATH_SEP);
1123 if (!q) {
1124 q = strchr (p + 1, 0);
1125 if (q == p + 1)
1126 break;
1128 c = *q;
1129 *q = 0;
1130 if (mc_lstat (path, &mybuf) < 0) {
1131 g_free (buf);
1132 g_free (buf2);
1133 *q = c;
1134 return NULL;
1136 if (!S_ISLNK (mybuf.st_mode))
1137 strcpy (r, p + 1);
1138 else {
1139 len = mc_readlink (path, buf2, MC_MAXPATHLEN);
1140 if (len < 0) {
1141 g_free (buf);
1142 g_free (buf2);
1143 *q = c;
1144 return NULL;
1146 buf2 [len] = 0;
1147 if (*buf2 == PATH_SEP)
1148 strcpy (buf, buf2);
1149 else
1150 strcpy (r, buf2);
1152 canonicalize_pathname (buf);
1153 r = strchr (buf, 0);
1154 if (!*r || *(r - 1) != PATH_SEP) {
1155 *r++ = PATH_SEP;
1156 *r = 0;
1158 *q = c;
1159 p = q;
1160 if (!c)
1161 break;
1163 if (!*buf)
1164 strcpy (buf, PATH_SEP_STR);
1165 else if (*(r - 1) == PATH_SEP && r != buf + 1)
1166 *(r - 1) = 0;
1167 g_free (buf2);
1168 return buf;
1171 /* Finds out a relative path from first to second, i.e. goes as many ..
1172 * as needed up in first and then goes down using second */
1173 char *diff_two_paths (char *first, char *second)
1175 char *p, *q, *r, *s, *buf = 0;
1176 int i, j, prevlen = -1, currlen;
1178 first = resolve_symlinks (first);
1179 if (first == NULL)
1180 return NULL;
1181 for (j = 0; j < 2; j++) {
1182 p = first;
1183 if (j) {
1184 second = resolve_symlinks (second);
1185 if (second == NULL) {
1186 g_free (first);
1187 return buf;
1190 q = second;
1191 for (;;) {
1192 r = strchr (p, PATH_SEP);
1193 s = strchr (q, PATH_SEP);
1194 if (!r || !s)
1195 break;
1196 *r = 0; *s = 0;
1197 if (strcmp (p, q)) {
1198 *r = PATH_SEP; *s = PATH_SEP;
1199 break;
1200 } else {
1201 *r = PATH_SEP; *s = PATH_SEP;
1203 p = r + 1;
1204 q = s + 1;
1206 p--;
1207 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++);
1208 currlen = (i + 1) * 3 + strlen (q) + 1;
1209 if (j) {
1210 if (currlen < prevlen)
1211 g_free (buf);
1212 else {
1213 g_free (first);
1214 g_free (second);
1215 return buf;
1218 p = buf = g_malloc (currlen);
1219 prevlen = currlen;
1220 for (; i >= 0; i--, p += 3)
1221 strcpy (p, "../");
1222 strcpy (p, q);
1224 g_free (first);
1225 g_free (second);
1226 return buf;
1229 #ifndef HAVE_TRUNCATE
1230 /* On SCO and Windows NT systems */
1231 int my_ftruncate (int fd, long size)
1233 #ifdef OS2_NT
1234 if(_chsize(fd, size))
1235 return -1;
1236 else
1237 return 0;
1238 #else
1239 struct flock lk;
1241 lk.l_whence = 0;
1242 lk.l_start = size;
1243 lk.l_len = 0;
1245 return fcntl (fd, F_FREESP, &lk);
1246 #endif /* !OS2_NT */
1249 int truncate (const char *path, long size)
1251 int fd;
1252 int res;
1254 fd = open (path, O_RDWR, 0);
1255 if (fd < 0)
1256 return fd;
1257 res = my_ftruncate (fd, size);
1258 close (fd);
1259 if (res < 0)
1260 return res;
1261 return 0;
1265 #endif /* !HAVE_TRUNCATE */
1266 #endif /* !VFS_STANDALONE */
1268 /* If filename is NULL, then we just append PATH_SEP to the dir */
1269 char *
1270 concat_dir_and_file (const char *dir, const char *file)
1272 int i = strlen (dir);
1274 if (dir [i-1] == PATH_SEP)
1275 return g_strconcat (dir, file, NULL);
1276 else
1277 return g_strconcat (dir, PATH_SEP_STR, file, NULL);
1280 /* Following code heavily borrows from libiberty, mkstemps.c */
1282 /* Number of attempts to create a temporary file */
1283 #ifndef TMP_MAX
1284 #define TMP_MAX 16384
1285 #endif /* !TMP_MAX */
1288 * Arguments:
1289 * pname (output) - pointer to the name of the temp file (needs g_free).
1290 * NULL if the function fails.
1291 * prefix - part of the filename before the random part.
1292 * Prepend $TMPDIR or /tmp if there are no path separators.
1293 * suffix - if not NULL, part of the filename after the random part.
1295 * Result:
1296 * handle of the open file or -1 if couldn't open any.
1298 int mc_mkstemps(char **pname, const char *prefix, const char *suffix)
1300 static const char letters[]
1301 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1302 static unsigned long value;
1303 struct timeval tv;
1304 char *tmpbase;
1305 char *tmpname;
1306 char *XXXXXX;
1307 int count;
1309 if (strchr(prefix, PATH_SEP) == NULL) {
1310 char *tmpdir;
1312 tmpdir = getenv("TMPDIR");
1313 if (!tmpdir) {
1314 tmpdir = TMPDIR_DEFAULT;
1317 /* Add prefix first to find the position of XXXXXX */
1318 tmpbase = concat_dir_and_file (tmpdir, prefix);
1319 } else {
1320 tmpbase = g_strdup (prefix);
1323 tmpname = g_strconcat (tmpbase, "XXXXXX", suffix, NULL);
1324 *pname = tmpname;
1325 XXXXXX = &tmpname[strlen (tmpbase)];
1326 g_free(tmpbase);
1328 /* Get some more or less random data. */
1329 gettimeofday (&tv, NULL);
1330 value += (tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
1332 for (count = 0; count < TMP_MAX; ++count) {
1333 unsigned long v = value;
1334 int fd;
1336 /* Fill in the random bits. */
1337 XXXXXX[0] = letters[v % 62];
1338 v /= 62;
1339 XXXXXX[1] = letters[v % 62];
1340 v /= 62;
1341 XXXXXX[2] = letters[v % 62];
1342 v /= 62;
1343 XXXXXX[3] = letters[v % 62];
1344 v /= 62;
1345 XXXXXX[4] = letters[v % 62];
1346 v /= 62;
1347 XXXXXX[5] = letters[v % 62];
1349 fd = open (tmpname, O_RDWR|O_CREAT|O_EXCL, 0600);
1350 if (fd >= 0) {
1351 /* Successfully created. */
1352 return fd;
1355 /* This is a random value. It is only necessary that the next
1356 TMP_MAX values generated by adding 7777 to VALUE are different
1357 with (module 2^32). */
1358 value += 7777;
1361 /* Unsuccessful. Free the filename. */
1362 g_free (tmpname);
1363 tmpname = NULL;
1365 return -1;