Merge branch '176_lzma'
[midnight-commander.git] / src / util.c
blobf1139b770c216fe6f500fc99b98a9f5ae04393dd
1 /* Various utilities
2 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
3 2004, 2005, 2007 Free Software Foundation, Inc.
4 Written 1994, 1995, 1996 by:
5 Miguel de Icaza, Janne Kukonlehto, Dugan Porter,
6 Jakub Jelinek, Mauricio Plaza.
8 The file_date routine is mostly from GNU's fileutils package,
9 written by Richard Stallman and David MacKenzie.
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
25 #include <config.h>
27 #include <ctype.h>
28 #include <limits.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
37 #include "global.h"
38 #include "profile.h"
39 #include "main.h" /* mc_home */
40 #include "cmd.h" /* guess_message_value */
41 #include "mountlist.h"
42 #include "win.h" /* xterm_flag */
43 #include "timefmt.h"
45 #ifdef HAVE_CHARSET
46 #include "charsets.h"
47 #endif
49 static const char app_text [] = "Midnight-Commander";
50 int easy_patterns = 1;
52 extern void str_replace(char *s, char from, char to)
54 for (; *s != '\0'; s++) {
55 if (*s == from)
56 *s = to;
60 static inline int
61 is_7bit_printable (unsigned char c)
63 return (c > 31 && c < 127);
66 static inline int
67 is_iso_printable (unsigned char c)
69 return ((c > 31 && c < 127) || c >= 160);
72 static inline int
73 is_8bit_printable (unsigned char c)
75 /* "Full 8 bits output" doesn't work on xterm */
76 if (xterm_flag)
77 return is_iso_printable (c);
79 return (c > 31 && c != 127 && c != 155);
82 int
83 is_printable (int c)
85 c &= 0xff;
87 #ifdef HAVE_CHARSET
88 /* "Display bits" is ignored, since the user controls the output
89 by setting the output codepage */
90 return is_8bit_printable (c);
91 #else
92 if (!eight_bit_clean)
93 return is_7bit_printable (c);
95 if (full_eight_bits) {
96 return is_8bit_printable (c);
97 } else
98 return is_iso_printable (c);
99 #endif /* !HAVE_CHARSET */
102 /* Calculates the message dimensions (lines and columns) */
103 void
104 msglen (const char *text, int *lines, int *columns)
106 int nlines = 1; /* even the empty string takes one line */
107 int ncolumns = 0;
108 int colindex = 0;
110 for (; *text != '\0'; text++) {
111 if (*text == '\n') {
112 nlines++;
113 colindex = 0;
114 } else {
115 colindex++;
116 if (colindex > ncolumns)
117 ncolumns = colindex;
121 *lines = nlines;
122 *columns = ncolumns;
126 * Copy from s to d, and trim the beginning if necessary, and prepend
127 * "..." in this case. The destination string can have at most len
128 * bytes, not counting trailing 0.
130 char *
131 trim (const char *s, char *d, int len)
133 int source_len;
135 /* Sanity check */
136 len = max (len, 0);
138 source_len = strlen (s);
139 if (source_len > len) {
140 /* Cannot fit the whole line */
141 if (len <= 3) {
142 /* We only have room for the dots */
143 memset (d, '.', len);
144 d[len] = 0;
145 return d;
146 } else {
147 /* Begin with ... and add the rest of the source string */
148 memset (d, '.', 3);
149 strcpy (d + 3, s + 3 + source_len - len);
151 } else
152 /* We can copy the whole line */
153 strcpy (d, s);
154 return d;
158 * Quote the filename for the purpose of inserting it into the command
159 * line. If quote_percent is 1, replace "%" with "%%" - the percent is
160 * processed by the mc command line.
162 char *
163 name_quote (const char *s, int quote_percent)
165 char *ret, *d;
167 d = ret = g_malloc (strlen (s) * 2 + 2 + 1);
168 if (*s == '-') {
169 *d++ = '.';
170 *d++ = '/';
173 for (; *s; s++, d++) {
174 switch (*s) {
175 case '%':
176 if (quote_percent)
177 *d++ = '%';
178 break;
179 case '\'':
180 case '\\':
181 case '\r':
182 case '\n':
183 case '\t':
184 case '"':
185 case ';':
186 case ' ':
187 case '?':
188 case '|':
189 case '[':
190 case ']':
191 case '{':
192 case '}':
193 case '<':
194 case '>':
195 case '`':
196 case '!':
197 case '$':
198 case '&':
199 case '*':
200 case '(':
201 case ')':
202 *d++ = '\\';
203 break;
204 case '~':
205 case '#':
206 if (d == ret)
207 *d++ = '\\';
208 break;
210 *d = *s;
212 *d = '\0';
213 return ret;
216 char *
217 fake_name_quote (const char *s, int quote_percent)
219 (void) quote_percent;
220 return g_strdup (s);
224 * Remove the middle part of the string to fit given length.
225 * Use "~" to show where the string was truncated.
226 * Return static buffer, no need to free() it.
228 const char *
229 name_trunc (const char *txt, size_t trunc_len)
231 static char x[MC_MAXPATHLEN + MC_MAXPATHLEN];
232 size_t txt_len;
233 char *p;
235 if (!txt)
236 return NULL;
237 if (!*txt)
238 return txt;
240 if (trunc_len > sizeof (x) - 1) {
241 trunc_len = sizeof (x) - 1;
243 txt_len = strlen (txt);
244 if (txt_len <= trunc_len) {
245 strcpy (x, txt);
246 } else {
247 size_t y = (trunc_len / 2) + (trunc_len % 2);
248 strncpy (x, txt, (size_t) y);
249 strncpy (x + y, txt + (txt_len - (trunc_len / 2)), trunc_len / 2);
250 x[y] = '~';
252 x[trunc_len] = 0;
253 for (p = x; *p; p++)
254 if (!is_printable (*p))
255 *p = '?';
256 return x;
260 * path_trunc() is the same as name_trunc() above but
261 * it deletes possible password from path for security
262 * reasons.
264 const char *
265 path_trunc (const char *path, size_t trunc_len) {
266 const char *ret;
267 char *secure_path = strip_password (g_strdup (path), 1);
269 ret = name_trunc (secure_path, trunc_len);
270 g_free (secure_path);
272 return ret;
275 const char *
276 size_trunc (double size)
278 static char x [BUF_TINY];
279 long int divisor = 1;
280 const char *xtra = "";
282 if (size > 999999999L){
283 divisor = 1024;
284 xtra = "K";
285 if (size/divisor > 999999999L){
286 divisor = 1024*1024;
287 xtra = "M";
290 g_snprintf (x, sizeof (x), "%.0f%s", (size/divisor), xtra);
291 return x;
294 const char *
295 size_trunc_sep (double size)
297 static char x [60];
298 int count;
299 const char *p, *y;
300 char *d;
302 p = y = size_trunc (size);
303 p += strlen (p) - 1;
304 d = x + sizeof (x) - 1;
305 *d-- = 0;
306 while (p >= y && isalpha ((unsigned char) *p))
307 *d-- = *p--;
308 for (count = 0; p >= y; count++){
309 if (count == 3){
310 *d-- = ',';
311 count = 0;
313 *d-- = *p--;
315 d++;
316 if (*d == ',')
317 d++;
318 return d;
322 * Print file SIZE to BUFFER, but don't exceed LEN characters,
323 * not including trailing 0. BUFFER should be at least LEN+1 long.
324 * This function is called for every file on panels, so avoid
325 * floating point by any means.
327 * Units: size units (filesystem sizes are 1K blocks)
328 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
330 void
331 size_trunc_len (char *buffer, int len, off_t size, int units)
333 /* Avoid taking power for every file. */
334 static const off_t power10 [] =
335 {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
336 1000000000};
337 static const char * const suffix [] =
338 {"", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL};
339 int j = 0;
341 /* Don't print more than 9 digits - use suffix. */
342 if (len == 0 || len > 9)
343 len = 9;
345 for (j = units; suffix [j] != NULL; j++) {
346 if (size == 0) {
347 if (j == units) {
348 /* Empty files will print "0" even with minimal width. */
349 g_snprintf (buffer, len + 1, "0");
350 break;
353 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
354 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s",
355 (j > 1) ? suffix[j - 1] : "B");
356 break;
359 if (size < power10 [len - (j > 0)]) {
360 g_snprintf (buffer, len + 1, "%lu%s", (unsigned long) size, suffix[j]);
361 break;
364 /* Powers of 1024, with rounding. */
365 size = (size + 512) >> 10;
370 is_exe (mode_t mode)
372 if ((S_IXUSR & mode) || (S_IXGRP & mode) || (S_IXOTH & mode))
373 return 1;
374 return 0;
377 #define ismode(n,m) ((n & m) == m)
379 const char *
380 string_perm (mode_t mode_bits)
382 static char mode[11];
384 strcpy (mode, "----------");
385 if (S_ISDIR (mode_bits))
386 mode[0] = 'd';
387 if (S_ISCHR (mode_bits))
388 mode[0] = 'c';
389 if (S_ISBLK (mode_bits))
390 mode[0] = 'b';
391 if (S_ISLNK (mode_bits))
392 mode[0] = 'l';
393 if (S_ISFIFO (mode_bits))
394 mode[0] = 'p';
395 if (S_ISNAM (mode_bits))
396 mode[0] = 'n';
397 if (S_ISSOCK (mode_bits))
398 mode[0] = 's';
399 if (S_ISDOOR (mode_bits))
400 mode[0] = 'D';
401 if (ismode (mode_bits, S_IXOTH))
402 mode[9] = 'x';
403 if (ismode (mode_bits, S_IWOTH))
404 mode[8] = 'w';
405 if (ismode (mode_bits, S_IROTH))
406 mode[7] = 'r';
407 if (ismode (mode_bits, S_IXGRP))
408 mode[6] = 'x';
409 if (ismode (mode_bits, S_IWGRP))
410 mode[5] = 'w';
411 if (ismode (mode_bits, S_IRGRP))
412 mode[4] = 'r';
413 if (ismode (mode_bits, S_IXUSR))
414 mode[3] = 'x';
415 if (ismode (mode_bits, S_IWUSR))
416 mode[2] = 'w';
417 if (ismode (mode_bits, S_IRUSR))
418 mode[1] = 'r';
419 #ifdef S_ISUID
420 if (ismode (mode_bits, S_ISUID))
421 mode[3] = (mode[3] == 'x') ? 's' : 'S';
422 #endif /* S_ISUID */
423 #ifdef S_ISGID
424 if (ismode (mode_bits, S_ISGID))
425 mode[6] = (mode[6] == 'x') ? 's' : 'S';
426 #endif /* S_ISGID */
427 #ifdef S_ISVTX
428 if (ismode (mode_bits, S_ISVTX))
429 mode[9] = (mode[9] == 'x') ? 't' : 'T';
430 #endif /* S_ISVTX */
431 return mode;
434 /* p: string which might contain an url with a password (this parameter is
435 modified in place).
436 has_prefix = 0: The first parameter is an url without a prefix
437 (user[:pass]@]machine[:port][remote-dir). Delete
438 the password.
439 has_prefix = 1: Search p for known url prefixes. If found delete
440 the password from the url.
441 Caveat: only the first url is found
443 char *
444 strip_password (char *p, int has_prefix)
446 static const struct {
447 const char *name;
448 size_t len;
449 } prefixes[] = { {"/#ftp:", 6},
450 {"ftp://", 6},
451 {"/#mc:", 5},
452 {"mc://", 5},
453 {"/#smb:", 6},
454 {"smb://", 6},
455 {"/#sh:", 5},
456 {"sh://", 5},
457 {"ssh://", 6}
459 char *at, *inner_colon, *dir;
460 size_t i;
461 char *result = p;
463 for (i = 0; i < sizeof (prefixes)/sizeof (prefixes[0]); i++) {
464 char *q;
466 if (has_prefix) {
467 if((q = strstr (p, prefixes[i].name)) == 0)
468 continue;
469 else
470 p = q + prefixes[i].len;
473 if ((dir = strchr (p, PATH_SEP)) != NULL)
474 *dir = '\0';
476 /* search for any possible user */
477 at = strrchr (p, '@');
479 if (dir)
480 *dir = PATH_SEP;
482 /* We have a username */
483 if (at) {
484 inner_colon = memchr (p, ':', at - p);
485 if (inner_colon)
486 memmove (inner_colon, at, strlen(at) + 1);
488 break;
490 return (result);
493 const char *
494 strip_home_and_password(const char *dir)
496 size_t len;
497 static char newdir [MC_MAXPATHLEN];
499 if (home_dir && !strncmp (dir, home_dir, len = strlen (home_dir)) &&
500 (dir[len] == PATH_SEP || dir[len] == '\0')){
501 newdir [0] = '~';
502 g_strlcpy (&newdir [1], &dir [len], sizeof(newdir) - 1);
503 return newdir;
506 /* We do not strip homes in /#ftp tree, I do not like ~'s there
507 (see ftpfs.c why) */
508 g_strlcpy (newdir, dir, sizeof(newdir));
509 strip_password (newdir, 1);
510 return newdir;
513 static char *
514 maybe_start_group (char *d, int do_group, int *was_wildcard)
516 if (!do_group)
517 return d;
518 if (*was_wildcard)
519 return d;
520 *was_wildcard = 1;
521 *d++ = '\\';
522 *d++ = '(';
523 return d;
526 static char *
527 maybe_end_group (char *d, int do_group, int *was_wildcard)
529 if (!do_group)
530 return d;
531 if (!*was_wildcard)
532 return d;
533 *was_wildcard = 0;
534 *d++ = '\\';
535 *d++ = ')';
536 return d;
539 /* If shell patterns are on converts a shell pattern to a regular
540 expression. Called by regexp_match and mask_rename. */
541 /* Shouldn't we support [a-fw] type wildcards as well ?? */
542 char *
543 convert_pattern (const char *pattern, int match_type, int do_group)
545 char *d;
546 char *new_pattern;
547 int was_wildcard = 0;
548 const char *s;
550 if ((match_type != match_regex) && easy_patterns){
551 new_pattern = g_malloc (MC_MAXPATHLEN);
552 d = new_pattern;
553 if (match_type == match_file)
554 *d++ = '^';
555 for (s = pattern; *s; s++, d++){
556 switch (*s){
557 case '*':
558 d = maybe_start_group (d, do_group, &was_wildcard);
559 *d++ = '.';
560 *d = '*';
561 break;
563 case '?':
564 d = maybe_start_group (d, do_group, &was_wildcard);
565 *d = '.';
566 break;
568 case '.':
569 d = maybe_end_group (d, do_group, &was_wildcard);
570 *d++ = '\\';
571 *d = '.';
572 break;
574 default:
575 d = maybe_end_group (d, do_group, &was_wildcard);
576 *d = *s;
577 break;
580 d = maybe_end_group (d, do_group, &was_wildcard);
581 if (match_type == match_file)
582 *d++ = '$';
583 *d = 0;
584 return new_pattern;
585 } else
586 return g_strdup (pattern);
590 regexp_match (const char *pattern, const char *string, int match_type)
592 static regex_t r;
593 static char *old_pattern = NULL;
594 static int old_type;
595 int rval;
596 char *my_pattern;
598 if (!old_pattern || STRCOMP (old_pattern, pattern) || old_type != match_type){
599 if (old_pattern){
600 regfree (&r);
601 g_free (old_pattern);
602 old_pattern = NULL;
604 my_pattern = convert_pattern (pattern, match_type, 0);
605 if (regcomp (&r, my_pattern, REG_EXTENDED|REG_NOSUB|MC_ARCH_FLAGS)) {
606 g_free (my_pattern);
607 return -1;
609 old_pattern = my_pattern;
610 old_type = match_type;
612 rval = !regexec (&r, string, 0, NULL, 0);
613 return rval;
616 const char *
617 extension (const char *filename)
619 const char *d = strrchr (filename, '.');
620 return (d != NULL) ? d + 1 : "";
624 get_int (const char *file, const char *key, int def)
626 return GetPrivateProfileInt (app_text, key, def, file);
630 set_int (const char *file, const char *key, int value)
632 char buffer [BUF_TINY];
634 g_snprintf (buffer, sizeof (buffer), "%d", value);
635 return WritePrivateProfileString (app_text, key, buffer, file);
638 extern char *
639 get_config_string (const char *file, const char *key, const char *defval)
641 char buffer[1024];
642 (void)GetPrivateProfileString (app_text, key, defval, buffer, sizeof(buffer), file);
643 return g_strdup (buffer);
646 extern void
647 set_config_string (const char *file, const char *key, const char *val)
649 (void)WritePrivateProfileString (app_text, key, val, file);
653 exist_file (const char *name)
655 return access (name, R_OK) == 0;
658 char *
659 load_file (const char *filename)
661 FILE *data_file;
662 struct stat s;
663 char *data;
664 long read_size;
666 if ((data_file = fopen (filename, "r")) == NULL){
667 return 0;
669 if (fstat (fileno (data_file), &s) != 0){
670 fclose (data_file);
671 return 0;
673 data = g_malloc (s.st_size+1);
674 read_size = fread (data, 1, s.st_size, data_file);
675 data [read_size] = 0;
676 fclose (data_file);
678 if (read_size > 0)
679 return data;
680 else {
681 g_free (data);
682 return 0;
686 char *
687 load_mc_home_file (const char *filename, char **allocated_filename)
689 char *hintfile_base, *hintfile;
690 char *lang;
691 char *data;
693 hintfile_base = concat_dir_and_file (mc_home, filename);
694 lang = guess_message_value ();
696 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
697 data = load_file (hintfile);
699 if (!data) {
700 g_free (hintfile);
701 /* Fall back to the two-letter language code */
702 if (lang[0] && lang[1])
703 lang[2] = 0;
704 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
705 data = load_file (hintfile);
707 if (!data) {
708 g_free (hintfile);
709 hintfile = hintfile_base;
710 data = load_file (hintfile_base);
714 g_free (lang);
716 if (hintfile != hintfile_base)
717 g_free (hintfile_base);
719 if (allocated_filename)
720 *allocated_filename = hintfile;
721 else
722 g_free (hintfile);
724 return data;
727 /* Check strftime() results. Some systems (i.e. Solaris) have different
728 short-month-name sizes for different locales */
729 size_t
730 i18n_checktimelength (void)
732 time_t testtime = time (NULL);
733 struct tm* lt = localtime(&testtime);
734 size_t length;
736 if (lt == NULL) {
737 // huh, localtime() doesnt seem to work ... falling back to "(invalid)"
738 length = strlen(INVALID_TIME_TEXT);
739 } else {
740 char buf [MAX_I18NTIMELENGTH + 1];
741 size_t a, b;
742 a = strftime (buf, sizeof(buf)-1, _("%b %e %H:%M"), lt);
743 b = strftime (buf, sizeof(buf)-1, _("%b %e %Y"), lt);
744 length = max (a, b);
747 /* Don't handle big differences. Use standard value (email bug, please) */
748 if ( length > MAX_I18NTIMELENGTH || length < MIN_I18NTIMELENGTH )
749 length = STD_I18NTIMELENGTH;
751 return length;
754 const char *
755 file_date (time_t when)
757 static char timebuf [MAX_I18NTIMELENGTH + 1];
758 time_t current_time = time ((time_t) 0);
759 static size_t i18n_timelength = 0;
760 static const char *fmtyear, *fmttime;
761 const char *fmt;
763 if (i18n_timelength == 0){
764 i18n_timelength = i18n_checktimelength() + 1;
766 /* strftime() format string for old dates */
767 fmtyear = _("%b %e %Y");
768 /* strftime() format string for recent dates */
769 fmttime = _("%b %e %H:%M");
772 if (current_time > when + 6L * 30L * 24L * 60L * 60L /* Old. */
773 || current_time < when - 60L * 60L) /* In the future. */
774 /* The file is fairly old or in the future.
775 POSIX says the cutoff is 6 months old;
776 approximate this by 6*30 days.
777 Allow a 1 hour slop factor for what is considered "the future",
778 to allow for NFS server/client clock disagreement.
779 Show the year instead of the time of day. */
781 fmt = fmtyear;
782 else
783 fmt = fmttime;
785 FMT_LOCALTIME(timebuf, i18n_timelength, fmt, when);
787 return timebuf;
790 const char *
791 extract_line (const char *s, const char *top)
793 static char tmp_line [BUF_MEDIUM];
794 char *t = tmp_line;
796 while (*s && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line)-1 && s < top)
797 *t++ = *s++;
798 *t = 0;
799 return tmp_line;
802 /* FIXME: I should write a faster version of this (Aho-Corasick stuff) */
803 const char *
804 _icase_search (const char *text, const char *data, int *lng)
806 const char *d = text;
807 const char *e = data;
808 int dlng = 0;
810 if (lng)
811 *lng = 0;
812 for (;*e; e++) {
813 while (*(e+1) == '\b' && *(e+2)) {
814 e += 2;
815 dlng += 2;
817 if (toupper((unsigned char) *d) == toupper((unsigned char) *e))
818 d++;
819 else {
820 e -= d - text;
821 d = text;
822 dlng = 0;
824 if (!*d) {
825 if (lng)
826 *lng = strlen (text) + dlng;
827 return e+1;
830 return 0;
833 /* The basename routine */
834 const char *
835 x_basename (const char *s)
837 const char *where;
838 return ((where = strrchr (s, PATH_SEP))) ? where + 1 : s;
842 const char *
843 unix_error_string (int error_num)
845 static char buffer [BUF_LARGE];
846 #if GLIB_MAJOR_VERSION >= 2
847 gchar *strerror_currentlocale;
849 strerror_currentlocale = g_locale_from_utf8(g_strerror (error_num), -1, NULL, NULL, NULL);
850 g_snprintf (buffer, sizeof (buffer), "%s (%d)",
851 strerror_currentlocale, error_num);
852 g_free(strerror_currentlocale);
853 #else
854 g_snprintf (buffer, sizeof (buffer), "%s (%d)",
855 g_strerror (error_num), error_num);
856 #endif
857 return buffer;
860 const char *
861 skip_separators (const char *s)
863 for (;*s; s++)
864 if (*s != ' ' && *s != '\t' && *s != ',')
865 break;
866 return s;
869 const char *
870 skip_numbers (const char *s)
872 for (;*s; s++)
873 if (!isdigit ((unsigned char) *s))
874 break;
875 return s;
878 /* Remove all control sequences from the argument string. We define
879 * "control sequence", in a sort of pidgin BNF, as follows:
881 * control-seq = Esc non-'['
882 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
884 * This scheme works for all the terminals described in my termcap /
885 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
886 * terminals. If I hear from a single person who uses such a terminal
887 * with MC, I'll be glad to add support for it. (Dugan)
888 * Non-printable characters are also removed.
891 char *
892 strip_ctrl_codes (char *s)
894 char *w; /* Current position where the stripped data is written */
895 char *r; /* Current position where the original data is read */
897 if (!s)
898 return 0;
900 for (w = s, r = s; *r; ) {
901 if (*r == ESC_CHAR) {
902 /* Skip the control sequence's arguments */ ;
903 if (*(++r) == '[') {
904 /* strchr() matches trailing binary 0 */
905 while (*(++r) && strchr ("0123456789;?", *r));
909 * Now we are at the last character of the sequence.
910 * Skip it unless it's binary 0.
912 if (*r)
913 r++;
914 continue;
917 if (is_printable(*r))
918 *w++ = *r;
919 ++r;
921 *w = 0;
922 return s;
926 #ifndef USE_VFS
927 char *
928 get_current_wd (char *buffer, int size)
930 char *p;
931 int len;
933 p = g_get_current_dir ();
934 len = strlen(p) + 1;
936 if (len > size) {
937 g_free (p);
938 return NULL;
941 memcpy (buffer, p, len);
942 g_free (p);
944 return buffer;
946 #endif /* !USE_VFS */
948 enum compression_type
949 get_compression_type (int fd)
951 unsigned char magic[16];
953 /* Read the magic signature */
954 if (mc_read (fd, (char *) magic, 4) != 4)
955 return COMPRESSION_NONE;
957 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
958 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236)) {
959 return COMPRESSION_GZIP;
962 /* PKZIP_MAGIC */
963 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003
964 && magic[3] == 004) {
965 /* Read compression type */
966 mc_lseek (fd, 8, SEEK_SET);
967 if (mc_read (fd, (char *) magic, 2) != 2)
968 return COMPRESSION_NONE;
970 /* Gzip can handle only deflated (8) or stored (0) files */
971 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
972 return COMPRESSION_NONE;
974 /* Compatible with gzip */
975 return COMPRESSION_GZIP;
978 /* PACK_MAGIC and LZH_MAGIC and compress magic */
979 if (magic[0] == 037
980 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235)) {
981 /* Compatible with gzip */
982 return COMPRESSION_GZIP;
985 /* BZIP and BZIP2 files */
986 if ((magic[0] == 'B') && (magic[1] == 'Z') &&
987 (magic[3] >= '1') && (magic[3] <= '9')) {
988 switch (magic[2]) {
989 case '0':
990 return COMPRESSION_BZIP;
991 case 'h':
992 return COMPRESSION_BZIP2;
996 /* LZMA files; both LZMA_Alone and LZMA utils formats. The LZMA_Alone
997 * format is used by the LZMA_Alone tool from LZMA SDK. The LZMA utils
998 * format is the default format of LZMA utils 4.32.1 and later. */
999 if (magic[0] < 0xE1 || (magic[0] == 0xFF && magic[1] == 'L' &&
1000 magic[2] == 'Z' && magic[3] == 'M')) {
1001 if (mc_read (fd, (char *) magic + 4, 9) == 9) {
1002 /* LZMA utils format */
1003 if (magic[0] == 0xFF && magic[4] == 'A' && magic[5] == 0x00)
1004 return COMPRESSION_LZMA;
1005 /* The LZMA_Alone format has no magic bytes, thus we
1006 * need to play a wizard. This can give false positives,
1007 * thus the detection below should be removed when
1008 * the newer LZMA utils format has got popular. */
1009 if (magic[0] < 0xE1 && magic[4] < 0x20 &&
1010 ((magic[10] == 0x00 && magic[11] == 0x00 &&
1011 magic[12] == 0x00) ||
1012 (magic[5] == 0xFF && magic[6] == 0xFF &&
1013 magic[7] == 0xFF && magic[8] == 0xFF &&
1014 magic[9] == 0xFF && magic[10] == 0xFF &&
1015 magic[11] == 0xFF && magic[12] == 0xFF)))
1016 return COMPRESSION_LZMA;
1020 return 0;
1023 const char *
1024 decompress_extension (int type)
1026 switch (type){
1027 case COMPRESSION_GZIP: return "#ugz";
1028 case COMPRESSION_BZIP: return "#ubz";
1029 case COMPRESSION_BZIP2: return "#ubz2";
1030 case COMPRESSION_LZMA: return "#ulzma";
1032 /* Should never reach this place */
1033 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
1034 return 0;
1037 /* Hooks */
1038 void
1039 add_hook (Hook **hook_list, void (*hook_fn)(void *), void *data)
1041 Hook *new_hook = g_new (Hook, 1);
1043 new_hook->hook_fn = hook_fn;
1044 new_hook->next = *hook_list;
1045 new_hook->hook_data = data;
1047 *hook_list = new_hook;
1050 void
1051 execute_hooks (Hook *hook_list)
1053 Hook *new_hook = 0;
1054 Hook *p;
1056 /* We copy the hook list first so tahat we let the hook
1057 * function call delete_hook
1060 while (hook_list){
1061 add_hook (&new_hook, hook_list->hook_fn, hook_list->hook_data);
1062 hook_list = hook_list->next;
1064 p = new_hook;
1066 while (new_hook){
1067 (*new_hook->hook_fn)(new_hook->hook_data);
1068 new_hook = new_hook->next;
1071 for (hook_list = p; hook_list;){
1072 p = hook_list;
1073 hook_list = hook_list->next;
1074 g_free (p);
1078 void
1079 delete_hook (Hook **hook_list, void (*hook_fn)(void *))
1081 Hook *current, *new_list, *next;
1083 new_list = 0;
1085 for (current = *hook_list; current; current = next){
1086 next = current->next;
1087 if (current->hook_fn == hook_fn)
1088 g_free (current);
1089 else
1090 add_hook (&new_list, current->hook_fn, current->hook_data);
1092 *hook_list = new_list;
1096 hook_present (Hook *hook_list, void (*hook_fn)(void *))
1098 Hook *p;
1100 for (p = hook_list; p; p = p->next)
1101 if (p->hook_fn == hook_fn)
1102 return 1;
1103 return 0;
1106 void
1107 wipe_password (char *passwd)
1109 char *p = passwd;
1111 if (!p)
1112 return;
1113 for (;*p ; p++)
1114 *p = 0;
1115 g_free (passwd);
1118 /* Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key */
1119 /* Returns a newly allocated string */
1120 char *
1121 convert_controls (const char *p)
1123 char *valcopy = g_strdup (p);
1124 char *q;
1126 /* Parse the escape special character */
1127 for (q = valcopy; *p;){
1128 if (*p == '\\'){
1129 p++;
1130 if ((*p == 'e') || (*p == 'E')){
1131 p++;
1132 *q++ = ESC_CHAR;
1134 } else {
1135 if (*p == '^'){
1136 p++;
1137 if (*p == '^')
1138 *q++ = *p++;
1139 else {
1140 char c = (*p | 0x20);
1141 if (c >= 'a' && c <= 'z') {
1142 *q++ = c - 'a' + 1;
1143 p++;
1144 } else if (*p)
1145 p++;
1147 } else
1148 *q++ = *p++;
1151 *q = 0;
1152 return valcopy;
1155 static char *
1156 resolve_symlinks (const char *path)
1158 char *buf, *buf2, *q, *r, c;
1159 int len;
1160 struct stat mybuf;
1161 const char *p;
1163 if (*path != PATH_SEP)
1164 return NULL;
1165 r = buf = g_malloc (MC_MAXPATHLEN);
1166 buf2 = g_malloc (MC_MAXPATHLEN);
1167 *r++ = PATH_SEP;
1168 *r = 0;
1169 p = path;
1170 for (;;) {
1171 q = strchr (p + 1, PATH_SEP);
1172 if (!q) {
1173 q = strchr (p + 1, 0);
1174 if (q == p + 1)
1175 break;
1177 c = *q;
1178 *q = 0;
1179 if (mc_lstat (path, &mybuf) < 0) {
1180 g_free (buf);
1181 g_free (buf2);
1182 *q = c;
1183 return NULL;
1185 if (!S_ISLNK (mybuf.st_mode))
1186 strcpy (r, p + 1);
1187 else {
1188 len = mc_readlink (path, buf2, MC_MAXPATHLEN - 1);
1189 if (len < 0) {
1190 g_free (buf);
1191 g_free (buf2);
1192 *q = c;
1193 return NULL;
1195 buf2 [len] = 0;
1196 if (*buf2 == PATH_SEP)
1197 strcpy (buf, buf2);
1198 else
1199 strcpy (r, buf2);
1201 canonicalize_pathname (buf);
1202 r = strchr (buf, 0);
1203 if (!*r || *(r - 1) != PATH_SEP) {
1204 *r++ = PATH_SEP;
1205 *r = 0;
1207 *q = c;
1208 p = q;
1209 if (!c)
1210 break;
1212 if (!*buf)
1213 strcpy (buf, PATH_SEP_STR);
1214 else if (*(r - 1) == PATH_SEP && r != buf + 1)
1215 *(r - 1) = 0;
1216 g_free (buf2);
1217 return buf;
1220 /* Finds out a relative path from first to second, i.e. goes as many ..
1221 * as needed up in first and then goes down using second */
1222 char *
1223 diff_two_paths (const char *first, const char *second)
1225 char *p, *q, *r, *s, *buf = NULL;
1226 int i, j, prevlen = -1, currlen;
1227 char *my_first = NULL, *my_second = NULL;
1229 my_first = resolve_symlinks (first);
1230 if (my_first == NULL)
1231 return NULL;
1232 my_second = resolve_symlinks (second);
1233 if (my_second == NULL) {
1234 g_free (my_first);
1235 return NULL;
1237 for (j = 0; j < 2; j++) {
1238 p = my_first;
1239 q = my_second;
1240 for (;;) {
1241 r = strchr (p, PATH_SEP);
1242 s = strchr (q, PATH_SEP);
1243 if (!r || !s)
1244 break;
1245 *r = 0; *s = 0;
1246 if (strcmp (p, q)) {
1247 *r = PATH_SEP; *s = PATH_SEP;
1248 break;
1249 } else {
1250 *r = PATH_SEP; *s = PATH_SEP;
1252 p = r + 1;
1253 q = s + 1;
1255 p--;
1256 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++);
1257 currlen = (i + 1) * 3 + strlen (q) + 1;
1258 if (j) {
1259 if (currlen < prevlen)
1260 g_free (buf);
1261 else {
1262 g_free (my_first);
1263 g_free (my_second);
1264 return buf;
1267 p = buf = g_malloc (currlen);
1268 prevlen = currlen;
1269 for (; i >= 0; i--, p += 3)
1270 strcpy (p, "../");
1271 strcpy (p, q);
1273 g_free (my_first);
1274 g_free (my_second);
1275 return buf;
1278 /* If filename is NULL, then we just append PATH_SEP to the dir */
1279 char *
1280 concat_dir_and_file (const char *dir, const char *file)
1282 int i = strlen (dir);
1284 if (dir [i-1] == PATH_SEP)
1285 return g_strconcat (dir, file, (char *) NULL);
1286 else
1287 return g_strconcat (dir, PATH_SEP_STR, file, (char *) NULL);
1290 /* Append text to GList, remove all entries with the same text */
1291 GList *
1292 list_append_unique (GList *list, char *text)
1294 GList *link, *newlink;
1297 * Go to the last position and traverse the list backwards
1298 * starting from the second last entry to make sure that we
1299 * are not removing the current link.
1301 list = g_list_append (list, text);
1302 list = g_list_last (list);
1303 link = g_list_previous (list);
1305 while (link) {
1306 newlink = g_list_previous (link);
1307 if (!strcmp ((char *) link->data, text)) {
1308 g_free (link->data);
1309 g_list_remove_link (list, link);
1310 g_list_free_1 (link);
1312 link = newlink;
1315 return list;
1318 /* Following code heavily borrows from libiberty, mkstemps.c */
1320 /* Number of attempts to create a temporary file */
1321 #ifndef TMP_MAX
1322 #define TMP_MAX 16384
1323 #endif /* !TMP_MAX */
1326 * Arguments:
1327 * pname (output) - pointer to the name of the temp file (needs g_free).
1328 * NULL if the function fails.
1329 * prefix - part of the filename before the random part.
1330 * Prepend $TMPDIR or /tmp if there are no path separators.
1331 * suffix - if not NULL, part of the filename after the random part.
1333 * Result:
1334 * handle of the open file or -1 if couldn't open any.
1337 mc_mkstemps (char **pname, const char *prefix, const char *suffix)
1339 static const char letters[]
1340 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1341 static unsigned long value;
1342 struct timeval tv;
1343 char *tmpbase;
1344 char *tmpname;
1345 char *XXXXXX;
1346 int count;
1348 if (strchr (prefix, PATH_SEP) == NULL) {
1349 /* Add prefix first to find the position of XXXXXX */
1350 tmpbase = concat_dir_and_file (mc_tmpdir (), prefix);
1351 } else {
1352 tmpbase = g_strdup (prefix);
1355 tmpname = g_strconcat (tmpbase, "XXXXXX", suffix, (char *) 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_TRUNC | O_EXCL,
1382 S_IRUSR | S_IWUSR);
1383 if (fd >= 0) {
1384 /* Successfully created. */
1385 return fd;
1388 /* This is a random value. It is only necessary that the next
1389 TMP_MAX values generated by adding 7777 to VALUE are different
1390 with (module 2^32). */
1391 value += 7777;
1394 /* Unsuccessful. Free the filename. */
1395 g_free (tmpname);
1396 *pname = NULL;
1398 return -1;
1402 * Read and restore position for the given filename.
1403 * If there is no stored data, return line 1 and col 0.
1405 void
1406 load_file_position (const char *filename, long *line, long *column)
1408 char *fn;
1409 FILE *f;
1410 char buf[MC_MAXPATHLEN + 20];
1411 int len;
1413 /* defaults */
1414 *line = 1;
1415 *column = 0;
1417 /* open file with positions */
1418 fn = concat_dir_and_file (home_dir, MC_FILEPOS);
1419 f = fopen (fn, "r");
1420 g_free (fn);
1421 if (!f)
1422 return;
1424 len = strlen (filename);
1426 while (fgets (buf, sizeof (buf), f)) {
1427 const char *p;
1429 /* check if the filename matches the beginning of string */
1430 if (strncmp (buf, filename, len) != 0)
1431 continue;
1433 /* followed by single space */
1434 if (buf[len] != ' ')
1435 continue;
1437 /* and string without spaces */
1438 p = &buf[len + 1];
1439 if (strchr (p, ' '))
1440 continue;
1442 *line = strtol(p, const_cast(char **, &p), 10);
1443 if (*p == ';') {
1444 *column = strtol(p+1, const_cast(char **, &p), 10);
1445 if (*p != '\n')
1446 *column = 0;
1447 } else
1448 *line = 1;
1450 fclose (f);
1453 /* Save position for the given file */
1454 void
1455 save_file_position (const char *filename, long line, long column)
1457 char *tmp, *fn;
1458 FILE *f, *t;
1459 char buf[MC_MAXPATHLEN + 20];
1460 int i = 1;
1461 int len;
1463 len = strlen (filename);
1465 tmp = concat_dir_and_file (home_dir, MC_FILEPOS_TMP);
1466 fn = concat_dir_and_file (home_dir, MC_FILEPOS);
1468 /* open temporary file */
1469 t = fopen (tmp, "w");
1470 if (!t) {
1471 g_free (tmp);
1472 g_free (fn);
1473 return;
1476 /* put the new record */
1477 if (line != 1 || column != 0) {
1478 fprintf (t, "%s %ld;%ld\n", filename, line, column);
1481 /* copy records from the old file */
1482 f = fopen (fn, "r");
1483 if (f) {
1484 while (fgets (buf, sizeof (buf), f)) {
1485 /* Skip entries for the current filename */
1486 if (strncmp (buf, filename, len) == 0 && buf[len] == ' '
1487 && !strchr (&buf[len + 1], ' '))
1488 continue;
1490 fprintf (t, "%s", buf);
1491 if (++i > MC_FILEPOS_ENTRIES)
1492 break;
1494 fclose (f);
1497 fclose (t);
1498 rename (tmp, fn);
1499 g_free (tmp);
1500 g_free (fn);
1503 extern const char *
1504 cstrcasestr (const char *haystack, const char *needle)
1506 const char *hptr;
1507 size_t i, needle_len;
1509 needle_len = strlen (needle);
1510 for (hptr = haystack; *hptr != '\0'; hptr++) {
1511 for (i = 0; i < needle_len; i++) {
1512 if (toupper ((unsigned char) hptr[i]) !=
1513 toupper ((unsigned char) needle[i]))
1514 goto next_try;
1516 return hptr;
1517 next_try:
1518 (void) 0;
1520 return NULL;
1523 const char *
1524 cstrstr (const char *haystack, const char *needle)
1526 return strstr(haystack, needle);
1529 extern char *
1530 str_unconst (const char *s)
1532 return (char *) s;
1535 #define ASCII_A (0x40 + 1)
1536 #define ASCII_Z (0x40 + 26)
1537 #define ASCII_a (0x60 + 1)
1538 #define ASCII_z (0x60 + 26)
1540 extern int
1541 ascii_alpha_to_cntrl (int ch)
1543 if ((ch >= ASCII_A && ch <= ASCII_Z)
1544 || (ch >= ASCII_a && ch <= ASCII_z)) {
1545 ch &= 0x1f;
1547 return ch;
1550 const char *
1551 Q_ (const char *s)
1553 const char *result, *sep;
1555 result = _(s);
1556 sep = strchr(result, '|');
1557 return (sep != NULL) ? sep + 1 : result;
1560 #define shell_escape_toesc(x) \
1561 (((x)==' ')||((x)=='!')||((x)=='#')||((x)=='$')||((x)=='%')|| \
1562 ((x)=='(')||((x)==')')||((x)=='\'')||((x)=='&')||((x)=='~')|| \
1563 ((x)=='{')||((x)=='}')||((x)=='[')||((x)==']')||((x)=='`')|| \
1564 ((x)=='?')||((x)=='|')||((x)=='<')||((x)=='>')||((x)==';')|| \
1565 ((x)=='*')||((x)=='\\')||((x)=='"'))
1567 #define shell_escape_nottoesc(x) \
1568 (((x)!=0) && (!shell_escape_toesc((x))))
1570 /** To be compatible with the general posix command lines we have to escape
1571 strings for the command line
1573 \params in
1574 string for escaping
1576 \returns
1577 return escaped string (which needs to be freed later)
1579 char*
1580 shell_escape(const char* src)
1582 GString *str;
1583 char *result = NULL;
1585 if ((src==NULL)||(!(*src)))
1586 return strdup("");
1588 str = g_string_new("");
1590 /* look for the first char to escape */
1591 while (1)
1593 char c;
1594 /* copy over all chars not to escape */
1595 while ((c=(*src)) && shell_escape_nottoesc(c))
1597 g_string_append_c(str,c);
1598 src++;
1601 /* at this point we either have an \0 or an char to escape */
1602 if (!c) {
1603 result = str->str;
1604 g_string_free(str,FALSE);
1605 return result;
1608 g_string_append_c(str,'\\');
1609 g_string_append_c(str,c);
1610 src++;
1614 /** Unescape paths or other strings for e.g the internal cd
1615 shell-unescape within a given buffer (writing to it!)
1617 \params src
1618 string for unescaping
1620 \returns
1621 return unescaped string (which needs to be freed)
1623 char*
1624 shell_unescape(const char* text)
1626 GString *str;
1627 char *result = NULL;
1629 if (!text)
1630 return NULL;
1633 /* look for the first \ - that's quick skipover if there's nothing to escape */
1634 const char* readptr = text;
1635 while ((*readptr) && ((*readptr)!='\\')) readptr++;
1636 if (!(*readptr)) {
1637 result = g_strdup(text);
1638 return result;
1640 str = g_string_new_len(text, readptr - text);
1642 /* if we're here, we're standing on the first '\' */
1643 char c;
1644 while ((c = *readptr))
1646 if (c=='\\')
1648 readptr++;
1649 switch ((c = *readptr))
1651 case '\0': /* end of string! malformed escape string */
1652 goto out;
1654 case 'n': g_string_append_c(str,'\n'); break;
1655 case 'r': g_string_append_c(str,'\r'); break;
1656 case 't': g_string_append_c(str,'\t'); break;
1658 case ' ':
1659 case '\\':
1660 case '#':
1661 case '$':
1662 case '%':
1663 case '(':
1664 case ')':
1665 case '[':
1666 case ']':
1667 case '{':
1668 case '}':
1669 case '<':
1670 case '>':
1671 case '!':
1672 case '*':
1673 case '?':
1674 case '~':
1675 case '`':
1676 case '"':
1677 case ';':
1678 default:
1679 g_string_append_c(str,c); break;
1682 else /* got a normal character */
1684 g_string_append_c(str,c);
1686 readptr++;
1688 out:
1690 result = str->str;
1691 g_string_free(str,FALSE);
1692 return result;
1695 /** Check if char in pointer contain escape'd chars
1697 \params in
1698 string for checking
1700 \returns
1701 return TRUE if string contain escaped chars
1702 otherwise return FALSE
1704 gboolean
1705 shell_is_char_escaped ( const char *in )
1707 if (in == NULL || !*in || in[0] != '\\')
1708 return FALSE;
1709 if (shell_escape_toesc(in[1]))
1710 return TRUE;
1711 return FALSE;