fix: incorret draw files in 8-bit codeset after recode
[midnight-commander.git] / src / util.c
blob23c15aa989b3e3bb6a75ec1ff557e9619c57e67a
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"
44 #include "strutil.h"
46 #ifdef HAVE_CHARSET
47 #include "charsets.h"
48 #endif
50 static const char app_text [] = "Midnight-Commander";
51 int easy_patterns = 1;
53 extern void str_replace(char *s, char from, char to)
55 for (; *s != '\0'; s++) {
56 if (*s == from)
57 *s = to;
61 static inline int
62 is_7bit_printable (unsigned char c)
64 return (c > 31 && c < 127);
67 static inline int
68 is_iso_printable (unsigned char c)
70 return ((c > 31 && c < 127) || c >= 160);
73 static inline int
74 is_8bit_printable (unsigned char c)
76 /* "Full 8 bits output" doesn't work on xterm */
77 if (xterm_flag)
78 return is_iso_printable (c);
80 return (c > 31 && c != 127 && c != 155);
83 int
84 is_printable (int c)
86 c &= 0xff;
88 #ifdef HAVE_CHARSET
89 /* "Display bits" is ignored, since the user controls the output
90 by setting the output codepage */
91 return is_8bit_printable (c);
92 #else
93 if (!eight_bit_clean)
94 return is_7bit_printable (c);
96 if (full_eight_bits) {
97 return is_8bit_printable (c);
98 } else
99 return is_iso_printable (c);
100 #endif /* !HAVE_CHARSET */
103 /* Calculates the message dimensions (lines and columns) */
104 void
105 msglen (const char *text, int *lines, int *columns)
107 int nlines = 1; /* even the empty string takes one line */
108 int ncolumns = 0;
109 int colindex = 0;
111 for (; *text != '\0'; text++) {
112 if (*text == '\n') {
113 nlines++;
114 colindex = 0;
115 } else {
116 colindex++;
117 if (colindex > ncolumns)
118 ncolumns = colindex;
122 *lines = nlines;
123 *columns = ncolumns;
127 * Copy from s to d, and trim the beginning if necessary, and prepend
128 * "..." in this case. The destination string can have at most len
129 * bytes, not counting trailing 0.
131 char *
132 trim (const char *s, char *d, int len)
134 int source_len;
136 /* Sanity check */
137 len = max (len, 0);
139 source_len = strlen (s);
140 if (source_len > len) {
141 /* Cannot fit the whole line */
142 if (len <= 3) {
143 /* We only have room for the dots */
144 memset (d, '.', len);
145 d[len] = 0;
146 return d;
147 } else {
148 /* Begin with ... and add the rest of the source string */
149 memset (d, '.', 3);
150 strcpy (d + 3, s + 3 + source_len - len);
152 } else
153 /* We can copy the whole line */
154 strcpy (d, s);
155 return d;
159 * Quote the filename for the purpose of inserting it into the command
160 * line. If quote_percent is 1, replace "%" with "%%" - the percent is
161 * processed by the mc command line.
163 char *
164 name_quote (const char *s, int quote_percent)
166 char *ret, *d;
168 d = ret = g_malloc (strlen (s) * 2 + 2 + 1);
169 if (*s == '-') {
170 *d++ = '.';
171 *d++ = '/';
174 for (; *s; s++, d++) {
175 switch (*s) {
176 case '%':
177 if (quote_percent)
178 *d++ = '%';
179 break;
180 case '\'':
181 case '\\':
182 case '\r':
183 case '\n':
184 case '\t':
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 case ')':
203 *d++ = '\\';
204 break;
205 case '~':
206 case '#':
207 if (d == ret)
208 *d++ = '\\';
209 break;
211 *d = *s;
213 *d = '\0';
214 return ret;
217 char *
218 fake_name_quote (const char *s, int quote_percent)
220 (void) quote_percent;
221 return g_strdup (s);
225 * Remove the middle part of the string to fit given length.
226 * Use "~" to show where the string was truncated.
227 * Return static buffer, no need to free() it.
229 const char *
230 name_trunc (const char *txt, size_t trunc_len)
232 return str_trunc (txt, trunc_len);
236 * path_trunc() is the same as name_trunc() above but
237 * it deletes possible password from path for security
238 * reasons.
240 const char *
241 path_trunc (const char *path, size_t trunc_len) {
242 char *secure_path = strip_password (g_strdup (path), 1);
244 const char *ret = str_trunc (secure_path, trunc_len);
245 g_free (secure_path);
247 return ret;
250 const char *
251 size_trunc (double size)
253 static char x [BUF_TINY];
254 long int divisor = 1;
255 const char *xtra = "";
257 if (size > 999999999L){
258 divisor = 1024;
259 xtra = "K";
260 if (size/divisor > 999999999L){
261 divisor = 1024*1024;
262 xtra = "M";
265 g_snprintf (x, sizeof (x), "%.0f%s", (size/divisor), xtra);
266 return x;
269 const char *
270 size_trunc_sep (double size)
272 static char x [60];
273 int count;
274 const char *p, *y;
275 char *d;
277 p = y = size_trunc (size);
278 p += strlen (p) - 1;
279 d = x + sizeof (x) - 1;
280 *d-- = 0;
281 while (p >= y && isalpha ((unsigned char) *p))
282 *d-- = *p--;
283 for (count = 0; p >= y; count++){
284 if (count == 3){
285 *d-- = ',';
286 count = 0;
288 *d-- = *p--;
290 d++;
291 if (*d == ',')
292 d++;
293 return d;
297 * Print file SIZE to BUFFER, but don't exceed LEN characters,
298 * not including trailing 0. BUFFER should be at least LEN+1 long.
299 * This function is called for every file on panels, so avoid
300 * floating point by any means.
302 * Units: size units (filesystem sizes are 1K blocks)
303 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
305 void
306 size_trunc_len (char *buffer, int len, off_t size, int units)
308 /* Avoid taking power for every file. */
309 static const off_t power10 [] =
310 {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
311 1000000000};
312 static const char * const suffix [] =
313 {"", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL};
314 int j = 0;
316 /* Don't print more than 9 digits - use suffix. */
317 if (len == 0 || len > 9)
318 len = 9;
320 for (j = units; suffix [j] != NULL; j++) {
321 if (size == 0) {
322 if (j == units) {
323 /* Empty files will print "0" even with minimal width. */
324 g_snprintf (buffer, len + 1, "0");
325 break;
328 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
329 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s",
330 (j > 1) ? suffix[j - 1] : "B");
331 break;
334 if (size < power10 [len - (j > 0)]) {
335 g_snprintf (buffer, len + 1, "%lu%s", (unsigned long) size, suffix[j]);
336 break;
339 /* Powers of 1024, with rounding. */
340 size = (size + 512) >> 10;
345 is_exe (mode_t mode)
347 if ((S_IXUSR & mode) || (S_IXGRP & mode) || (S_IXOTH & mode))
348 return 1;
349 return 0;
352 #define ismode(n,m) ((n & m) == m)
354 const char *
355 string_perm (mode_t mode_bits)
357 static char mode[11];
359 strcpy (mode, "----------");
360 if (S_ISDIR (mode_bits))
361 mode[0] = 'd';
362 if (S_ISCHR (mode_bits))
363 mode[0] = 'c';
364 if (S_ISBLK (mode_bits))
365 mode[0] = 'b';
366 if (S_ISLNK (mode_bits))
367 mode[0] = 'l';
368 if (S_ISFIFO (mode_bits))
369 mode[0] = 'p';
370 if (S_ISNAM (mode_bits))
371 mode[0] = 'n';
372 if (S_ISSOCK (mode_bits))
373 mode[0] = 's';
374 if (S_ISDOOR (mode_bits))
375 mode[0] = 'D';
376 if (ismode (mode_bits, S_IXOTH))
377 mode[9] = 'x';
378 if (ismode (mode_bits, S_IWOTH))
379 mode[8] = 'w';
380 if (ismode (mode_bits, S_IROTH))
381 mode[7] = 'r';
382 if (ismode (mode_bits, S_IXGRP))
383 mode[6] = 'x';
384 if (ismode (mode_bits, S_IWGRP))
385 mode[5] = 'w';
386 if (ismode (mode_bits, S_IRGRP))
387 mode[4] = 'r';
388 if (ismode (mode_bits, S_IXUSR))
389 mode[3] = 'x';
390 if (ismode (mode_bits, S_IWUSR))
391 mode[2] = 'w';
392 if (ismode (mode_bits, S_IRUSR))
393 mode[1] = 'r';
394 #ifdef S_ISUID
395 if (ismode (mode_bits, S_ISUID))
396 mode[3] = (mode[3] == 'x') ? 's' : 'S';
397 #endif /* S_ISUID */
398 #ifdef S_ISGID
399 if (ismode (mode_bits, S_ISGID))
400 mode[6] = (mode[6] == 'x') ? 's' : 'S';
401 #endif /* S_ISGID */
402 #ifdef S_ISVTX
403 if (ismode (mode_bits, S_ISVTX))
404 mode[9] = (mode[9] == 'x') ? 't' : 'T';
405 #endif /* S_ISVTX */
406 return mode;
409 /* p: string which might contain an url with a password (this parameter is
410 modified in place).
411 has_prefix = 0: The first parameter is an url without a prefix
412 (user[:pass]@]machine[:port][remote-dir). Delete
413 the password.
414 has_prefix = 1: Search p for known url prefixes. If found delete
415 the password from the url.
416 Caveat: only the first url is found
418 char *
419 strip_password (char *p, int has_prefix)
421 static const struct {
422 const char *name;
423 size_t len;
424 } prefixes[] = { {"/#ftp:", 6},
425 {"ftp://", 6},
426 {"/#mc:", 5},
427 {"mc://", 5},
428 {"/#smb:", 6},
429 {"smb://", 6},
430 {"/#sh:", 5},
431 {"sh://", 5},
432 {"ssh://", 6}
434 char *at, *inner_colon, *dir;
435 size_t i;
436 char *result = p;
438 for (i = 0; i < sizeof (prefixes)/sizeof (prefixes[0]); i++) {
439 char *q;
441 if (has_prefix) {
442 if((q = strstr (p, prefixes[i].name)) == 0)
443 continue;
444 else
445 p = q + prefixes[i].len;
448 if ((dir = strchr (p, PATH_SEP)) != NULL)
449 *dir = '\0';
451 /* search for any possible user */
452 at = strrchr (p, '@');
454 if (dir)
455 *dir = PATH_SEP;
457 /* We have a username */
458 if (at) {
459 inner_colon = memchr (p, ':', at - p);
460 if (inner_colon)
461 memmove (inner_colon, at, strlen(at) + 1);
463 break;
465 return (result);
468 const char *
469 strip_home_and_password(const char *dir)
471 size_t len;
472 static char newdir [MC_MAXPATHLEN];
474 if (home_dir && !strncmp (dir, home_dir, len = strlen (home_dir)) &&
475 (dir[len] == PATH_SEP || dir[len] == '\0')){
476 newdir [0] = '~';
477 g_strlcpy (&newdir [1], &dir [len], sizeof(newdir) - 1);
478 return newdir;
481 /* We do not strip homes in /#ftp tree, I do not like ~'s there
482 (see ftpfs.c why) */
483 g_strlcpy (newdir, dir, sizeof(newdir));
484 strip_password (newdir, 1);
485 return newdir;
488 static char *
489 maybe_start_group (char *d, int do_group, int *was_wildcard)
491 if (!do_group)
492 return d;
493 if (*was_wildcard)
494 return d;
495 *was_wildcard = 1;
496 *d++ = '\\';
497 *d++ = '(';
498 return d;
501 static char *
502 maybe_end_group (char *d, int do_group, int *was_wildcard)
504 if (!do_group)
505 return d;
506 if (!*was_wildcard)
507 return d;
508 *was_wildcard = 0;
509 *d++ = '\\';
510 *d++ = ')';
511 return d;
514 /* If shell patterns are on converts a shell pattern to a regular
515 expression. Called by regexp_match and mask_rename. */
516 /* Shouldn't we support [a-fw] type wildcards as well ?? */
517 char *
518 convert_pattern (const char *pattern, int match_type, int do_group)
520 char *d;
521 char *new_pattern;
522 int was_wildcard = 0;
523 const char *s;
525 if ((match_type != match_regex) && easy_patterns){
526 new_pattern = g_malloc (MC_MAXPATHLEN);
527 d = new_pattern;
528 if (match_type == match_file)
529 *d++ = '^';
530 for (s = pattern; *s; s++, d++){
531 switch (*s){
532 case '*':
533 d = maybe_start_group (d, do_group, &was_wildcard);
534 *d++ = '.';
535 *d = '*';
536 break;
538 case '?':
539 d = maybe_start_group (d, do_group, &was_wildcard);
540 *d = '.';
541 break;
543 case '.':
544 d = maybe_end_group (d, do_group, &was_wildcard);
545 *d++ = '\\';
546 *d = '.';
547 break;
549 default:
550 d = maybe_end_group (d, do_group, &was_wildcard);
551 *d = *s;
552 break;
555 d = maybe_end_group (d, do_group, &was_wildcard);
556 if (match_type == match_file)
557 *d++ = '$';
558 *d = 0;
559 return new_pattern;
560 } else
561 return g_strdup (pattern);
565 regexp_match (const char *pattern, const char *string, int match_type)
567 static regex_t r;
568 static char *old_pattern = NULL;
569 static int old_type;
570 int rval;
571 char *my_pattern;
573 if (!old_pattern || STRCOMP (old_pattern, pattern) || old_type != match_type){
574 if (old_pattern){
575 regfree (&r);
576 g_free (old_pattern);
577 old_pattern = NULL;
579 my_pattern = convert_pattern (pattern, match_type, 0);
580 if (regcomp (&r, my_pattern, REG_EXTENDED|REG_NOSUB|MC_ARCH_FLAGS)) {
581 g_free (my_pattern);
582 return -1;
584 old_pattern = my_pattern;
585 old_type = match_type;
587 rval = !regexec (&r, string, 0, NULL, 0);
588 return rval;
591 const char *
592 extension (const char *filename)
594 const char *d = strrchr (filename, '.');
595 return (d != NULL) ? d + 1 : "";
599 get_int (const char *file, const char *key, int def)
601 return GetPrivateProfileInt (app_text, key, def, file);
605 set_int (const char *file, const char *key, int value)
607 char buffer [BUF_TINY];
609 g_snprintf (buffer, sizeof (buffer), "%d", value);
610 return WritePrivateProfileString (app_text, key, buffer, file);
613 extern char *
614 get_config_string (const char *file, const char *key, const char *defval)
616 char buffer[1024];
617 (void)GetPrivateProfileString (app_text, key, defval, buffer, sizeof(buffer), file);
618 return g_strdup (buffer);
621 extern void
622 set_config_string (const char *file, const char *key, const char *val)
624 (void)WritePrivateProfileString (app_text, key, val, file);
628 exist_file (const char *name)
630 return access (name, R_OK) == 0;
633 char *
634 load_file (const char *filename)
636 FILE *data_file;
637 struct stat s;
638 char *data;
639 long read_size;
641 if ((data_file = fopen (filename, "r")) == NULL){
642 return 0;
644 if (fstat (fileno (data_file), &s) != 0){
645 fclose (data_file);
646 return 0;
648 data = g_malloc (s.st_size+1);
649 read_size = fread (data, 1, s.st_size, data_file);
650 data [read_size] = 0;
651 fclose (data_file);
653 if (read_size > 0)
654 return data;
655 else {
656 g_free (data);
657 return 0;
661 char *
662 load_mc_home_file (const char *filename, char **allocated_filename)
664 char *hintfile_base, *hintfile;
665 char *lang;
666 char *data;
668 hintfile_base = concat_dir_and_file (mc_home, filename);
669 lang = guess_message_value ();
671 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
672 data = load_file (hintfile);
674 if (!data) {
675 g_free (hintfile);
676 /* Fall back to the two-letter language code */
677 if (lang[0] && lang[1])
678 lang[2] = 0;
679 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
680 data = load_file (hintfile);
682 if (!data) {
683 g_free (hintfile);
684 hintfile = hintfile_base;
685 data = load_file (hintfile_base);
689 g_free (lang);
691 if (hintfile != hintfile_base)
692 g_free (hintfile_base);
694 if (allocated_filename)
695 *allocated_filename = hintfile;
696 else
697 g_free (hintfile);
699 return data;
702 /* Check strftime() results. Some systems (i.e. Solaris) have different
703 short-month-name sizes for different locales */
704 size_t
705 i18n_checktimelength (void)
707 size_t length;
708 time_t testtime = time (NULL);
709 struct tm* lt = localtime(&testtime);
711 if (lt == NULL) {
712 /* huh, localtime() doesnt seem to work ... falling back to "(invalid)" */
713 length = str_term_width1 (_(INVALID_TIME_TEXT));
714 } else {
715 char buf [MB_LEN_MAX * MAX_I18NTIMELENGTH + 1];
716 size_t a, b;
718 strftime (buf, sizeof(buf) - 1, _("%b %e %H:%M"), lt);
719 a = str_term_width1 (buf);
720 strftime (buf, sizeof(buf) - 1, _("%b %e %Y"), lt);
721 b = str_term_width1 (buf);
723 length = max (a, b);
724 length = max (str_term_width1 (_(INVALID_TIME_TEXT)), length);
727 /* Don't handle big differences. Use standard value (email bug, please) */
728 if (length > MAX_I18NTIMELENGTH || length < MIN_I18NTIMELENGTH)
729 length = STD_I18NTIMELENGTH;
731 return length;
734 const char *
735 file_date (time_t when)
737 static char timebuf [MB_LEN_MAX * MAX_I18NTIMELENGTH + 1];
738 time_t current_time = time ((time_t) 0);
739 static int i18n = 0;
740 static const char *fmtyear, *fmttime;
741 const char *fmt;
743 if (!i18n){
744 /* strftime() format string for old dates */
745 fmtyear = _("%b %e %Y");
746 /* strftime() format string for recent dates */
747 fmttime = _("%b %e %H:%M");
748 i18n = 1;
751 if (current_time > when + 6L * 30L * 24L * 60L * 60L /* Old. */
752 || current_time < when - 60L * 60L) /* In the future. */
753 /* The file is fairly old or in the future.
754 POSIX says the cutoff is 6 months old;
755 approximate this by 6*30 days.
756 Allow a 1 hour slop factor for what is considered "the future",
757 to allow for NFS server/client clock disagreement.
758 Show the year instead of the time of day. */
760 fmt = fmtyear;
761 else
762 fmt = fmttime;
764 FMT_LOCALTIME(timebuf, sizeof (timebuf), fmt, when);
766 return timebuf;
769 const char *
770 extract_line (const char *s, const char *top)
772 static char tmp_line [BUF_MEDIUM];
773 char *t = tmp_line;
775 while (*s && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line)-1 && s < top)
776 *t++ = *s++;
777 *t = 0;
778 return tmp_line;
781 /* FIXME: I should write a faster version of this (Aho-Corasick stuff) */
782 const char *
783 _icase_search (const char *text, const char *data, int *lng)
785 const char *d = text;
786 const char *e = data;
787 int dlng = 0;
789 if (lng)
790 *lng = 0;
791 for (;*e; e++) {
792 while (*(e+1) == '\b' && *(e+2)) {
793 e += 2;
794 dlng += 2;
796 if (g_ascii_toupper((gchar) *d) == g_ascii_toupper((gchar) *e))
797 d++;
798 else {
799 e -= d - text;
800 d = text;
801 dlng = 0;
803 if (!*d) {
804 if (lng)
805 *lng = strlen (text) + dlng;
806 return e+1;
809 return 0;
812 /* The basename routine */
813 const char *
814 x_basename (const char *s)
816 const char *where;
817 return ((where = strrchr (s, PATH_SEP))) ? where + 1 : s;
821 const char *
822 unix_error_string (int error_num)
824 static char buffer [BUF_LARGE];
825 #if GLIB_MAJOR_VERSION >= 2
826 gchar *strerror_currentlocale;
828 strerror_currentlocale = g_locale_from_utf8(g_strerror (error_num), -1, NULL, NULL, NULL);
829 g_snprintf (buffer, sizeof (buffer), "%s (%d)",
830 strerror_currentlocale, error_num);
831 g_free(strerror_currentlocale);
832 #else
833 g_snprintf (buffer, sizeof (buffer), "%s (%d)",
834 g_strerror (error_num), error_num);
835 #endif
836 return buffer;
839 const char *
840 skip_separators (const char *s)
842 const char *su = s;
844 for (;*su; str_cnext_char (&su))
845 if (*su != ' ' && *su != '\t' && *su != ',') break;
847 return su;
850 const char *
851 skip_numbers (const char *s)
853 const char *su = s;
855 for (;*su; str_cnext_char (&su))
856 if (!str_isdigit (su)) break;
858 return su;
861 /* Remove all control sequences from the argument string. We define
862 * "control sequence", in a sort of pidgin BNF, as follows:
864 * control-seq = Esc non-'['
865 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
867 * This scheme works for all the terminals described in my termcap /
868 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
869 * terminals. If I hear from a single person who uses such a terminal
870 * with MC, I'll be glad to add support for it. (Dugan)
871 * Non-printable characters are also removed.
874 char *
875 strip_ctrl_codes (char *s)
877 char *w; /* Current position where the stripped data is written */
878 char *r; /* Current position where the original data is read */
879 char *n;
881 if (!s)
882 return 0;
884 for (w = s, r = s; *r; ) {
885 if (*r == ESC_CHAR) {
886 /* Skip the control sequence's arguments */ ;
887 if (*(++r) == '[') {
888 /* strchr() matches trailing binary 0 */
889 while (*(++r) && strchr ("0123456789;?", *r));
893 * Now we are at the last character of the sequence.
894 * Skip it unless it's binary 0.
896 if (*r)
897 r++;
898 continue;
901 n = str_get_next_char (r);
902 if (str_isprint (r)) {
903 memmove (w, r, n - r);
904 w+= n - r;
906 r = n;
908 *w = 0;
909 return s;
913 #ifndef USE_VFS
914 char *
915 get_current_wd (char *buffer, int size)
917 char *p;
918 int len;
920 p = g_get_current_dir ();
921 len = strlen(p) + 1;
923 if (len > size) {
924 g_free (p);
925 return NULL;
928 memcpy (buffer, p, len);
929 g_free (p);
931 return buffer;
933 #endif /* !USE_VFS */
935 enum compression_type
936 get_compression_type (int fd)
938 unsigned char magic[16];
940 /* Read the magic signature */
941 if (mc_read (fd, (char *) magic, 4) != 4)
942 return COMPRESSION_NONE;
944 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
945 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236)) {
946 return COMPRESSION_GZIP;
949 /* PKZIP_MAGIC */
950 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003
951 && magic[3] == 004) {
952 /* Read compression type */
953 mc_lseek (fd, 8, SEEK_SET);
954 if (mc_read (fd, (char *) magic, 2) != 2)
955 return COMPRESSION_NONE;
957 /* Gzip can handle only deflated (8) or stored (0) files */
958 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
959 return COMPRESSION_NONE;
961 /* Compatible with gzip */
962 return COMPRESSION_GZIP;
965 /* PACK_MAGIC and LZH_MAGIC and compress magic */
966 if (magic[0] == 037
967 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235)) {
968 /* Compatible with gzip */
969 return COMPRESSION_GZIP;
972 /* BZIP and BZIP2 files */
973 if ((magic[0] == 'B') && (magic[1] == 'Z') &&
974 (magic[3] >= '1') && (magic[3] <= '9')) {
975 switch (magic[2]) {
976 case '0':
977 return COMPRESSION_BZIP;
978 case 'h':
979 return COMPRESSION_BZIP2;
983 /* LZMA files; both LZMA_Alone and LZMA utils formats. The LZMA_Alone
984 * format is used by the LZMA_Alone tool from LZMA SDK. The LZMA utils
985 * format is the default format of LZMA utils 4.32.1 and later. */
986 if (magic[0] < 0xE1 || (magic[0] == 0xFF && magic[1] == 'L' &&
987 magic[2] == 'Z' && magic[3] == 'M')) {
988 if (mc_read (fd, (char *) magic + 4, 9) == 9) {
989 /* LZMA utils format */
990 if (magic[0] == 0xFF && magic[4] == 'A' && magic[5] == 0x00)
991 return COMPRESSION_LZMA;
992 /* The LZMA_Alone format has no magic bytes, thus we
993 * need to play a wizard. This can give false positives,
994 * thus the detection below should be removed when
995 * the newer LZMA utils format has got popular. */
996 if (magic[0] < 0xE1 && magic[4] < 0x20 &&
997 ((magic[10] == 0x00 && magic[11] == 0x00 &&
998 magic[12] == 0x00) ||
999 (magic[5] == 0xFF && magic[6] == 0xFF &&
1000 magic[7] == 0xFF && magic[8] == 0xFF &&
1001 magic[9] == 0xFF && magic[10] == 0xFF &&
1002 magic[11] == 0xFF && magic[12] == 0xFF)))
1003 return COMPRESSION_LZMA;
1007 return 0;
1010 const char *
1011 decompress_extension (int type)
1013 switch (type){
1014 case COMPRESSION_GZIP: return "#ugz";
1015 case COMPRESSION_BZIP: return "#ubz";
1016 case COMPRESSION_BZIP2: return "#ubz2";
1017 case COMPRESSION_LZMA: return "#ulzma";
1019 /* Should never reach this place */
1020 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
1021 return 0;
1024 /* Hooks */
1025 void
1026 add_hook (Hook **hook_list, void (*hook_fn)(void *), void *data)
1028 Hook *new_hook = g_new (Hook, 1);
1030 new_hook->hook_fn = hook_fn;
1031 new_hook->next = *hook_list;
1032 new_hook->hook_data = data;
1034 *hook_list = new_hook;
1037 void
1038 execute_hooks (Hook *hook_list)
1040 Hook *new_hook = 0;
1041 Hook *p;
1043 /* We copy the hook list first so tahat we let the hook
1044 * function call delete_hook
1047 while (hook_list){
1048 add_hook (&new_hook, hook_list->hook_fn, hook_list->hook_data);
1049 hook_list = hook_list->next;
1051 p = new_hook;
1053 while (new_hook){
1054 (*new_hook->hook_fn)(new_hook->hook_data);
1055 new_hook = new_hook->next;
1058 for (hook_list = p; hook_list;){
1059 p = hook_list;
1060 hook_list = hook_list->next;
1061 g_free (p);
1065 void
1066 delete_hook (Hook **hook_list, void (*hook_fn)(void *))
1068 Hook *current, *new_list, *next;
1070 new_list = 0;
1072 for (current = *hook_list; current; current = next){
1073 next = current->next;
1074 if (current->hook_fn == hook_fn)
1075 g_free (current);
1076 else
1077 add_hook (&new_list, current->hook_fn, current->hook_data);
1079 *hook_list = new_list;
1083 hook_present (Hook *hook_list, void (*hook_fn)(void *))
1085 Hook *p;
1087 for (p = hook_list; p; p = p->next)
1088 if (p->hook_fn == hook_fn)
1089 return 1;
1090 return 0;
1093 void
1094 wipe_password (char *passwd)
1096 char *p = passwd;
1098 if (!p)
1099 return;
1100 for (;*p ; p++)
1101 *p = 0;
1102 g_free (passwd);
1105 /* Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key */
1106 /* Returns a newly allocated string */
1107 char *
1108 convert_controls (const char *p)
1110 char *valcopy = g_strdup (p);
1111 char *q;
1113 /* Parse the escape special character */
1114 for (q = valcopy; *p;){
1115 if (*p == '\\'){
1116 p++;
1117 if ((*p == 'e') || (*p == 'E')){
1118 p++;
1119 *q++ = ESC_CHAR;
1121 } else {
1122 if (*p == '^'){
1123 p++;
1124 if (*p == '^')
1125 *q++ = *p++;
1126 else {
1127 char c = (*p | 0x20);
1128 if (c >= 'a' && c <= 'z') {
1129 *q++ = c - 'a' + 1;
1130 p++;
1131 } else if (*p)
1132 p++;
1134 } else
1135 *q++ = *p++;
1138 *q = 0;
1139 return valcopy;
1142 static char *
1143 resolve_symlinks (const char *path)
1145 char *buf, *buf2, *q, *r, c;
1146 int len;
1147 struct stat mybuf;
1148 const char *p;
1150 if (*path != PATH_SEP)
1151 return NULL;
1152 r = buf = g_malloc (MC_MAXPATHLEN);
1153 buf2 = g_malloc (MC_MAXPATHLEN);
1154 *r++ = PATH_SEP;
1155 *r = 0;
1156 p = path;
1157 for (;;) {
1158 q = strchr (p + 1, PATH_SEP);
1159 if (!q) {
1160 q = strchr (p + 1, 0);
1161 if (q == p + 1)
1162 break;
1164 c = *q;
1165 *q = 0;
1166 if (mc_lstat (path, &mybuf) < 0) {
1167 g_free (buf);
1168 g_free (buf2);
1169 *q = c;
1170 return NULL;
1172 if (!S_ISLNK (mybuf.st_mode))
1173 strcpy (r, p + 1);
1174 else {
1175 len = mc_readlink (path, buf2, MC_MAXPATHLEN - 1);
1176 if (len < 0) {
1177 g_free (buf);
1178 g_free (buf2);
1179 *q = c;
1180 return NULL;
1182 buf2 [len] = 0;
1183 if (*buf2 == PATH_SEP)
1184 strcpy (buf, buf2);
1185 else
1186 strcpy (r, buf2);
1188 canonicalize_pathname (buf);
1189 r = strchr (buf, 0);
1190 if (!*r || *(r - 1) != PATH_SEP) {
1191 *r++ = PATH_SEP;
1192 *r = 0;
1194 *q = c;
1195 p = q;
1196 if (!c)
1197 break;
1199 if (!*buf)
1200 strcpy (buf, PATH_SEP_STR);
1201 else if (*(r - 1) == PATH_SEP && r != buf + 1)
1202 *(r - 1) = 0;
1203 g_free (buf2);
1204 return buf;
1207 /* Finds out a relative path from first to second, i.e. goes as many ..
1208 * as needed up in first and then goes down using second */
1209 char *
1210 diff_two_paths (const char *first, const char *second)
1212 char *p, *q, *r, *s, *buf = NULL;
1213 int i, j, prevlen = -1, currlen;
1214 char *my_first = NULL, *my_second = NULL;
1216 my_first = resolve_symlinks (first);
1217 if (my_first == NULL)
1218 return NULL;
1219 my_second = resolve_symlinks (second);
1220 if (my_second == NULL) {
1221 g_free (my_first);
1222 return NULL;
1224 for (j = 0; j < 2; j++) {
1225 p = my_first;
1226 q = my_second;
1227 for (;;) {
1228 r = strchr (p, PATH_SEP);
1229 s = strchr (q, PATH_SEP);
1230 if (!r || !s)
1231 break;
1232 *r = 0; *s = 0;
1233 if (strcmp (p, q)) {
1234 *r = PATH_SEP; *s = PATH_SEP;
1235 break;
1236 } else {
1237 *r = PATH_SEP; *s = PATH_SEP;
1239 p = r + 1;
1240 q = s + 1;
1242 p--;
1243 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++);
1244 currlen = (i + 1) * 3 + strlen (q) + 1;
1245 if (j) {
1246 if (currlen < prevlen)
1247 g_free (buf);
1248 else {
1249 g_free (my_first);
1250 g_free (my_second);
1251 return buf;
1254 p = buf = g_malloc (currlen);
1255 prevlen = currlen;
1256 for (; i >= 0; i--, p += 3)
1257 strcpy (p, "../");
1258 strcpy (p, q);
1260 g_free (my_first);
1261 g_free (my_second);
1262 return buf;
1265 /* If filename is NULL, then we just append PATH_SEP to the dir */
1266 char *
1267 concat_dir_and_file (const char *dir, const char *file)
1269 int i = strlen (dir);
1271 if (dir [i-1] == PATH_SEP)
1272 return g_strconcat (dir, file, (char *) NULL);
1273 else
1274 return g_strconcat (dir, PATH_SEP_STR, file, (char *) NULL);
1277 /* Append text to GList, remove all entries with the same text */
1278 GList *
1279 list_append_unique (GList *list, char *text)
1281 GList *link, *newlink;
1284 * Go to the last position and traverse the list backwards
1285 * starting from the second last entry to make sure that we
1286 * are not removing the current link.
1288 list = g_list_append (list, text);
1289 list = g_list_last (list);
1290 link = g_list_previous (list);
1292 while (link) {
1293 newlink = g_list_previous (link);
1294 if (!strcmp ((char *) link->data, text)) {
1295 g_free (link->data);
1296 g_list_remove_link (list, link);
1297 g_list_free_1 (link);
1299 link = newlink;
1302 return list;
1305 /* Following code heavily borrows from libiberty, mkstemps.c */
1307 /* Number of attempts to create a temporary file */
1308 #ifndef TMP_MAX
1309 #define TMP_MAX 16384
1310 #endif /* !TMP_MAX */
1313 * Arguments:
1314 * pname (output) - pointer to the name of the temp file (needs g_free).
1315 * NULL if the function fails.
1316 * prefix - part of the filename before the random part.
1317 * Prepend $TMPDIR or /tmp if there are no path separators.
1318 * suffix - if not NULL, part of the filename after the random part.
1320 * Result:
1321 * handle of the open file or -1 if couldn't open any.
1324 mc_mkstemps (char **pname, const char *prefix, const char *suffix)
1326 static const char letters[]
1327 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1328 static unsigned long value;
1329 struct timeval tv;
1330 char *tmpbase;
1331 char *tmpname;
1332 char *XXXXXX;
1333 int count;
1335 if (strchr (prefix, PATH_SEP) == NULL) {
1336 /* Add prefix first to find the position of XXXXXX */
1337 tmpbase = concat_dir_and_file (mc_tmpdir (), prefix);
1338 } else {
1339 tmpbase = g_strdup (prefix);
1342 tmpname = g_strconcat (tmpbase, "XXXXXX", suffix, (char *) NULL);
1343 *pname = tmpname;
1344 XXXXXX = &tmpname[strlen (tmpbase)];
1345 g_free (tmpbase);
1347 /* Get some more or less random data. */
1348 gettimeofday (&tv, NULL);
1349 value += (tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
1351 for (count = 0; count < TMP_MAX; ++count) {
1352 unsigned long v = value;
1353 int fd;
1355 /* Fill in the random bits. */
1356 XXXXXX[0] = letters[v % 62];
1357 v /= 62;
1358 XXXXXX[1] = letters[v % 62];
1359 v /= 62;
1360 XXXXXX[2] = letters[v % 62];
1361 v /= 62;
1362 XXXXXX[3] = letters[v % 62];
1363 v /= 62;
1364 XXXXXX[4] = letters[v % 62];
1365 v /= 62;
1366 XXXXXX[5] = letters[v % 62];
1368 fd = open (tmpname, O_RDWR | O_CREAT | O_TRUNC | O_EXCL,
1369 S_IRUSR | S_IWUSR);
1370 if (fd >= 0) {
1371 /* Successfully created. */
1372 return fd;
1375 /* This is a random value. It is only necessary that the next
1376 TMP_MAX values generated by adding 7777 to VALUE are different
1377 with (module 2^32). */
1378 value += 7777;
1381 /* Unsuccessful. Free the filename. */
1382 g_free (tmpname);
1383 *pname = NULL;
1385 return -1;
1389 * Read and restore position for the given filename.
1390 * If there is no stored data, return line 1 and col 0.
1392 void
1393 load_file_position (const char *filename, long *line, long *column)
1395 char *fn;
1396 FILE *f;
1397 char buf[MC_MAXPATHLEN + 20];
1398 int len;
1400 /* defaults */
1401 *line = 1;
1402 *column = 0;
1404 /* open file with positions */
1405 fn = concat_dir_and_file (home_dir, MC_FILEPOS);
1406 f = fopen (fn, "r");
1407 g_free (fn);
1408 if (!f)
1409 return;
1411 len = strlen (filename);
1413 while (fgets (buf, sizeof (buf), f)) {
1414 const char *p;
1416 /* check if the filename matches the beginning of string */
1417 if (strncmp (buf, filename, len) != 0)
1418 continue;
1420 /* followed by single space */
1421 if (buf[len] != ' ')
1422 continue;
1424 /* and string without spaces */
1425 p = &buf[len + 1];
1426 if (strchr (p, ' '))
1427 continue;
1429 *line = strtol(p, const_cast(char **, &p), 10);
1430 if (*p == ';') {
1431 *column = strtol(p+1, const_cast(char **, &p), 10);
1432 if (*p != '\n')
1433 *column = 0;
1434 } else
1435 *line = 1;
1437 fclose (f);
1440 /* Save position for the given file */
1441 void
1442 save_file_position (const char *filename, long line, long column)
1444 char *tmp, *fn;
1445 FILE *f, *t;
1446 char buf[MC_MAXPATHLEN + 20];
1447 int i = 1;
1448 int len;
1450 len = strlen (filename);
1452 tmp = concat_dir_and_file (home_dir, MC_FILEPOS_TMP);
1453 fn = concat_dir_and_file (home_dir, MC_FILEPOS);
1455 /* open temporary file */
1456 t = fopen (tmp, "w");
1457 if (!t) {
1458 g_free (tmp);
1459 g_free (fn);
1460 return;
1463 /* put the new record */
1464 if (line != 1 || column != 0) {
1465 fprintf (t, "%s %ld;%ld\n", filename, line, column);
1468 /* copy records from the old file */
1469 f = fopen (fn, "r");
1470 if (f) {
1471 while (fgets (buf, sizeof (buf), f)) {
1472 /* Skip entries for the current filename */
1473 if (strncmp (buf, filename, len) == 0 && buf[len] == ' '
1474 && !strchr (&buf[len + 1], ' '))
1475 continue;
1477 fprintf (t, "%s", buf);
1478 if (++i > MC_FILEPOS_ENTRIES)
1479 break;
1481 fclose (f);
1484 fclose (t);
1485 rename (tmp, fn);
1486 g_free (tmp);
1487 g_free (fn);
1490 extern const char *
1491 cstrcasestr (const char *haystack, const char *needle)
1493 char *nee = str_create_search_needle (needle, 0);
1494 const char *result = str_search_first (haystack, nee, 0);
1495 str_release_search_needle (nee, 0);
1496 return result;
1499 const char *
1500 cstrstr (const char *haystack, const char *needle)
1502 return strstr(haystack, needle);
1505 extern char *
1506 str_unconst (const char *s)
1508 return (char *) s;
1511 #define ASCII_A (0x40 + 1)
1512 #define ASCII_Z (0x40 + 26)
1513 #define ASCII_a (0x60 + 1)
1514 #define ASCII_z (0x60 + 26)
1516 extern int
1517 ascii_alpha_to_cntrl (int ch)
1519 if ((ch >= ASCII_A && ch <= ASCII_Z)
1520 || (ch >= ASCII_a && ch <= ASCII_z)) {
1521 ch &= 0x1f;
1523 return ch;
1526 const char *
1527 Q_ (const char *s)
1529 const char *result, *sep;
1531 result = _(s);
1532 sep = strchr(result, '|');
1533 return (sep != NULL) ? sep + 1 : result;
1536 #define shell_escape_toesc(x) \
1537 (((x)==' ')||((x)=='!')||((x)=='#')||((x)=='$')||((x)=='%')|| \
1538 ((x)=='(')||((x)==')')||((x)=='\'')||((x)=='&')||((x)=='~')|| \
1539 ((x)=='{')||((x)=='}')||((x)=='[')||((x)==']')||((x)=='`')|| \
1540 ((x)=='?')||((x)=='|')||((x)=='<')||((x)=='>')||((x)==';')|| \
1541 ((x)=='*')||((x)=='\\')||((x)=='"'))
1543 #define shell_escape_nottoesc(x) \
1544 (((x)!=0) && (!shell_escape_toesc((x))))
1546 /** To be compatible with the general posix command lines we have to escape
1547 strings for the command line
1549 \params in
1550 string for escaping
1552 \returns
1553 return escaped string (which needs to be freed later)
1555 char*
1556 shell_escape(const char* src)
1558 GString *str;
1559 char *result = NULL;
1561 if ((src==NULL)||(!(*src)))
1562 return strdup("");
1564 str = g_string_new("");
1566 /* look for the first char to escape */
1567 while (1)
1569 char c;
1570 /* copy over all chars not to escape */
1571 while ((c=(*src)) && shell_escape_nottoesc(c))
1573 g_string_append_c(str,c);
1574 src++;
1577 /* at this point we either have an \0 or an char to escape */
1578 if (!c) {
1579 result = str->str;
1580 g_string_free(str,FALSE);
1581 return result;
1584 g_string_append_c(str,'\\');
1585 g_string_append_c(str,c);
1586 src++;
1590 /** Unescape paths or other strings for e.g the internal cd
1591 shell-unescape within a given buffer (writing to it!)
1593 \params src
1594 string for unescaping
1596 \returns
1597 return unescaped string (which needs to be freed)
1599 char*
1600 shell_unescape(const char* text)
1602 GString *str;
1603 char *result = NULL;
1605 if (!text)
1606 return NULL;
1609 /* look for the first \ - that's quick skipover if there's nothing to escape */
1610 const char* readptr = text;
1611 while ((*readptr) && ((*readptr)!='\\')) readptr++;
1612 if (!(*readptr)) {
1613 result = g_strdup(text);
1614 return result;
1616 str = g_string_new_len(text, readptr - text);
1618 /* if we're here, we're standing on the first '\' */
1619 char c;
1620 while ((c = *readptr))
1622 if (c=='\\')
1624 readptr++;
1625 switch ((c = *readptr))
1627 case '\0': /* end of string! malformed escape string */
1628 goto out;
1630 case 'n': g_string_append_c(str,'\n'); break;
1631 case 'r': g_string_append_c(str,'\r'); break;
1632 case 't': g_string_append_c(str,'\t'); break;
1634 case ' ':
1635 case '\\':
1636 case '#':
1637 case '$':
1638 case '%':
1639 case '(':
1640 case ')':
1641 case '[':
1642 case ']':
1643 case '{':
1644 case '}':
1645 case '<':
1646 case '>':
1647 case '!':
1648 case '*':
1649 case '?':
1650 case '~':
1651 case '`':
1652 case '"':
1653 case ';':
1654 default:
1655 g_string_append_c(str,c); break;
1658 else /* got a normal character */
1660 g_string_append_c(str,c);
1662 readptr++;
1664 out:
1666 result = str->str;
1667 g_string_free(str,FALSE);
1668 return result;
1671 /** Check if char in pointer contain escape'd chars
1673 \params in
1674 string for checking
1676 \returns
1677 return TRUE if string contain escaped chars
1678 otherwise return FALSE
1680 gboolean
1681 shell_is_char_escaped ( const char *in )
1683 if (in == NULL || !*in || in[0] != '\\')
1684 return FALSE;
1685 if (shell_escape_toesc(in[1]))
1686 return TRUE;
1687 return FALSE;