*** empty log message ***
[midnight-commander.git] / src / util.c
bloba5fce3e55d3e717f07016e7d19ad992a13b82c10
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 <regex.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 align_extensions = 1;
62 int tilde_trunc = 1;
64 #ifndef VFS_STANDALONE
65 int is_printable (int c)
67 #ifndef HAVE_X
68 static const unsigned char xterm_printable[] = {
69 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,
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,1,
71 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,
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,0,
73 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,
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,
75 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,
76 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
79 extern int xterm_flag;
81 c &= 0xff;
82 #ifdef HAVE_CHARSET
83 if (display_codepage < 0) {
84 if (xterm_flag)
85 return xterm_printable[c];
86 else
87 return (c > 31 && c != 127 && c != 155);
88 } else
89 return printable[ c ];
90 #else
91 if (eight_bit_clean){
92 if (full_eight_bits){
93 if (xterm_flag)
94 return xterm_printable [c];
95 else
96 return (c > 31 && c != 127 && c != 155);
97 } else
98 return ((c >31 && c < 127) || c >= 160);
99 } else
100 return (c > 31 && c < 127);
101 #endif /* !HAVE_CHARSET */
102 #else
103 return 1;
104 #endif /* HAVE_X */
107 /* Returns the message dimensions (lines and columns) */
108 int msglen (char *text, int *lines)
110 int max = 0;
111 int line_len = 0;
113 for (*lines = 1;*text; text++){
114 if (*text == '\n'){
115 line_len = 0;
116 (*lines)++;
117 } else {
118 line_len++;
119 if (line_len > max)
120 max = line_len;
123 return max;
126 char *trim (char *s, char *d, int len)
128 int source_len = strlen (s);
130 if (source_len > len){
131 strcpy (d, s+(source_len-len));
132 d [0] = '.';
133 d [1] = '.';
134 d [2] = '.';
135 } else
136 strcpy (d, s);
137 return d;
139 #endif /* !VFS_STANDALONE */
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 #ifndef VFS_STANDALONE
196 char *
197 fake_name_quote (const char *s, int quote_percent)
199 return g_strdup (s);
202 /* If passed an empty txt (this usually means that there is an error)
203 * in the upper layers, we return "/"
205 char *name_trunc (char *txt, int trunc_len)
207 static char x [MC_MAXPATHLEN+MC_MAXPATHLEN];
208 int txt_len;
209 char *p;
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 (*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 *string_perm (mode_t mode_bits)
339 static char mode [11];
341 strcpy (mode, "----------");
342 if (ismode (mode_bits, S_IFDIR)) mode [0] = 'd';
343 #ifdef S_IFSOCK
344 if (ismode (mode_bits, S_IFSOCK)) mode [0] = 's';
345 #endif /* S_IFSOCK */
346 if (ismode (mode_bits, S_IXOTH)) mode [9] = 'x';
347 if (ismode (mode_bits, S_IWOTH)) mode [8] = 'w';
348 if (ismode (mode_bits, S_IROTH)) mode [7] = 'r';
349 if (ismode (mode_bits, S_IXGRP)) mode [6] = 'x';
350 if (ismode (mode_bits, S_IWGRP)) mode [5] = 'w';
351 if (ismode (mode_bits, S_IRGRP)) mode [4] = 'r';
352 if (ismode (mode_bits, S_IXUSR)) mode [3] = 'x';
353 if (ismode (mode_bits, S_IWUSR)) mode [2] = 'w';
354 if (ismode (mode_bits, S_IRUSR)) mode [1] = 'r';
355 #ifndef OS2_NT
356 if (ismode (mode_bits, S_ISUID)) mode [3] = (mode [3] == 'x') ? 's' : 'S';
357 if (ismode (mode_bits, S_ISGID)) mode [6] = (mode [6] == 'x') ? 's' : 'S';
358 if (ismode (mode_bits, S_IFCHR)) mode [0] = 'c';
359 if (ismode (mode_bits, S_IFBLK)) mode [0] = 'b';
360 if (ismode (mode_bits, S_ISVTX)) mode [9] = (mode [9] == 'x') ? 't' : 'T';
361 if (ismode (mode_bits, S_IFLNK)) mode [0] = 'l';
362 if (ismode (mode_bits, S_IFIFO)) mode [0] = 'p';
363 #endif /* !OS2_NT */
364 return mode;
367 /* p: string which might contain an url with a password (this parameter is
368 modified in place).
369 has_prefix = 0: The first parameter is an url without a prefix
370 (user[:pass]@]machine[:port][remote-dir). Delete
371 the password.
372 has_prefix = 1: Search p for known url prefixes. If found delete
373 the password from the url.
374 Cavevat: only the first url is found
376 char *
377 strip_password (char *p, int has_prefix)
379 static const struct {
380 char *name;
381 size_t len;
382 } prefixes[] = { {"/#ftp:", 6},
383 {"/#mc:", 5},
384 {"ftp://", 6},
385 {"/#smb:", 6},
387 char *at, *inner_colon, *dir;
388 int i;
389 char *result = p;
391 for (i = 0; i < sizeof (prefixes)/sizeof (prefixes[0]); i++) {
392 char *q;
394 if (has_prefix) {
395 if((q = strstr (p, prefixes[i].name)) == 0)
396 continue;
397 else
398 p = q + prefixes[i].len;
401 if ((dir = strchr (p, PATH_SEP)) != NULL)
402 *dir = '\0';
403 /* search for any possible user */
404 at = strchr (p, '@');
406 /* We have a username */
407 if (at) {
408 *at = 0;
409 inner_colon = strchr (p, ':');
410 *at = '@';
411 if (inner_colon)
412 strcpy (inner_colon, at);
414 if (dir)
415 *dir = PATH_SEP;
416 break;
418 return (result);
421 char *strip_home_and_password(char *dir)
423 size_t len;
424 static char newdir [MC_MAXPATHLEN];
426 if (home_dir && !strncmp (dir, home_dir, len = strlen (home_dir)) &&
427 (dir[len] == PATH_SEP || dir[len] == '\0')){
428 newdir [0] = '~';
429 strcpy (&newdir [1], &dir [strlen (home_dir)]);
430 return newdir;
433 /* We do not strip homes in /#ftp tree, I do not like ~'s there
434 (see ftpfs.c why) */
435 strcpy (newdir, dir);
436 strip_password (newdir, 1);
437 return newdir;
440 static char *maybe_start_group (char *d, int do_group, int *was_wildcard)
442 if (!do_group)
443 return d;
444 if (*was_wildcard)
445 return d;
446 *was_wildcard = 1;
447 *d++ = '\\';
448 *d++ = '(';
449 return d;
452 static char *maybe_end_group (char *d, int do_group, int *was_wildcard)
454 if (!do_group)
455 return d;
456 if (!*was_wildcard)
457 return d;
458 *was_wildcard = 0;
459 *d++ = '\\';
460 *d++ = ')';
461 return d;
464 /* If shell patterns are on converts a shell pattern to a regular
465 expression. Called by regexp_match and mask_rename. */
466 /* Shouldn't we support [a-fw] type wildcards as well ?? */
467 char *convert_pattern (char *pattern, int match_type, int do_group)
469 char *s, *d;
470 char *new_pattern;
471 int was_wildcard = 0;
473 if (easy_patterns){
474 new_pattern = g_malloc (MC_MAXPATHLEN);
475 d = new_pattern;
476 if (match_type == match_file)
477 *d++ = '^';
478 for (s = pattern; *s; s++, d++){
479 switch (*s){
480 case '*':
481 d = maybe_start_group (d, do_group, &was_wildcard);
482 *d++ = '.';
483 *d = '*';
484 break;
486 case '?':
487 d = maybe_start_group (d, do_group, &was_wildcard);
488 *d = '.';
489 break;
491 case '.':
492 d = maybe_end_group (d, do_group, &was_wildcard);
493 *d++ = '\\';
494 *d = '.';
495 break;
497 default:
498 d = maybe_end_group (d, do_group, &was_wildcard);
499 *d = *s;
500 break;
503 d = maybe_end_group (d, do_group, &was_wildcard);
504 if (match_type == match_file)
505 *d++ = '$';
506 *d = 0;
507 return new_pattern;
508 } else
509 return g_strdup (pattern);
512 int regexp_match (char *pattern, char *string, int match_type)
514 static regex_t r;
515 static char *old_pattern = NULL;
516 static int old_type;
517 int rval;
519 if (!old_pattern || STRCOMP (old_pattern, pattern) || old_type != match_type){
520 if (old_pattern){
521 regfree (&r);
522 g_free (old_pattern);
523 old_pattern = NULL;
525 pattern = convert_pattern (pattern, match_type, 0);
526 if (regcomp (&r, pattern, REG_EXTENDED|REG_NOSUB|MC_ARCH_FLAGS)) {
527 g_free (pattern);
528 return -1;
530 old_pattern = pattern;
531 old_type = match_type;
533 rval = !regexec (&r, string, 0, NULL, 0);
534 return rval;
537 char *extension (char *filename)
539 char *d;
541 if (!(*filename))
542 return "";
544 d = filename + strlen (filename) - 1;
545 for (;d >= filename; d--){
546 if (*d == '.')
547 return d+1;
549 return "";
552 /* This routine uses the fact that x is at most 14 chars or so */
553 char *split_extension (char *x, int pad)
555 return x;
557 /* Buggy code
558 if (!align_extensions)
559 return x;
561 if (strlen (x) >= pad)
562 return x;
564 if ((ext = extension (x)) == x || *ext == 0)
565 return x;
567 strcpy (xbuf, x);
568 for (i = strlen (x); i < pad; i++)
569 xbuf [i] = ' ';
570 xbuf [pad] = 0;
572 l = strlen (ext);
573 for (i = 0; i < l; i++)
574 xbuf [pad-i] = *(ext+l-i-1);
575 for (i = xbuf + (ext - x); i <
576 return xbuf; */
579 int get_int (char *file, char *key, int def)
581 return GetPrivateProfileInt (app_text, key, def, file);
584 int set_int (char *file, char *key, int value)
586 char buffer [BUF_TINY];
588 g_snprintf (buffer, sizeof (buffer), "%d", value);
589 return WritePrivateProfileString (app_text, key, buffer, file);
592 int exist_file (char *name)
594 return access (name, R_OK) == 0;
597 char *load_file (char *filename)
599 FILE *data_file;
600 struct stat s;
601 char *data;
602 long read_size;
604 if ((data_file = fopen (filename, "r")) == NULL){
605 return 0;
607 if (fstat (fileno (data_file), &s) != 0){
608 fclose (data_file);
609 return 0;
611 data = (char *) g_malloc (s.st_size+1);
612 read_size = fread (data, 1, s.st_size, data_file);
613 data [read_size] = 0;
614 fclose (data_file);
616 if (read_size > 0)
617 return data;
618 else {
619 g_free (data);
620 return 0;
624 char *load_mc_home_file (const char *filename, char ** allocated_filename)
626 char *hintfile_base, *hintfile;
627 char *lang;
628 char *data;
630 hintfile_base = concat_dir_and_file (mc_home, filename);
631 lang = guess_message_value ();
633 hintfile = g_strdup_printf ("%s.%s", hintfile_base, lang);
634 data = load_file (hintfile);
636 if (!data) {
637 g_free (hintfile);
638 hintfile = g_strdup_printf ("%s.%.2s", hintfile_base, lang);
639 data = load_file (hintfile);
641 if (!data) {
642 g_free (hintfile);
643 hintfile = hintfile_base;
644 data = load_file (hintfile_base);
648 g_free (lang);
650 if (hintfile != hintfile_base)
651 g_free (hintfile_base);
653 if (allocated_filename)
654 *allocated_filename = hintfile;
655 else
656 g_free (hintfile);
658 return data;
661 /* Check strftime() results. Some systems (i.e. Solaris) have different
662 short-month-name sizes for different locales */
663 size_t i18n_checktimelength (void)
665 size_t length, a, b;
666 char buf [MAX_I18NTIMELENGTH + 1];
667 time_t testtime = time (NULL);
669 a = strftime (buf, sizeof(buf)-1, _("%b %e %H:%M"), localtime(&testtime));
670 b = strftime (buf, sizeof(buf)-1, _("%b %e %Y"), localtime(&testtime));
672 length = max (a, b);
674 /* Don't handle big differences. Use standard value (email bug, please) */
675 if ( length > MAX_I18NTIMELENGTH || length < MIN_I18NTIMELENGTH )
676 length = STD_I18NTIMELENGTH;
678 return length;
681 char *file_date (time_t when)
683 static char timebuf [MAX_I18NTIMELENGTH + 1];
684 time_t current_time = time ((time_t) 0);
685 static size_t i18n_timelength = 0;
686 static char *fmtyear, *fmttime;
687 char *fmt;
689 if (i18n_timelength == 0){
690 i18n_timelength = i18n_checktimelength() + 1;
692 /* strftime() format string for old dates */
693 fmtyear = _("%b %e %Y");
694 /* strftime() format string for recent dates */
695 fmttime = _("%b %e %H:%M");
698 if (current_time > when + 6L * 30L * 24L * 60L * 60L /* Old. */
699 || current_time < when - 60L * 60L) /* In the future. */
700 /* The file is fairly old or in the future.
701 POSIX says the cutoff is 6 months old;
702 approximate this by 6*30 days.
703 Allow a 1 hour slop factor for what is considered "the future",
704 to allow for NFS server/client clock disagreement.
705 Show the year instead of the time of day. */
707 fmt = fmtyear;
708 else
709 fmt = fmttime;
711 strftime (timebuf, i18n_timelength, fmt, localtime(&when));
712 return timebuf;
715 /* Like file_date, but packs the data to fit in 10 columns */
716 char *file_date_pck (time_t when)
718 /* FIXME: Should return only 10 chars, not 14 */
719 return file_date (when);
722 char *extract_line (char *s, char *top)
724 static char tmp_line [BUF_MEDIUM];
725 char *t = tmp_line;
727 while (*s && *s != '\n' && (t - tmp_line) < sizeof (tmp_line)-1 && s < top)
728 *t++ = *s++;
729 *t = 0;
730 return tmp_line;
733 /* FIXME: I should write a faster version of this (Aho-Corasick stuff) */
734 char * _icase_search (char *text, char *data, int *lng)
736 char *d = text;
737 char *e = data;
738 int dlng = 0;
740 if (lng)
741 *lng = 0;
742 for (;*e; e++) {
743 while (*(e+1) == '\b' && *(e+2)) {
744 e += 2;
745 dlng += 2;
747 if (toupper((unsigned char) *d) == toupper((unsigned char) *e))
748 d++;
749 else {
750 e -= d - text;
751 d = text;
752 dlng = 0;
754 if (!*d) {
755 if (lng)
756 *lng = strlen (text) + dlng;
757 return e+1;
760 return 0;
763 /* The basename routine */
764 char *x_basename (char *s)
766 char *where;
767 return ((where = strrchr (s, PATH_SEP))) ? where + 1 : s;
770 void my_putenv (char *name, char *data)
772 char *full;
774 full = malloc (strlen (name) + strlen (data) + 2);
775 strcpy (full, name);
776 strcat (full, "=");
777 strcat (full, data);
778 putenv (full);
780 /* WARNING: NEVER FREE THE full VARIABLE!!!!!!!!!!!!!!!!!!!!!!!! */
781 /* It is used by putenv. Freeing it will corrupt the environment */
783 #endif /* !VFS_STANDALONE */
785 char *unix_error_string (int error_num)
787 static char buffer [BUF_LARGE];
789 g_snprintf (buffer, sizeof (buffer), "%s (%d)",
790 g_strerror (error_num), error_num);
791 return buffer;
794 #ifndef VFS_STANDALONE
795 long blocks2kilos (int blocks, int bsize)
797 if (bsize > 1024){
798 return blocks * (bsize / 1024);
799 } else if (bsize < 1024){
800 return blocks / (1024 /bsize);
801 } else
802 return blocks;
805 char *skip_separators (char *s)
807 for (;*s; s++)
808 if (*s != ' ' && *s != '\t' && *s != ',')
809 break;
810 return s;
813 char *skip_numbers (char *s)
815 for (;*s; s++)
816 if (!isdigit (*s))
817 break;
818 return s;
821 /* Remove all control sequences from the argument string. We define
822 * "control sequence", in a sort of pidgin BNF, as follows:
824 * control-seq = Esc non-'['
825 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
827 * This scheme works for all the terminals described in my termcap /
828 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
829 * terminals. If I hear from a single person who uses such a terminal
830 * with MC, I'll be glad to add support for it. (Dugan)
831 * Non-printable characters are also removed.
834 char *strip_ctrl_codes (char *s)
836 char *w; /* Current position where the stripped data is written */
837 char *r; /* Current position where the original data is read */
839 if (!s)
840 return 0;
842 for (w = s, r = s; *r; ++r)
843 if (*r != ESC_CHAR){
844 if (is_printable(*r))
845 *w++ = *r;
846 } else {
847 if (*(++r) == '[') {
848 while (strchr ("0123456789;?", *(++r)))
849 /* Skip the control sequence's arguments */ ;
852 *w = 0;
853 return s;
856 #endif /* !VFS_STANDALONE */
858 #ifndef USE_VFS
859 char *get_current_wd (char *buffer, int size)
861 char *p;
862 int len;
864 p = g_get_current_dir ();
865 len = strlen(p) + 1;
867 if (len > size) {
868 g_free (p);
869 return NULL;
872 strncpy (buffer, p, len);
873 g_free (p);
875 return buffer;
877 #endif /* !USE_VFS */
879 #define CHECK(x) if (x == -1) return 0;
881 static long
882 get_small_endian_long (int fd)
884 unsigned char buffer [4];
886 CHECK (mc_read (fd, buffer, 4));
887 return (buffer [3] << 24) | (buffer [2] << 16) | (buffer [1] << 8) | buffer [0];
891 * This constant makes the magic array on the stack be larger than
892 * it needs because Linux when reading the second byte of /proc/locks
893 * for example will write 2 bytes, even if we only asked for one
895 #define LINUX_HAS_PROBLEMS_WHEN_READING_PROC_LOCKS_ON_SOME_KERNELS 40
897 /* This function returns 0 if the file is not in gunzip format */
898 /* or how much memory must be allocated to load the gziped file */
899 /* Warning: this function moves the current file pointer */
900 long int is_gunzipable (int fd, int *type)
902 unsigned char magic [4+LINUX_HAS_PROBLEMS_WHEN_READING_PROC_LOCKS_ON_SOME_KERNELS];
904 *type = ISGUNZIPABLE_GUNZIP;
906 /* Read the magic signature */
907 CHECK (mc_read (fd, &magic [0], 4));
909 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
910 if (magic [0] == 037 && (magic [1] == 0213 || magic [1] == 0236)){
911 /* Read the uncompressed size of the file */
912 mc_lseek (fd, -4, SEEK_END);
913 return get_small_endian_long (fd);
916 /* PKZIP_MAGIC */
917 if (magic [0] == 0120 && magic [1] == 0113 && magic [2] == 003 && magic [3] == 004){
918 /* Read compression type */
919 mc_lseek (fd, 8, SEEK_SET);
920 CHECK (mc_read (fd, &magic [0], 2));
922 /* Gzip can handle only deflated (8) or stored (0) files */
923 if ((magic [0] != 8 && magic [0] != 0) || magic [1] != 0)
924 return 0;
925 /* Read the uncompressed size of the first file in the archive */
926 mc_lseek (fd, 22, SEEK_SET);
927 return get_small_endian_long (fd);
930 /* PACK_MAGIC and LZH_MAGIC and compress magic */
931 if (magic [0] == 037 && (magic [1] == 036 || magic [1] == 0240 || magic [1] == 0235)){
932 /* In case the file is packed, sco lzhed or compress_magic, the */
933 /* program guesses that the uncompressed size is (at most) four */
934 /* times the length of the compressed size, if the compression */
935 /* ratio is more than 4:1 the end of the file is not displayed */
936 return 4*mc_lseek (fd, 0, SEEK_END);
939 /* BZIP and BZIP2 files */
940 if ((magic[0] == 'B') && (magic[1] == 'Z') &&
941 (magic [3] >= '1') && (magic [3] <= '9')){
942 switch (magic[2]) {
943 case '0':
944 *type = ISGUNZIPABLE_BZIP;
945 return 5*mc_lseek (fd, 0, SEEK_END);
946 case 'h':
947 *type = ISGUNZIPABLE_BZIP2;
948 return 5*mc_lseek (fd, 0, SEEK_END);
951 return 0;
954 char *
955 decompress_extension (int type)
957 switch (type){
958 case ISGUNZIPABLE_GUNZIP: return "#ugz";
959 case ISGUNZIPABLE_BZIP: return "#ubz";
960 case ISGUNZIPABLE_BZIP2: return "#ubz2";
962 /* Should never reach this place */
963 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
964 return 0;
967 char *
968 decompress_command (int type)
970 switch (type){
971 case ISGUNZIPABLE_GUNZIP:
972 return "gzip -cdf";
974 case ISGUNZIPABLE_BZIP:
975 return "bzip -d";
977 case ISGUNZIPABLE_BZIP2:
978 return "bzip2 -d";
980 /* Should never reach this place */
981 fprintf (stderr, "Fatal: decompress_command called with an unknown argument\n");
982 return 0;
985 void
986 decompress_command_and_arg (int type, char **cmd, char **flags)
988 switch (type){
989 case ISGUNZIPABLE_GUNZIP:
990 *cmd = "gzip";
991 *flags = "-cdf";
992 return;
994 case ISGUNZIPABLE_BZIP:
995 *cmd = "bzip";
996 *flags = "-d";
997 return;
1000 case ISGUNZIPABLE_BZIP2:
1001 *cmd = "bzip2";
1002 *flags = "-d";
1003 return;
1005 *cmd = 0;
1006 *flags = 0;
1008 /* Should never reach this place */
1009 fprintf (stderr, "Fatal: decompress_command called with an unknown argument\n");
1012 #ifndef VFS_STANDALONE
1013 /* Hooks */
1014 void add_hook (Hook **hook_list, void (*hook_fn)(void *), void *data)
1016 Hook *new_hook = g_new (Hook, 1);
1018 new_hook->hook_fn = hook_fn;
1019 new_hook->next = *hook_list;
1020 new_hook->hook_data = data;
1022 *hook_list = new_hook;
1025 void execute_hooks (Hook *hook_list)
1027 Hook *new_hook = 0;
1028 Hook *p;
1030 /* We copy the hook list first so tahat we let the hook
1031 * function call delete_hook
1034 while (hook_list){
1035 add_hook (&new_hook, hook_list->hook_fn, hook_list->hook_data);
1036 hook_list = hook_list->next;
1038 p = new_hook;
1040 while (new_hook){
1041 (*new_hook->hook_fn)(new_hook->hook_data);
1042 new_hook = new_hook->next;
1045 for (hook_list = p; hook_list;){
1046 p = hook_list;
1047 hook_list = hook_list->next;
1048 g_free (p);
1052 void delete_hook (Hook **hook_list, void (*hook_fn)(void *))
1054 Hook *current, *new_list, *next;
1056 new_list = 0;
1058 for (current = *hook_list; current; current = next){
1059 next = current->next;
1060 if (current->hook_fn == hook_fn)
1061 g_free (current);
1062 else
1063 add_hook (&new_list, current->hook_fn, current->hook_data);
1065 *hook_list = new_list;
1068 int hook_present (Hook *hook_list, void (*hook_fn)(void *))
1070 Hook *p;
1072 for (p = hook_list; p; p = p->next)
1073 if (p->hook_fn == hook_fn)
1074 return 1;
1075 return 0;
1078 void wipe_password (char *passwd)
1080 char *p = passwd;
1082 if (!p)
1083 return;
1084 for (;*p ; p++)
1085 *p = 0;
1086 g_free (passwd);
1089 /* Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key */
1090 /* Returns a newly allocated string */
1091 char *convert_controls (char *s)
1093 char *valcopy = g_strdup (s);
1094 char *p, *q;
1096 /* Parse the escape special character */
1097 for (p = s, q = valcopy; *p;){
1098 if (*p == '\\'){
1099 p++;
1100 if ((*p == 'e') || (*p == 'E')){
1101 p++;
1102 *q++ = ESC_CHAR;
1104 } else {
1105 if (*p == '^'){
1106 p++;
1107 if (*p == '^')
1108 *q++ = *p++;
1109 else {
1110 *p = (*p | 0x20);
1111 if (*p >= 'a' && *p <= 'z') {
1112 *q++ = *p++ - 'a' + 1;
1113 } else
1114 p++;
1116 } else
1117 *q++ = *p++;
1120 *q++ = 0;
1121 return valcopy;
1124 /* Reverse the string */
1125 char *reverse_string (char *string)
1127 int len = strlen (string);
1128 int i;
1129 const int steps = len/2;
1131 for (i = 0; i < steps; i++){
1132 char c = string [i];
1134 string [i] = string [len-i-1];
1135 string [len-i-1] = c;
1137 return string;
1140 char *resolve_symlinks (char *path)
1142 char *buf, *buf2, *p, *q, *r, c;
1143 int len;
1144 struct stat mybuf;
1146 if (*path != PATH_SEP)
1147 return NULL;
1148 r = buf = g_malloc (MC_MAXPATHLEN);
1149 buf2 = g_malloc (MC_MAXPATHLEN);
1150 *r++ = PATH_SEP;
1151 *r = 0;
1152 p = path;
1153 for (;;) {
1154 q = strchr (p + 1, PATH_SEP);
1155 if (!q) {
1156 q = strchr (p + 1, 0);
1157 if (q == p + 1)
1158 break;
1160 c = *q;
1161 *q = 0;
1162 if (mc_lstat (path, &mybuf) < 0) {
1163 g_free (buf);
1164 g_free (buf2);
1165 *q = c;
1166 return NULL;
1168 if (!S_ISLNK (mybuf.st_mode))
1169 strcpy (r, p + 1);
1170 else {
1171 len = mc_readlink (path, buf2, MC_MAXPATHLEN);
1172 if (len < 0) {
1173 g_free (buf);
1174 g_free (buf2);
1175 *q = c;
1176 return NULL;
1178 buf2 [len] = 0;
1179 if (*buf2 == PATH_SEP)
1180 strcpy (buf, buf2);
1181 else
1182 strcpy (r, buf2);
1184 canonicalize_pathname (buf);
1185 r = strchr (buf, 0);
1186 if (!*r || *(r - 1) != PATH_SEP) {
1187 *r++ = PATH_SEP;
1188 *r = 0;
1190 *q = c;
1191 p = q;
1192 if (!c)
1193 break;
1195 if (!*buf)
1196 strcpy (buf, PATH_SEP_STR);
1197 else if (*(r - 1) == PATH_SEP && r != buf + 1)
1198 *(r - 1) = 0;
1199 g_free (buf2);
1200 return buf;
1203 /* Finds out a relative path from first to second, i.e. goes as many ..
1204 * as needed up in first and then goes down using second */
1205 char *diff_two_paths (char *first, char *second)
1207 char *p, *q, *r, *s, *buf = 0;
1208 int i, j, prevlen = -1, currlen;
1210 first = resolve_symlinks (first);
1211 if (first == NULL)
1212 return NULL;
1213 for (j = 0; j < 2; j++) {
1214 p = first;
1215 if (j) {
1216 second = resolve_symlinks (second);
1217 if (second == NULL) {
1218 g_free (first);
1219 return buf;
1222 q = second;
1223 for (;;) {
1224 r = strchr (p, PATH_SEP);
1225 s = strchr (q, PATH_SEP);
1226 if (!r || !s)
1227 break;
1228 *r = 0; *s = 0;
1229 if (strcmp (p, q)) {
1230 *r = PATH_SEP; *s = PATH_SEP;
1231 break;
1232 } else {
1233 *r = PATH_SEP; *s = PATH_SEP;
1235 p = r + 1;
1236 q = s + 1;
1238 p--;
1239 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++);
1240 currlen = (i + 1) * 3 + strlen (q) + 1;
1241 if (j) {
1242 if (currlen < prevlen)
1243 g_free (buf);
1244 else {
1245 g_free (first);
1246 g_free (second);
1247 return buf;
1250 p = buf = g_malloc (currlen);
1251 prevlen = currlen;
1252 for (; i >= 0; i--, p += 3)
1253 strcpy (p, "../");
1254 strcpy (p, q);
1256 g_free (first);
1257 g_free (second);
1258 return buf;
1261 #ifndef HAVE_TRUNCATE
1262 /* On SCO and Windows NT systems */
1263 int my_ftruncate (int fd, long size)
1265 #ifdef OS2_NT
1266 if(_chsize(fd, size))
1267 return -1;
1268 else
1269 return 0;
1270 #else
1271 struct flock lk;
1273 lk.l_whence = 0;
1274 lk.l_start = size;
1275 lk.l_len = 0;
1277 return fcntl (fd, F_FREESP, &lk);
1278 #endif /* !OS2_NT */
1281 int truncate (const char *path, long size)
1283 int fd;
1284 int res;
1286 fd = open (path, O_RDWR, 0);
1287 if (fd < 0)
1288 return fd;
1289 res = my_ftruncate (fd, size);
1290 if (res < 0)
1291 return res;
1292 close (fd);
1293 return 0;
1297 #endif /* !HAVE_TRUNCATE */
1298 #endif /* !VFS_STANDALONE */
1300 /* If filename is NULL, then we just append PATH_SEP to the dir */
1301 char *
1302 concat_dir_and_file (const char *dir, const char *file)
1304 int i = strlen (dir);
1306 if (dir [i-1] == PATH_SEP)
1307 return g_strconcat (dir, file, NULL);
1308 else
1309 return g_strconcat (dir, PATH_SEP_STR, file, NULL);
1312 /* Following code heavily borrows from libiberty, mkstemps.c */
1314 /* Number of attempts to create a temporary file */
1315 #ifndef TMP_MAX
1316 #define TMP_MAX 16384
1317 #endif /* !TMP_MAX */
1320 * Arguments:
1321 * pname (output) - pointer to the name of the temp file (needs g_free).
1322 * NULL if the function fails.
1323 * prefix - part of the filename before the random part.
1324 * Prepend $TMPDIR or /tmp if there are no path separators.
1325 * suffix - if not NULL, part of the filename after the random part.
1327 * Result:
1328 * handle of the open file or -1 if couldn't open any.
1330 int mc_mkstemps(char **pname, const char *prefix, const char *suffix)
1332 static const char letters[]
1333 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1334 static unsigned long value;
1335 struct timeval tv;
1336 char *tmpbase;
1337 char *tmpname;
1338 char *XXXXXX;
1339 int count;
1341 if (strchr(prefix, PATH_SEP) == NULL) {
1342 char *tmpdir;
1344 tmpdir = getenv("TMPDIR");
1345 if (!tmpdir) {
1346 tmpdir = TMPDIR_DEFAULT;
1349 /* Add prefix first to find the position of XXXXXX */
1350 tmpbase = concat_dir_and_file (tmpdir, prefix);
1351 } else {
1352 tmpbase = g_strdup (prefix);
1355 tmpname = g_strconcat (tmpbase, "XXXXXX", suffix, NULL);
1356 *pname = tmpname;
1357 XXXXXX = &tmpname[strlen (tmpbase)];
1358 g_free(tmpbase);
1360 /* Get some more or less random data. */
1361 gettimeofday (&tv, NULL);
1362 value += (tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
1364 for (count = 0; count < TMP_MAX; ++count) {
1365 unsigned long v = value;
1366 int fd;
1368 /* Fill in the random bits. */
1369 XXXXXX[0] = letters[v % 62];
1370 v /= 62;
1371 XXXXXX[1] = letters[v % 62];
1372 v /= 62;
1373 XXXXXX[2] = letters[v % 62];
1374 v /= 62;
1375 XXXXXX[3] = letters[v % 62];
1376 v /= 62;
1377 XXXXXX[4] = letters[v % 62];
1378 v /= 62;
1379 XXXXXX[5] = letters[v % 62];
1381 fd = open (tmpname, O_RDWR|O_CREAT|O_EXCL, 0600);
1382 if (fd >= 0) {
1383 /* Successfully created. */
1384 return fd;
1387 /* This is a random value. It is only necessary that the next
1388 TMP_MAX values generated by adding 7777 to VALUE are different
1389 with (module 2^32). */
1390 value += 7777;
1393 /* Unsuccessful. Free the filename. */
1394 g_free (tmpname);
1395 tmpname = NULL;
1397 return -1;