Removed TODO Marker in util.c as the functions are fully ported to use glib now
[midnight-commander.git] / src / util.c
blobcee81e8794e9cd6887f8ddec9936d74d9b2792c6
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[4];
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;
995 return 0;
998 const char *
999 decompress_extension (int type)
1001 switch (type){
1002 case COMPRESSION_GZIP: return "#ugz";
1003 case COMPRESSION_BZIP: return "#ubz";
1004 case COMPRESSION_BZIP2: return "#ubz2";
1006 /* Should never reach this place */
1007 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
1008 return 0;
1011 /* Hooks */
1012 void
1013 add_hook (Hook **hook_list, void (*hook_fn)(void *), void *data)
1015 Hook *new_hook = g_new (Hook, 1);
1017 new_hook->hook_fn = hook_fn;
1018 new_hook->next = *hook_list;
1019 new_hook->hook_data = data;
1021 *hook_list = new_hook;
1024 void
1025 execute_hooks (Hook *hook_list)
1027 Hook *new_hook = 0;
1028 Hook *p;
1030 /* We copy the hook list first so tahat we let the hook
1031 * function call delete_hook
1034 while (hook_list){
1035 add_hook (&new_hook, hook_list->hook_fn, hook_list->hook_data);
1036 hook_list = hook_list->next;
1038 p = new_hook;
1040 while (new_hook){
1041 (*new_hook->hook_fn)(new_hook->hook_data);
1042 new_hook = new_hook->next;
1045 for (hook_list = p; hook_list;){
1046 p = hook_list;
1047 hook_list = hook_list->next;
1048 g_free (p);
1052 void
1053 delete_hook (Hook **hook_list, void (*hook_fn)(void *))
1055 Hook *current, *new_list, *next;
1057 new_list = 0;
1059 for (current = *hook_list; current; current = next){
1060 next = current->next;
1061 if (current->hook_fn == hook_fn)
1062 g_free (current);
1063 else
1064 add_hook (&new_list, current->hook_fn, current->hook_data);
1066 *hook_list = new_list;
1070 hook_present (Hook *hook_list, void (*hook_fn)(void *))
1072 Hook *p;
1074 for (p = hook_list; p; p = p->next)
1075 if (p->hook_fn == hook_fn)
1076 return 1;
1077 return 0;
1080 void
1081 wipe_password (char *passwd)
1083 char *p = passwd;
1085 if (!p)
1086 return;
1087 for (;*p ; p++)
1088 *p = 0;
1089 g_free (passwd);
1092 /* Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key */
1093 /* Returns a newly allocated string */
1094 char *
1095 convert_controls (const char *p)
1097 char *valcopy = g_strdup (p);
1098 char *q;
1100 /* Parse the escape special character */
1101 for (q = valcopy; *p;){
1102 if (*p == '\\'){
1103 p++;
1104 if ((*p == 'e') || (*p == 'E')){
1105 p++;
1106 *q++ = ESC_CHAR;
1108 } else {
1109 if (*p == '^'){
1110 p++;
1111 if (*p == '^')
1112 *q++ = *p++;
1113 else {
1114 char c = (*p | 0x20);
1115 if (c >= 'a' && c <= 'z') {
1116 *q++ = c - 'a' + 1;
1117 p++;
1118 } else if (*p)
1119 p++;
1121 } else
1122 *q++ = *p++;
1125 *q = 0;
1126 return valcopy;
1129 static char *
1130 resolve_symlinks (const char *path)
1132 char *buf, *buf2, *q, *r, c;
1133 int len;
1134 struct stat mybuf;
1135 const char *p;
1137 if (*path != PATH_SEP)
1138 return NULL;
1139 r = buf = g_malloc (MC_MAXPATHLEN);
1140 buf2 = g_malloc (MC_MAXPATHLEN);
1141 *r++ = PATH_SEP;
1142 *r = 0;
1143 p = path;
1144 for (;;) {
1145 q = strchr (p + 1, PATH_SEP);
1146 if (!q) {
1147 q = strchr (p + 1, 0);
1148 if (q == p + 1)
1149 break;
1151 c = *q;
1152 *q = 0;
1153 if (mc_lstat (path, &mybuf) < 0) {
1154 g_free (buf);
1155 g_free (buf2);
1156 *q = c;
1157 return NULL;
1159 if (!S_ISLNK (mybuf.st_mode))
1160 strcpy (r, p + 1);
1161 else {
1162 len = mc_readlink (path, buf2, MC_MAXPATHLEN - 1);
1163 if (len < 0) {
1164 g_free (buf);
1165 g_free (buf2);
1166 *q = c;
1167 return NULL;
1169 buf2 [len] = 0;
1170 if (*buf2 == PATH_SEP)
1171 strcpy (buf, buf2);
1172 else
1173 strcpy (r, buf2);
1175 canonicalize_pathname (buf);
1176 r = strchr (buf, 0);
1177 if (!*r || *(r - 1) != PATH_SEP) {
1178 *r++ = PATH_SEP;
1179 *r = 0;
1181 *q = c;
1182 p = q;
1183 if (!c)
1184 break;
1186 if (!*buf)
1187 strcpy (buf, PATH_SEP_STR);
1188 else if (*(r - 1) == PATH_SEP && r != buf + 1)
1189 *(r - 1) = 0;
1190 g_free (buf2);
1191 return buf;
1194 /* Finds out a relative path from first to second, i.e. goes as many ..
1195 * as needed up in first and then goes down using second */
1196 char *
1197 diff_two_paths (const char *first, const char *second)
1199 char *p, *q, *r, *s, *buf = NULL;
1200 int i, j, prevlen = -1, currlen;
1201 char *my_first = NULL, *my_second = NULL;
1203 my_first = resolve_symlinks (first);
1204 if (my_first == NULL)
1205 return NULL;
1206 my_second = resolve_symlinks (second);
1207 if (my_second == NULL) {
1208 g_free (my_first);
1209 return NULL;
1211 for (j = 0; j < 2; j++) {
1212 p = my_first;
1213 q = my_second;
1214 for (;;) {
1215 r = strchr (p, PATH_SEP);
1216 s = strchr (q, PATH_SEP);
1217 if (!r || !s)
1218 break;
1219 *r = 0; *s = 0;
1220 if (strcmp (p, q)) {
1221 *r = PATH_SEP; *s = PATH_SEP;
1222 break;
1223 } else {
1224 *r = PATH_SEP; *s = PATH_SEP;
1226 p = r + 1;
1227 q = s + 1;
1229 p--;
1230 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++);
1231 currlen = (i + 1) * 3 + strlen (q) + 1;
1232 if (j) {
1233 if (currlen < prevlen)
1234 g_free (buf);
1235 else {
1236 g_free (my_first);
1237 g_free (my_second);
1238 return buf;
1241 p = buf = g_malloc (currlen);
1242 prevlen = currlen;
1243 for (; i >= 0; i--, p += 3)
1244 strcpy (p, "../");
1245 strcpy (p, q);
1247 g_free (my_first);
1248 g_free (my_second);
1249 return buf;
1252 /* If filename is NULL, then we just append PATH_SEP to the dir */
1253 char *
1254 concat_dir_and_file (const char *dir, const char *file)
1256 int i = strlen (dir);
1258 if (dir [i-1] == PATH_SEP)
1259 return g_strconcat (dir, file, (char *) NULL);
1260 else
1261 return g_strconcat (dir, PATH_SEP_STR, file, (char *) NULL);
1264 /* Append text to GList, remove all entries with the same text */
1265 GList *
1266 list_append_unique (GList *list, char *text)
1268 GList *link, *newlink;
1271 * Go to the last position and traverse the list backwards
1272 * starting from the second last entry to make sure that we
1273 * are not removing the current link.
1275 list = g_list_append (list, text);
1276 list = g_list_last (list);
1277 link = g_list_previous (list);
1279 while (link) {
1280 newlink = g_list_previous (link);
1281 if (!strcmp ((char *) link->data, text)) {
1282 g_free (link->data);
1283 g_list_remove_link (list, link);
1284 g_list_free_1 (link);
1286 link = newlink;
1289 return list;
1292 /* Following code heavily borrows from libiberty, mkstemps.c */
1294 /* Number of attempts to create a temporary file */
1295 #ifndef TMP_MAX
1296 #define TMP_MAX 16384
1297 #endif /* !TMP_MAX */
1300 * Arguments:
1301 * pname (output) - pointer to the name of the temp file (needs g_free).
1302 * NULL if the function fails.
1303 * prefix - part of the filename before the random part.
1304 * Prepend $TMPDIR or /tmp if there are no path separators.
1305 * suffix - if not NULL, part of the filename after the random part.
1307 * Result:
1308 * handle of the open file or -1 if couldn't open any.
1311 mc_mkstemps (char **pname, const char *prefix, const char *suffix)
1313 static const char letters[]
1314 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1315 static unsigned long value;
1316 struct timeval tv;
1317 char *tmpbase;
1318 char *tmpname;
1319 char *XXXXXX;
1320 int count;
1322 if (strchr (prefix, PATH_SEP) == NULL) {
1323 /* Add prefix first to find the position of XXXXXX */
1324 tmpbase = concat_dir_and_file (mc_tmpdir (), prefix);
1325 } else {
1326 tmpbase = g_strdup (prefix);
1329 tmpname = g_strconcat (tmpbase, "XXXXXX", suffix, (char *) NULL);
1330 *pname = tmpname;
1331 XXXXXX = &tmpname[strlen (tmpbase)];
1332 g_free (tmpbase);
1334 /* Get some more or less random data. */
1335 gettimeofday (&tv, NULL);
1336 value += (tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
1338 for (count = 0; count < TMP_MAX; ++count) {
1339 unsigned long v = value;
1340 int fd;
1342 /* Fill in the random bits. */
1343 XXXXXX[0] = letters[v % 62];
1344 v /= 62;
1345 XXXXXX[1] = letters[v % 62];
1346 v /= 62;
1347 XXXXXX[2] = letters[v % 62];
1348 v /= 62;
1349 XXXXXX[3] = letters[v % 62];
1350 v /= 62;
1351 XXXXXX[4] = letters[v % 62];
1352 v /= 62;
1353 XXXXXX[5] = letters[v % 62];
1355 fd = open (tmpname, O_RDWR | O_CREAT | O_TRUNC | O_EXCL,
1356 S_IRUSR | S_IWUSR);
1357 if (fd >= 0) {
1358 /* Successfully created. */
1359 return fd;
1362 /* This is a random value. It is only necessary that the next
1363 TMP_MAX values generated by adding 7777 to VALUE are different
1364 with (module 2^32). */
1365 value += 7777;
1368 /* Unsuccessful. Free the filename. */
1369 g_free (tmpname);
1370 *pname = NULL;
1372 return -1;
1376 * Read and restore position for the given filename.
1377 * If there is no stored data, return line 1 and col 0.
1379 void
1380 load_file_position (const char *filename, long *line, long *column)
1382 char *fn;
1383 FILE *f;
1384 char buf[MC_MAXPATHLEN + 20];
1385 int len;
1387 /* defaults */
1388 *line = 1;
1389 *column = 0;
1391 /* open file with positions */
1392 fn = concat_dir_and_file (home_dir, MC_FILEPOS);
1393 f = fopen (fn, "r");
1394 g_free (fn);
1395 if (!f)
1396 return;
1398 len = strlen (filename);
1400 while (fgets (buf, sizeof (buf), f)) {
1401 const char *p;
1403 /* check if the filename matches the beginning of string */
1404 if (strncmp (buf, filename, len) != 0)
1405 continue;
1407 /* followed by single space */
1408 if (buf[len] != ' ')
1409 continue;
1411 /* and string without spaces */
1412 p = &buf[len + 1];
1413 if (strchr (p, ' '))
1414 continue;
1416 *line = strtol(p, const_cast(char **, &p), 10);
1417 if (*p == ';') {
1418 *column = strtol(p+1, const_cast(char **, &p), 10);
1419 if (*p != '\n')
1420 *column = 0;
1421 } else
1422 *line = 1;
1424 fclose (f);
1427 /* Save position for the given file */
1428 void
1429 save_file_position (const char *filename, long line, long column)
1431 char *tmp, *fn;
1432 FILE *f, *t;
1433 char buf[MC_MAXPATHLEN + 20];
1434 int i = 1;
1435 int len;
1437 len = strlen (filename);
1439 tmp = concat_dir_and_file (home_dir, MC_FILEPOS_TMP);
1440 fn = concat_dir_and_file (home_dir, MC_FILEPOS);
1442 /* open temporary file */
1443 t = fopen (tmp, "w");
1444 if (!t) {
1445 g_free (tmp);
1446 g_free (fn);
1447 return;
1450 /* put the new record */
1451 if (line != 1 || column != 0) {
1452 fprintf (t, "%s %ld;%ld\n", filename, line, column);
1455 /* copy records from the old file */
1456 f = fopen (fn, "r");
1457 if (f) {
1458 while (fgets (buf, sizeof (buf), f)) {
1459 /* Skip entries for the current filename */
1460 if (strncmp (buf, filename, len) == 0 && buf[len] == ' '
1461 && !strchr (&buf[len + 1], ' '))
1462 continue;
1464 fprintf (t, "%s", buf);
1465 if (++i > MC_FILEPOS_ENTRIES)
1466 break;
1468 fclose (f);
1471 fclose (t);
1472 rename (tmp, fn);
1473 g_free (tmp);
1474 g_free (fn);
1477 extern const char *
1478 cstrcasestr (const char *haystack, const char *needle)
1480 const char *hptr;
1481 size_t i, needle_len;
1483 needle_len = strlen (needle);
1484 for (hptr = haystack; *hptr != '\0'; hptr++) {
1485 for (i = 0; i < needle_len; i++) {
1486 if (toupper ((unsigned char) hptr[i]) !=
1487 toupper ((unsigned char) needle[i]))
1488 goto next_try;
1490 return hptr;
1491 next_try:
1492 (void) 0;
1494 return NULL;
1497 const char *
1498 cstrstr (const char *haystack, const char *needle)
1500 return strstr(haystack, needle);
1503 extern char *
1504 str_unconst (const char *s)
1506 return (char *) s;
1509 #define ASCII_A (0x40 + 1)
1510 #define ASCII_Z (0x40 + 26)
1511 #define ASCII_a (0x60 + 1)
1512 #define ASCII_z (0x60 + 26)
1514 extern int
1515 ascii_alpha_to_cntrl (int ch)
1517 if ((ch >= ASCII_A && ch <= ASCII_Z)
1518 || (ch >= ASCII_a && ch <= ASCII_z)) {
1519 ch &= 0x1f;
1521 return ch;
1524 const char *
1525 Q_ (const char *s)
1527 const char *result, *sep;
1529 result = _(s);
1530 sep = strchr(result, '|');
1531 return (sep != NULL) ? sep + 1 : result;
1534 #define shell_escape_toesc(x) \
1535 (((x)==' ')||((x)=='!')||((x)=='#')||((x)=='$')||((x)=='%')|| \
1536 ((x)=='(')||((x)==')')||((x)=='\'')||((x)=='&')||((x)=='~')|| \
1537 ((x)=='{')||((x)=='}')||((x)=='[')||((x)==']')||((x)=='`')|| \
1538 ((x)=='?')||((x)=='|')||((x)=='<')||((x)=='>')||((x)==';')|| \
1539 ((x)=='*')||((x)=='\\')||((x)=='"'))
1541 #define shell_escape_nottoesc(x) \
1542 (((x)!=0) && (!shell_escape_toesc((x))))
1544 /** To be compatible with the general posix command lines we have to escape
1545 strings for the command line
1547 \params in
1548 string for escaping
1550 \returns
1551 return escaped string (which needs to be freed later)
1553 char*
1554 shell_escape(const char* src)
1556 GString *str;
1557 char *result = NULL;
1559 if ((src==NULL)||(!(*src)))
1560 return strdup("");
1562 str = g_string_new("");
1564 /* look for the first char to escape */
1565 while (1)
1567 char c;
1568 /* copy over all chars not to escape */
1569 while ((c=(*src)) && shell_escape_nottoesc(c))
1571 g_string_append_c(str,c);
1572 src++;
1575 /* at this point we either have an \0 or an char to escape */
1576 if (!c) {
1577 result = str->str;
1578 g_string_free(str,FALSE);
1579 return result;
1582 g_string_append_c(str,'\\');
1583 g_string_append_c(str,c);
1584 src++;
1588 /** Unescape paths or other strings for e.g the internal cd
1589 shell-unescape within a given buffer (writing to it!)
1591 \params src
1592 string for unescaping
1594 \returns
1595 return unescaped string (which needs to be freed)
1597 char*
1598 shell_unescape(const char* text)
1600 GString *str;
1601 char *result = NULL;
1603 if (!text)
1604 return NULL;
1607 /* look for the first \ - that's quick skipover if there's nothing to escape */
1608 const char* readptr = text;
1609 while ((*readptr) && ((*readptr)!='\\')) readptr++;
1610 if (!(*readptr)) {
1611 result = g_strdup(text);
1612 return result;
1614 str = g_string_new("");
1616 /* if we're here, we're standing on the first '\' */
1617 char c;
1618 while ((c = *readptr))
1620 if (c=='\\')
1622 readptr++;
1623 switch ((c = *readptr))
1625 case 'n': g_string_append_c(str,'\n'); break;
1626 case 'r': g_string_append_c(str,'\r'); break;
1627 case 't': g_string_append_c(str,'\t'); break;
1629 case ' ':
1630 case '\\':
1631 case '#':
1632 case '$':
1633 case '%':
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 '\0': /* end of string! malformed escape string */
1650 goto out;
1651 default:
1652 g_string_append_c(str,c); break;
1655 else /* got a normal character */
1657 g_string_append_c(str,c);
1659 readptr++;
1661 out:
1662 g_string_append_c(str,'\0');
1664 result = str->str;
1665 g_string_free(str,FALSE);
1666 return result;
1669 /** Check if char in pointer contain escape'd chars
1671 \params in
1672 string for checking
1674 \returns
1675 return TRUE if string contain escaped chars
1676 otherwise return FALSE
1678 gboolean
1679 shell_is_char_escaped ( const char *in )
1681 if (in == NULL || !*in || in[0] != '\\')
1682 return FALSE;
1683 if (shell_escape_toesc(in[1]))
1684 return TRUE;
1685 return FALSE;