replaced calls to g_strdup() by mhl_str_dup()
[midnight-commander.git] / src / util.c
bloba7f06ffb16051bd90d76740ea8bf34cc31a8c904
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 <mhl/string.h>
39 #include "global.h"
40 #include "profile.h"
41 #include "main.h" /* mc_home */
42 #include "cmd.h" /* guess_message_value */
43 #include "mountlist.h"
44 #include "win.h" /* xterm_flag */
45 #include "timefmt.h"
47 #ifdef HAVE_CHARSET
48 #include "charsets.h"
49 #endif
51 static const char app_text [] = "Midnight-Commander";
52 int easy_patterns = 1;
54 extern void str_replace(char *s, char from, char to)
56 for (; *s != '\0'; s++) {
57 if (*s == from)
58 *s = to;
62 static inline int
63 is_7bit_printable (unsigned char c)
65 return (c > 31 && c < 127);
68 static inline int
69 is_iso_printable (unsigned char c)
71 return ((c > 31 && c < 127) || c >= 160);
74 static inline int
75 is_8bit_printable (unsigned char c)
77 /* "Full 8 bits output" doesn't work on xterm */
78 if (xterm_flag)
79 return is_iso_printable (c);
81 return (c > 31 && c != 127 && c != 155);
84 int
85 is_printable (int c)
87 c &= 0xff;
89 #ifdef HAVE_CHARSET
90 /* "Display bits" is ignored, since the user controls the output
91 by setting the output codepage */
92 return is_8bit_printable (c);
93 #else
94 if (!eight_bit_clean)
95 return is_7bit_printable (c);
97 if (full_eight_bits) {
98 return is_8bit_printable (c);
99 } else
100 return is_iso_printable (c);
101 #endif /* !HAVE_CHARSET */
104 /* Calculates the message dimensions (lines and columns) */
105 void
106 msglen (const char *text, int *lines, int *columns)
108 int nlines = 1; /* even the empty string takes one line */
109 int ncolumns = 0;
110 int colindex = 0;
112 for (; *text != '\0'; text++) {
113 if (*text == '\n') {
114 nlines++;
115 colindex = 0;
116 } else {
117 colindex++;
118 if (colindex > ncolumns)
119 ncolumns = colindex;
123 *lines = nlines;
124 *columns = ncolumns;
128 * Copy from s to d, and trim the beginning if necessary, and prepend
129 * "..." in this case. The destination string can have at most len
130 * bytes, not counting trailing 0.
132 char *
133 trim (const char *s, char *d, int len)
135 int source_len;
137 /* Sanity check */
138 len = max (len, 0);
140 source_len = strlen (s);
141 if (source_len > len) {
142 /* Cannot fit the whole line */
143 if (len <= 3) {
144 /* We only have room for the dots */
145 memset (d, '.', len);
146 d[len] = 0;
147 return d;
148 } else {
149 /* Begin with ... and add the rest of the source string */
150 memset (d, '.', 3);
151 strcpy (d + 3, s + 3 + source_len - len);
153 } else
154 /* We can copy the whole line */
155 strcpy (d, s);
156 return d;
160 * Quote the filename for the purpose of inserting it into the command
161 * line. If quote_percent is 1, replace "%" with "%%" - the percent is
162 * processed by the mc command line.
164 char *
165 name_quote (const char *s, int quote_percent)
167 char *ret, *d;
169 d = ret = g_malloc (strlen (s) * 2 + 2 + 1);
170 if (*s == '-') {
171 *d++ = '.';
172 *d++ = '/';
175 for (; *s; s++, d++) {
176 switch (*s) {
177 case '%':
178 if (quote_percent)
179 *d++ = '%';
180 break;
181 case '\'':
182 case '\\':
183 case '\r':
184 case '\n':
185 case '\t':
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 case ')':
204 *d++ = '\\';
205 break;
206 case '~':
207 case '#':
208 if (d == ret)
209 *d++ = '\\';
210 break;
212 *d = *s;
214 *d = '\0';
215 return ret;
218 char *
219 fake_name_quote (const char *s, int quote_percent)
221 (void) quote_percent;
222 return mhl_str_dup (s);
226 * Remove the middle part of the string to fit given length.
227 * Use "~" to show where the string was truncated.
228 * Return static buffer, no need to free() it.
230 const char *
231 name_trunc (const char *txt, int trunc_len)
233 static char x[MC_MAXPATHLEN + MC_MAXPATHLEN];
234 int txt_len;
235 char *p;
237 if ((size_t) trunc_len > sizeof (x) - 1) {
238 trunc_len = sizeof (x) - 1;
240 txt_len = strlen (txt);
241 if (txt_len <= trunc_len) {
242 strcpy (x, txt);
243 } else {
244 int y = (trunc_len / 2) + (trunc_len % 2);
245 strncpy (x, txt, y);
246 strncpy (x + y, txt + txt_len - (trunc_len / 2), trunc_len / 2);
247 x[y] = '~';
249 x[trunc_len] = 0;
250 for (p = x; *p; p++)
251 if (!is_printable (*p))
252 *p = '?';
253 return x;
257 * path_trunc() is the same as name_trunc() above but
258 * it deletes possible password from path for security
259 * reasons.
261 const char *
262 path_trunc (const char *path, int trunc_len) {
263 const char *ret;
264 char *secure_path = strip_password (mhl_str_dup (path), 1);
266 ret = name_trunc (secure_path, trunc_len);
267 g_free (secure_path);
269 return ret;
272 const char *
273 size_trunc (double size)
275 static char x [BUF_TINY];
276 long int divisor = 1;
277 const char *xtra = "";
279 if (size > 999999999L){
280 divisor = 1024;
281 xtra = "K";
282 if (size/divisor > 999999999L){
283 divisor = 1024*1024;
284 xtra = "M";
287 g_snprintf (x, sizeof (x), "%.0f%s", (size/divisor), xtra);
288 return x;
291 const char *
292 size_trunc_sep (double size)
294 static char x [60];
295 int count;
296 const char *p, *y;
297 char *d;
299 p = y = size_trunc (size);
300 p += strlen (p) - 1;
301 d = x + sizeof (x) - 1;
302 *d-- = 0;
303 while (p >= y && isalpha ((unsigned char) *p))
304 *d-- = *p--;
305 for (count = 0; p >= y; count++){
306 if (count == 3){
307 *d-- = ',';
308 count = 0;
310 *d-- = *p--;
312 d++;
313 if (*d == ',')
314 d++;
315 return d;
319 * Print file SIZE to BUFFER, but don't exceed LEN characters,
320 * not including trailing 0. BUFFER should be at least LEN+1 long.
321 * This function is called for every file on panels, so avoid
322 * floating point by any means.
324 * Units: size units (filesystem sizes are 1K blocks)
325 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
327 void
328 size_trunc_len (char *buffer, int len, off_t size, int units)
330 /* Avoid taking power for every file. */
331 static const off_t power10 [] =
332 {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
333 1000000000};
334 static const char * const suffix [] =
335 {"", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL};
336 int j = 0;
338 /* Don't print more than 9 digits - use suffix. */
339 if (len == 0 || len > 9)
340 len = 9;
342 for (j = units; suffix [j] != NULL; j++) {
343 if (size == 0) {
344 if (j == units) {
345 /* Empty files will print "0" even with minimal width. */
346 g_snprintf (buffer, len + 1, "0");
347 break;
350 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
351 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s",
352 (j > 1) ? suffix[j - 1] : "B");
353 break;
356 if (size < power10 [len - (j > 0)]) {
357 g_snprintf (buffer, len + 1, "%lu%s", (unsigned long) size, suffix[j]);
358 break;
361 /* Powers of 1024, with rounding. */
362 size = (size + 512) >> 10;
367 is_exe (mode_t mode)
369 if ((S_IXUSR & mode) || (S_IXGRP & mode) || (S_IXOTH & mode))
370 return 1;
371 return 0;
374 #define ismode(n,m) ((n & m) == m)
376 const char *
377 string_perm (mode_t mode_bits)
379 static char mode[11];
381 strcpy (mode, "----------");
382 if (S_ISDIR (mode_bits))
383 mode[0] = 'd';
384 if (S_ISCHR (mode_bits))
385 mode[0] = 'c';
386 if (S_ISBLK (mode_bits))
387 mode[0] = 'b';
388 if (S_ISLNK (mode_bits))
389 mode[0] = 'l';
390 if (S_ISFIFO (mode_bits))
391 mode[0] = 'p';
392 if (S_ISNAM (mode_bits))
393 mode[0] = 'n';
394 if (S_ISSOCK (mode_bits))
395 mode[0] = 's';
396 if (S_ISDOOR (mode_bits))
397 mode[0] = 'D';
398 if (ismode (mode_bits, S_IXOTH))
399 mode[9] = 'x';
400 if (ismode (mode_bits, S_IWOTH))
401 mode[8] = 'w';
402 if (ismode (mode_bits, S_IROTH))
403 mode[7] = 'r';
404 if (ismode (mode_bits, S_IXGRP))
405 mode[6] = 'x';
406 if (ismode (mode_bits, S_IWGRP))
407 mode[5] = 'w';
408 if (ismode (mode_bits, S_IRGRP))
409 mode[4] = 'r';
410 if (ismode (mode_bits, S_IXUSR))
411 mode[3] = 'x';
412 if (ismode (mode_bits, S_IWUSR))
413 mode[2] = 'w';
414 if (ismode (mode_bits, S_IRUSR))
415 mode[1] = 'r';
416 #ifdef S_ISUID
417 if (ismode (mode_bits, S_ISUID))
418 mode[3] = (mode[3] == 'x') ? 's' : 'S';
419 #endif /* S_ISUID */
420 #ifdef S_ISGID
421 if (ismode (mode_bits, S_ISGID))
422 mode[6] = (mode[6] == 'x') ? 's' : 'S';
423 #endif /* S_ISGID */
424 #ifdef S_ISVTX
425 if (ismode (mode_bits, S_ISVTX))
426 mode[9] = (mode[9] == 'x') ? 't' : 'T';
427 #endif /* S_ISVTX */
428 return mode;
431 /* p: string which might contain an url with a password (this parameter is
432 modified in place).
433 has_prefix = 0: The first parameter is an url without a prefix
434 (user[:pass]@]machine[:port][remote-dir). Delete
435 the password.
436 has_prefix = 1: Search p for known url prefixes. If found delete
437 the password from the url.
438 Caveat: only the first url is found
440 char *
441 strip_password (char *p, int has_prefix)
443 static const struct {
444 const char *name;
445 size_t len;
446 } prefixes[] = { {"/#ftp:", 6},
447 {"ftp://", 6},
448 {"/#mc:", 5},
449 {"mc://", 5},
450 {"/#smb:", 6},
451 {"smb://", 6},
452 {"/#sh:", 5},
453 {"sh://", 5},
454 {"ssh://", 6}
456 char *at, *inner_colon, *dir;
457 size_t i;
458 char *result = p;
460 for (i = 0; i < sizeof (prefixes)/sizeof (prefixes[0]); i++) {
461 char *q;
463 if (has_prefix) {
464 if((q = strstr (p, prefixes[i].name)) == 0)
465 continue;
466 else
467 p = q + prefixes[i].len;
470 if ((dir = strchr (p, PATH_SEP)) != NULL)
471 *dir = '\0';
473 /* search for any possible user */
474 at = strrchr (p, '@');
476 if (dir)
477 *dir = PATH_SEP;
479 /* We have a username */
480 if (at) {
481 inner_colon = memchr (p, ':', at - p);
482 if (inner_colon)
483 memmove (inner_colon, at, strlen(at) + 1);
485 break;
487 return (result);
490 const char *
491 strip_home_and_password(const char *dir)
493 size_t len;
494 static char newdir [MC_MAXPATHLEN];
496 if (home_dir && !strncmp (dir, home_dir, len = strlen (home_dir)) &&
497 (dir[len] == PATH_SEP || dir[len] == '\0')){
498 newdir [0] = '~';
499 g_strlcpy (&newdir [1], &dir [len], sizeof(newdir) - 1);
500 return newdir;
503 /* We do not strip homes in /#ftp tree, I do not like ~'s there
504 (see ftpfs.c why) */
505 g_strlcpy (newdir, dir, sizeof(newdir));
506 strip_password (newdir, 1);
507 return newdir;
510 static char *
511 maybe_start_group (char *d, int do_group, int *was_wildcard)
513 if (!do_group)
514 return d;
515 if (*was_wildcard)
516 return d;
517 *was_wildcard = 1;
518 *d++ = '\\';
519 *d++ = '(';
520 return d;
523 static char *
524 maybe_end_group (char *d, int do_group, int *was_wildcard)
526 if (!do_group)
527 return d;
528 if (!*was_wildcard)
529 return d;
530 *was_wildcard = 0;
531 *d++ = '\\';
532 *d++ = ')';
533 return d;
536 /* If shell patterns are on converts a shell pattern to a regular
537 expression. Called by regexp_match and mask_rename. */
538 /* Shouldn't we support [a-fw] type wildcards as well ?? */
539 char *
540 convert_pattern (const char *pattern, int match_type, int do_group)
542 char *d;
543 char *new_pattern;
544 int was_wildcard = 0;
545 const char *s;
547 if ((match_type != match_regex) && easy_patterns){
548 new_pattern = g_malloc (MC_MAXPATHLEN);
549 d = new_pattern;
550 if (match_type == match_file)
551 *d++ = '^';
552 for (s = pattern; *s; s++, d++){
553 switch (*s){
554 case '*':
555 d = maybe_start_group (d, do_group, &was_wildcard);
556 *d++ = '.';
557 *d = '*';
558 break;
560 case '?':
561 d = maybe_start_group (d, do_group, &was_wildcard);
562 *d = '.';
563 break;
565 case '.':
566 d = maybe_end_group (d, do_group, &was_wildcard);
567 *d++ = '\\';
568 *d = '.';
569 break;
571 default:
572 d = maybe_end_group (d, do_group, &was_wildcard);
573 *d = *s;
574 break;
577 d = maybe_end_group (d, do_group, &was_wildcard);
578 if (match_type == match_file)
579 *d++ = '$';
580 *d = 0;
581 return new_pattern;
582 } else
583 return mhl_str_dup (pattern);
587 regexp_match (const char *pattern, const char *string, int match_type)
589 static regex_t r;
590 static char *old_pattern = NULL;
591 static int old_type;
592 int rval;
593 char *my_pattern;
595 if (!old_pattern || STRCOMP (old_pattern, pattern) || old_type != match_type){
596 if (old_pattern){
597 regfree (&r);
598 g_free (old_pattern);
599 old_pattern = NULL;
601 my_pattern = convert_pattern (pattern, match_type, 0);
602 if (regcomp (&r, my_pattern, REG_EXTENDED|REG_NOSUB|MC_ARCH_FLAGS)) {
603 g_free (my_pattern);
604 return -1;
606 old_pattern = my_pattern;
607 old_type = match_type;
609 rval = !regexec (&r, string, 0, NULL, 0);
610 return rval;
613 const char *
614 extension (const char *filename)
616 const char *d = strrchr (filename, '.');
617 return (d != NULL) ? d + 1 : "";
621 get_int (const char *file, const char *key, int def)
623 return GetPrivateProfileInt (app_text, key, def, file);
627 set_int (const char *file, const char *key, int value)
629 char buffer [BUF_TINY];
631 g_snprintf (buffer, sizeof (buffer), "%d", value);
632 return WritePrivateProfileString (app_text, key, buffer, file);
635 extern char *
636 get_config_string (const char *file, const char *key, const char *defval)
638 char buffer[1024];
639 (void)GetPrivateProfileString (app_text, key, defval, buffer, sizeof(buffer), file);
640 return mhl_str_dup (buffer);
643 extern void
644 set_config_string (const char *file, const char *key, const char *val)
646 (void)WritePrivateProfileString (app_text, key, val, file);
650 exist_file (const char *name)
652 return access (name, R_OK) == 0;
655 char *
656 load_file (const char *filename)
658 FILE *data_file;
659 struct stat s;
660 char *data;
661 long read_size;
663 if ((data_file = fopen (filename, "r")) == NULL){
664 return 0;
666 if (fstat (fileno (data_file), &s) != 0){
667 fclose (data_file);
668 return 0;
670 data = g_malloc (s.st_size+1);
671 read_size = fread (data, 1, s.st_size, data_file);
672 data [read_size] = 0;
673 fclose (data_file);
675 if (read_size > 0)
676 return data;
677 else {
678 g_free (data);
679 return 0;
683 char *
684 load_mc_home_file (const char *filename, char **allocated_filename)
686 char *hintfile_base, *hintfile;
687 char *lang;
688 char *data;
690 hintfile_base = concat_dir_and_file (mc_home, filename);
691 lang = guess_message_value ();
693 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
694 data = load_file (hintfile);
696 if (!data) {
697 g_free (hintfile);
698 /* Fall back to the two-letter language code */
699 if (lang[0] && lang[1])
700 lang[2] = 0;
701 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
702 data = load_file (hintfile);
704 if (!data) {
705 g_free (hintfile);
706 hintfile = hintfile_base;
707 data = load_file (hintfile_base);
711 g_free (lang);
713 if (hintfile != hintfile_base)
714 g_free (hintfile_base);
716 if (allocated_filename)
717 *allocated_filename = hintfile;
718 else
719 g_free (hintfile);
721 return data;
724 /* Check strftime() results. Some systems (i.e. Solaris) have different
725 short-month-name sizes for different locales */
726 size_t
727 i18n_checktimelength (void)
729 time_t testtime = time (NULL);
730 struct tm* lt = localtime(&testtime);
731 size_t length;
733 if (lt == NULL) {
734 // huh, localtime() doesnt seem to work ... falling back to "(invalid)"
735 length = strlen(INVALID_TIME_TEXT);
736 } else {
737 char buf [MAX_I18NTIMELENGTH + 1];
738 size_t a, b;
739 a = strftime (buf, sizeof(buf)-1, _("%b %e %H:%M"), lt);
740 b = strftime (buf, sizeof(buf)-1, _("%b %e %Y"), lt);
741 length = max (a, b);
744 /* Don't handle big differences. Use standard value (email bug, please) */
745 if ( length > MAX_I18NTIMELENGTH || length < MIN_I18NTIMELENGTH )
746 length = STD_I18NTIMELENGTH;
748 return length;
751 const char *
752 file_date (time_t when)
754 static char timebuf [MAX_I18NTIMELENGTH + 1];
755 time_t current_time = time ((time_t) 0);
756 static size_t i18n_timelength = 0;
757 static const char *fmtyear, *fmttime;
758 const char *fmt;
760 if (i18n_timelength == 0){
761 i18n_timelength = i18n_checktimelength() + 1;
763 /* strftime() format string for old dates */
764 fmtyear = _("%b %e %Y");
765 /* strftime() format string for recent dates */
766 fmttime = _("%b %e %H:%M");
769 if (current_time > when + 6L * 30L * 24L * 60L * 60L /* Old. */
770 || current_time < when - 60L * 60L) /* In the future. */
771 /* The file is fairly old or in the future.
772 POSIX says the cutoff is 6 months old;
773 approximate this by 6*30 days.
774 Allow a 1 hour slop factor for what is considered "the future",
775 to allow for NFS server/client clock disagreement.
776 Show the year instead of the time of day. */
778 fmt = fmtyear;
779 else
780 fmt = fmttime;
782 FMT_LOCALTIME(timebuf, i18n_timelength, fmt, when);
784 return timebuf;
787 const char *
788 extract_line (const char *s, const char *top)
790 static char tmp_line [BUF_MEDIUM];
791 char *t = tmp_line;
793 while (*s && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line)-1 && s < top)
794 *t++ = *s++;
795 *t = 0;
796 return tmp_line;
799 /* FIXME: I should write a faster version of this (Aho-Corasick stuff) */
800 const char *
801 _icase_search (const char *text, const char *data, int *lng)
803 const char *d = text;
804 const char *e = data;
805 int dlng = 0;
807 if (lng)
808 *lng = 0;
809 for (;*e; e++) {
810 while (*(e+1) == '\b' && *(e+2)) {
811 e += 2;
812 dlng += 2;
814 if (toupper((unsigned char) *d) == toupper((unsigned char) *e))
815 d++;
816 else {
817 e -= d - text;
818 d = text;
819 dlng = 0;
821 if (!*d) {
822 if (lng)
823 *lng = strlen (text) + dlng;
824 return e+1;
827 return 0;
830 /* The basename routine */
831 const char *
832 x_basename (const char *s)
834 const char *where;
835 return ((where = strrchr (s, PATH_SEP))) ? where + 1 : s;
839 const char *
840 unix_error_string (int error_num)
842 static char buffer [BUF_LARGE];
843 #if GLIB_MAJOR_VERSION >= 2
844 gchar *strerror_currentlocale;
846 strerror_currentlocale = g_locale_from_utf8(g_strerror (error_num), -1, NULL, NULL, NULL);
847 g_snprintf (buffer, sizeof (buffer), "%s (%d)",
848 strerror_currentlocale, error_num);
849 g_free(strerror_currentlocale);
850 #else
851 g_snprintf (buffer, sizeof (buffer), "%s (%d)",
852 g_strerror (error_num), error_num);
853 #endif
854 return buffer;
857 const char *
858 skip_separators (const char *s)
860 for (;*s; s++)
861 if (*s != ' ' && *s != '\t' && *s != ',')
862 break;
863 return s;
866 const char *
867 skip_numbers (const char *s)
869 for (;*s; s++)
870 if (!isdigit ((unsigned char) *s))
871 break;
872 return s;
875 /* Remove all control sequences from the argument string. We define
876 * "control sequence", in a sort of pidgin BNF, as follows:
878 * control-seq = Esc non-'['
879 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
881 * This scheme works for all the terminals described in my termcap /
882 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
883 * terminals. If I hear from a single person who uses such a terminal
884 * with MC, I'll be glad to add support for it. (Dugan)
885 * Non-printable characters are also removed.
888 char *
889 strip_ctrl_codes (char *s)
891 char *w; /* Current position where the stripped data is written */
892 char *r; /* Current position where the original data is read */
894 if (!s)
895 return 0;
897 for (w = s, r = s; *r; ) {
898 if (*r == ESC_CHAR) {
899 /* Skip the control sequence's arguments */ ;
900 if (*(++r) == '[') {
901 /* strchr() matches trailing binary 0 */
902 while (*(++r) && strchr ("0123456789;?", *r));
906 * Now we are at the last character of the sequence.
907 * Skip it unless it's binary 0.
909 if (*r)
910 r++;
911 continue;
914 if (is_printable(*r))
915 *w++ = *r;
916 ++r;
918 *w = 0;
919 return s;
923 #ifndef USE_VFS
924 char *
925 get_current_wd (char *buffer, int size)
927 char *p;
928 int len;
930 p = g_get_current_dir ();
931 len = strlen(p) + 1;
933 if (len > size) {
934 g_free (p);
935 return NULL;
938 memcpy (buffer, p, len);
939 g_free (p);
941 return buffer;
943 #endif /* !USE_VFS */
945 enum compression_type
946 get_compression_type (int fd)
948 unsigned char magic[4];
950 /* Read the magic signature */
951 if (mc_read (fd, (char *) magic, 4) != 4)
952 return COMPRESSION_NONE;
954 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
955 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236)) {
956 return COMPRESSION_GZIP;
959 /* PKZIP_MAGIC */
960 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003
961 && magic[3] == 004) {
962 /* Read compression type */
963 mc_lseek (fd, 8, SEEK_SET);
964 if (mc_read (fd, (char *) magic, 2) != 2)
965 return COMPRESSION_NONE;
967 /* Gzip can handle only deflated (8) or stored (0) files */
968 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
969 return COMPRESSION_NONE;
971 /* Compatible with gzip */
972 return COMPRESSION_GZIP;
975 /* PACK_MAGIC and LZH_MAGIC and compress magic */
976 if (magic[0] == 037
977 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235)) {
978 /* Compatible with gzip */
979 return COMPRESSION_GZIP;
982 /* BZIP and BZIP2 files */
983 if ((magic[0] == 'B') && (magic[1] == 'Z') &&
984 (magic[3] >= '1') && (magic[3] <= '9')) {
985 switch (magic[2]) {
986 case '0':
987 return COMPRESSION_BZIP;
988 case 'h':
989 return COMPRESSION_BZIP2;
992 return 0;
995 const char *
996 decompress_extension (int type)
998 switch (type){
999 case COMPRESSION_GZIP: return "#ugz";
1000 case COMPRESSION_BZIP: return "#ubz";
1001 case COMPRESSION_BZIP2: return "#ubz2";
1003 /* Should never reach this place */
1004 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
1005 return 0;
1008 /* Hooks */
1009 void
1010 add_hook (Hook **hook_list, void (*hook_fn)(void *), void *data)
1012 Hook *new_hook = g_new (Hook, 1);
1014 new_hook->hook_fn = hook_fn;
1015 new_hook->next = *hook_list;
1016 new_hook->hook_data = data;
1018 *hook_list = new_hook;
1021 void
1022 execute_hooks (Hook *hook_list)
1024 Hook *new_hook = 0;
1025 Hook *p;
1027 /* We copy the hook list first so tahat we let the hook
1028 * function call delete_hook
1031 while (hook_list){
1032 add_hook (&new_hook, hook_list->hook_fn, hook_list->hook_data);
1033 hook_list = hook_list->next;
1035 p = new_hook;
1037 while (new_hook){
1038 (*new_hook->hook_fn)(new_hook->hook_data);
1039 new_hook = new_hook->next;
1042 for (hook_list = p; hook_list;){
1043 p = hook_list;
1044 hook_list = hook_list->next;
1045 g_free (p);
1049 void
1050 delete_hook (Hook **hook_list, void (*hook_fn)(void *))
1052 Hook *current, *new_list, *next;
1054 new_list = 0;
1056 for (current = *hook_list; current; current = next){
1057 next = current->next;
1058 if (current->hook_fn == hook_fn)
1059 g_free (current);
1060 else
1061 add_hook (&new_list, current->hook_fn, current->hook_data);
1063 *hook_list = new_list;
1067 hook_present (Hook *hook_list, void (*hook_fn)(void *))
1069 Hook *p;
1071 for (p = hook_list; p; p = p->next)
1072 if (p->hook_fn == hook_fn)
1073 return 1;
1074 return 0;
1077 void
1078 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 *
1092 convert_controls (const char *p)
1094 char *valcopy = mhl_str_dup (p);
1095 char *q;
1097 /* Parse the escape special character */
1098 for (q = valcopy; *p;){
1099 if (*p == '\\'){
1100 p++;
1101 if ((*p == 'e') || (*p == 'E')){
1102 p++;
1103 *q++ = ESC_CHAR;
1105 } else {
1106 if (*p == '^'){
1107 p++;
1108 if (*p == '^')
1109 *q++ = *p++;
1110 else {
1111 char c = (*p | 0x20);
1112 if (c >= 'a' && c <= 'z') {
1113 *q++ = c - 'a' + 1;
1114 p++;
1115 } else if (*p)
1116 p++;
1118 } else
1119 *q++ = *p++;
1122 *q = 0;
1123 return valcopy;
1126 static char *
1127 resolve_symlinks (const char *path)
1129 char *buf, *buf2, *q, *r, c;
1130 int len;
1131 struct stat mybuf;
1132 const char *p;
1134 if (*path != PATH_SEP)
1135 return NULL;
1136 r = buf = g_malloc (MC_MAXPATHLEN);
1137 buf2 = g_malloc (MC_MAXPATHLEN);
1138 *r++ = PATH_SEP;
1139 *r = 0;
1140 p = path;
1141 for (;;) {
1142 q = strchr (p + 1, PATH_SEP);
1143 if (!q) {
1144 q = strchr (p + 1, 0);
1145 if (q == p + 1)
1146 break;
1148 c = *q;
1149 *q = 0;
1150 if (mc_lstat (path, &mybuf) < 0) {
1151 g_free (buf);
1152 g_free (buf2);
1153 *q = c;
1154 return NULL;
1156 if (!S_ISLNK (mybuf.st_mode))
1157 strcpy (r, p + 1);
1158 else {
1159 len = mc_readlink (path, buf2, MC_MAXPATHLEN - 1);
1160 if (len < 0) {
1161 g_free (buf);
1162 g_free (buf2);
1163 *q = c;
1164 return NULL;
1166 buf2 [len] = 0;
1167 if (*buf2 == PATH_SEP)
1168 strcpy (buf, buf2);
1169 else
1170 strcpy (r, buf2);
1172 canonicalize_pathname (buf);
1173 r = strchr (buf, 0);
1174 if (!*r || *(r - 1) != PATH_SEP) {
1175 *r++ = PATH_SEP;
1176 *r = 0;
1178 *q = c;
1179 p = q;
1180 if (!c)
1181 break;
1183 if (!*buf)
1184 strcpy (buf, PATH_SEP_STR);
1185 else if (*(r - 1) == PATH_SEP && r != buf + 1)
1186 *(r - 1) = 0;
1187 g_free (buf2);
1188 return buf;
1191 /* Finds out a relative path from first to second, i.e. goes as many ..
1192 * as needed up in first and then goes down using second */
1193 char *
1194 diff_two_paths (const char *first, const char *second)
1196 char *p, *q, *r, *s, *buf = NULL;
1197 int i, j, prevlen = -1, currlen;
1198 char *my_first = NULL, *my_second = NULL;
1200 my_first = resolve_symlinks (first);
1201 if (my_first == NULL)
1202 return NULL;
1203 my_second = resolve_symlinks (second);
1204 if (my_second == NULL) {
1205 g_free (my_first);
1206 return NULL;
1208 for (j = 0; j < 2; j++) {
1209 p = my_first;
1210 q = my_second;
1211 for (;;) {
1212 r = strchr (p, PATH_SEP);
1213 s = strchr (q, PATH_SEP);
1214 if (!r || !s)
1215 break;
1216 *r = 0; *s = 0;
1217 if (strcmp (p, q)) {
1218 *r = PATH_SEP; *s = PATH_SEP;
1219 break;
1220 } else {
1221 *r = PATH_SEP; *s = PATH_SEP;
1223 p = r + 1;
1224 q = s + 1;
1226 p--;
1227 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++);
1228 currlen = (i + 1) * 3 + strlen (q) + 1;
1229 if (j) {
1230 if (currlen < prevlen)
1231 g_free (buf);
1232 else {
1233 g_free (my_first);
1234 g_free (my_second);
1235 return buf;
1238 p = buf = g_malloc (currlen);
1239 prevlen = currlen;
1240 for (; i >= 0; i--, p += 3)
1241 strcpy (p, "../");
1242 strcpy (p, q);
1244 g_free (my_first);
1245 g_free (my_second);
1246 return buf;
1249 /* If filename is NULL, then we just append PATH_SEP to the dir */
1250 char *
1251 concat_dir_and_file (const char *dir, const char *file)
1253 int i = strlen (dir);
1255 if (dir [i-1] == PATH_SEP)
1256 return g_strconcat (dir, file, (char *) NULL);
1257 else
1258 return g_strconcat (dir, PATH_SEP_STR, file, (char *) NULL);
1261 /* Append text to GList, remove all entries with the same text */
1262 GList *
1263 list_append_unique (GList *list, char *text)
1265 GList *link, *newlink;
1268 * Go to the last position and traverse the list backwards
1269 * starting from the second last entry to make sure that we
1270 * are not removing the current link.
1272 list = g_list_append (list, text);
1273 list = g_list_last (list);
1274 link = g_list_previous (list);
1276 while (link) {
1277 newlink = g_list_previous (link);
1278 if (!strcmp ((char *) link->data, text)) {
1279 g_free (link->data);
1280 g_list_remove_link (list, link);
1281 g_list_free_1 (link);
1283 link = newlink;
1286 return list;
1289 /* Following code heavily borrows from libiberty, mkstemps.c */
1291 /* Number of attempts to create a temporary file */
1292 #ifndef TMP_MAX
1293 #define TMP_MAX 16384
1294 #endif /* !TMP_MAX */
1297 * Arguments:
1298 * pname (output) - pointer to the name of the temp file (needs g_free).
1299 * NULL if the function fails.
1300 * prefix - part of the filename before the random part.
1301 * Prepend $TMPDIR or /tmp if there are no path separators.
1302 * suffix - if not NULL, part of the filename after the random part.
1304 * Result:
1305 * handle of the open file or -1 if couldn't open any.
1308 mc_mkstemps (char **pname, const char *prefix, const char *suffix)
1310 static const char letters[]
1311 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1312 static unsigned long value;
1313 struct timeval tv;
1314 char *tmpbase;
1315 char *tmpname;
1316 char *XXXXXX;
1317 int count;
1319 if (strchr (prefix, PATH_SEP) == NULL) {
1320 /* Add prefix first to find the position of XXXXXX */
1321 tmpbase = concat_dir_and_file (mc_tmpdir (), prefix);
1322 } else {
1323 tmpbase = mhl_str_dup (prefix);
1326 tmpname = g_strconcat (tmpbase, "XXXXXX", suffix, (char *) NULL);
1327 *pname = tmpname;
1328 XXXXXX = &tmpname[strlen (tmpbase)];
1329 g_free (tmpbase);
1331 /* Get some more or less random data. */
1332 gettimeofday (&tv, NULL);
1333 value += (tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
1335 for (count = 0; count < TMP_MAX; ++count) {
1336 unsigned long v = value;
1337 int fd;
1339 /* Fill in the random bits. */
1340 XXXXXX[0] = letters[v % 62];
1341 v /= 62;
1342 XXXXXX[1] = letters[v % 62];
1343 v /= 62;
1344 XXXXXX[2] = letters[v % 62];
1345 v /= 62;
1346 XXXXXX[3] = letters[v % 62];
1347 v /= 62;
1348 XXXXXX[4] = letters[v % 62];
1349 v /= 62;
1350 XXXXXX[5] = letters[v % 62];
1352 fd = open (tmpname, O_RDWR | O_CREAT | O_TRUNC | O_EXCL,
1353 S_IRUSR | S_IWUSR);
1354 if (fd >= 0) {
1355 /* Successfully created. */
1356 return fd;
1359 /* This is a random value. It is only necessary that the next
1360 TMP_MAX values generated by adding 7777 to VALUE are different
1361 with (module 2^32). */
1362 value += 7777;
1365 /* Unsuccessful. Free the filename. */
1366 g_free (tmpname);
1367 *pname = NULL;
1369 return -1;
1373 * Read and restore position for the given filename.
1374 * If there is no stored data, return line 1 and col 0.
1376 void
1377 load_file_position (const char *filename, long *line, long *column)
1379 char *fn;
1380 FILE *f;
1381 char buf[MC_MAXPATHLEN + 20];
1382 int len;
1384 /* defaults */
1385 *line = 1;
1386 *column = 0;
1388 /* open file with positions */
1389 fn = concat_dir_and_file (home_dir, MC_FILEPOS);
1390 f = fopen (fn, "r");
1391 g_free (fn);
1392 if (!f)
1393 return;
1395 len = strlen (filename);
1397 while (fgets (buf, sizeof (buf), f)) {
1398 const char *p;
1400 /* check if the filename matches the beginning of string */
1401 if (strncmp (buf, filename, len) != 0)
1402 continue;
1404 /* followed by single space */
1405 if (buf[len] != ' ')
1406 continue;
1408 /* and string without spaces */
1409 p = &buf[len + 1];
1410 if (strchr (p, ' '))
1411 continue;
1413 *line = strtol(p, const_cast(char **, &p), 10);
1414 if (*p == ';') {
1415 *column = strtol(p+1, const_cast(char **, &p), 10);
1416 if (*p != '\n')
1417 *column = 0;
1418 } else
1419 *line = 1;
1421 fclose (f);
1424 /* Save position for the given file */
1425 void
1426 save_file_position (const char *filename, long line, long column)
1428 char *tmp, *fn;
1429 FILE *f, *t;
1430 char buf[MC_MAXPATHLEN + 20];
1431 int i = 1;
1432 int len;
1434 len = strlen (filename);
1436 tmp = concat_dir_and_file (home_dir, MC_FILEPOS_TMP);
1437 fn = concat_dir_and_file (home_dir, MC_FILEPOS);
1439 /* open temporary file */
1440 t = fopen (tmp, "w");
1441 if (!t) {
1442 g_free (tmp);
1443 g_free (fn);
1444 return;
1447 /* put the new record */
1448 if (line != 1 || column != 0) {
1449 fprintf (t, "%s %ld;%ld\n", filename, line, column);
1452 /* copy records from the old file */
1453 f = fopen (fn, "r");
1454 if (f) {
1455 while (fgets (buf, sizeof (buf), f)) {
1456 /* Skip entries for the current filename */
1457 if (strncmp (buf, filename, len) == 0 && buf[len] == ' '
1458 && !strchr (&buf[len + 1], ' '))
1459 continue;
1461 fprintf (t, "%s", buf);
1462 if (++i > MC_FILEPOS_ENTRIES)
1463 break;
1465 fclose (f);
1468 fclose (t);
1469 rename (tmp, fn);
1470 g_free (tmp);
1471 g_free (fn);
1474 extern const char *
1475 cstrcasestr (const char *haystack, const char *needle)
1477 const char *hptr;
1478 size_t i, needle_len;
1480 needle_len = strlen (needle);
1481 for (hptr = haystack; *hptr != '\0'; hptr++) {
1482 for (i = 0; i < needle_len; i++) {
1483 if (toupper ((unsigned char) hptr[i]) !=
1484 toupper ((unsigned char) needle[i]))
1485 goto next_try;
1487 return hptr;
1488 next_try:
1489 (void) 0;
1491 return NULL;
1494 const char *
1495 cstrstr (const char *haystack, const char *needle)
1497 return strstr(haystack, needle);
1500 extern char *
1501 str_unconst (const char *s)
1503 return (char *) s;
1506 #define ASCII_A (0x40 + 1)
1507 #define ASCII_Z (0x40 + 26)
1508 #define ASCII_a (0x60 + 1)
1509 #define ASCII_z (0x60 + 26)
1511 extern int
1512 ascii_alpha_to_cntrl (int ch)
1514 if ((ch >= ASCII_A && ch <= ASCII_Z)
1515 || (ch >= ASCII_a && ch <= ASCII_z)) {
1516 ch &= 0x1f;
1518 return ch;
1521 const char *
1522 Q_ (const char *s)
1524 const char *result, *sep;
1526 result = _(s);
1527 sep = strchr(result, '|');
1528 return (sep != NULL) ? sep + 1 : result;