Some fixups with wrongly highlighted words
[midnight-commander.git] / src / util.c
blobcb3068ab16060de269c3a4abee59eb7d0a4385b5
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 /** \file
26 * \brief Source: various utilities
29 #include <config.h>
31 #include <ctype.h>
32 #include <limits.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <unistd.h>
41 #include "global.h"
42 #include "profile.h"
43 #include "main.h" /* mc_home */
44 #include "cmd.h" /* guess_message_value */
45 #include "mountlist.h"
46 #include "win.h" /* xterm_flag */
47 #include "timefmt.h"
49 #ifdef HAVE_CHARSET
50 #include "charsets.h"
51 #endif
53 static const char app_text [] = "Midnight-Commander";
54 int easy_patterns = 1;
56 extern void str_replace(char *s, char from, char to)
58 for (; *s != '\0'; s++) {
59 if (*s == from)
60 *s = to;
64 static inline int
65 is_7bit_printable (unsigned char c)
67 return (c > 31 && c < 127);
70 static inline int
71 is_iso_printable (unsigned char c)
73 return ((c > 31 && c < 127) || c >= 160);
76 static inline int
77 is_8bit_printable (unsigned char c)
79 /* "Full 8 bits output" doesn't work on xterm */
80 if (xterm_flag)
81 return is_iso_printable (c);
83 return (c > 31 && c != 127 && c != 155);
86 int
87 is_printable (int c)
89 c &= 0xff;
91 #ifdef HAVE_CHARSET
92 /* "Display bits" is ignored, since the user controls the output
93 by setting the output codepage */
94 return is_8bit_printable (c);
95 #else
96 if (!eight_bit_clean)
97 return is_7bit_printable (c);
99 if (full_eight_bits) {
100 return is_8bit_printable (c);
101 } else
102 return is_iso_printable (c);
103 #endif /* !HAVE_CHARSET */
106 /* Calculates the message dimensions (lines and columns) */
107 void
108 msglen (const char *text, int *lines, int *columns)
110 int nlines = 1; /* even the empty string takes one line */
111 int ncolumns = 0;
112 int colindex = 0;
114 for (; *text != '\0'; text++) {
115 if (*text == '\n') {
116 nlines++;
117 colindex = 0;
118 } else {
119 colindex++;
120 if (colindex > ncolumns)
121 ncolumns = colindex;
125 *lines = nlines;
126 *columns = ncolumns;
130 * Copy from s to d, and trim the beginning if necessary, and prepend
131 * "..." in this case. The destination string can have at most len
132 * bytes, not counting trailing 0.
134 char *
135 trim (const char *s, char *d, int len)
137 int source_len;
139 /* Sanity check */
140 len = max (len, 0);
142 source_len = strlen (s);
143 if (source_len > len) {
144 /* Cannot fit the whole line */
145 if (len <= 3) {
146 /* We only have room for the dots */
147 memset (d, '.', len);
148 d[len] = 0;
149 return d;
150 } else {
151 /* Begin with ... and add the rest of the source string */
152 memset (d, '.', 3);
153 strcpy (d + 3, s + 3 + source_len - len);
155 } else
156 /* We can copy the whole line */
157 strcpy (d, s);
158 return d;
162 * Quote the filename for the purpose of inserting it into the command
163 * line. If quote_percent is 1, replace "%" with "%%" - the percent is
164 * processed by the mc command line.
166 char *
167 name_quote (const char *s, int quote_percent)
169 char *ret, *d;
171 d = ret = g_malloc (strlen (s) * 2 + 2 + 1);
172 if (*s == '-') {
173 *d++ = '.';
174 *d++ = '/';
177 for (; *s; s++, d++) {
178 switch (*s) {
179 case '%':
180 if (quote_percent)
181 *d++ = '%';
182 break;
183 case '\'':
184 case '\\':
185 case '\r':
186 case '\n':
187 case '\t':
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 case '(':
205 case ')':
206 *d++ = '\\';
207 break;
208 case '~':
209 case '#':
210 if (d == ret)
211 *d++ = '\\';
212 break;
214 *d = *s;
216 *d = '\0';
217 return ret;
220 char *
221 fake_name_quote (const char *s, int quote_percent)
223 (void) quote_percent;
224 return g_strdup (s);
228 * Remove the middle part of the string to fit given length.
229 * Use "~" to show where the string was truncated.
230 * Return static buffer, no need to free() it.
232 const char *
233 name_trunc (const char *txt, size_t trunc_len)
235 static char x[MC_MAXPATHLEN + MC_MAXPATHLEN];
236 size_t txt_len;
237 char *p;
239 if (!txt)
240 return NULL;
241 if (!*txt)
242 return txt;
244 if (trunc_len > sizeof (x) - 1) {
245 trunc_len = sizeof (x) - 1;
247 txt_len = strlen (txt);
248 if (txt_len <= trunc_len) {
249 strcpy (x, txt);
250 } else {
251 size_t y = (trunc_len / 2) + (trunc_len % 2);
252 strncpy (x, txt, (size_t) y);
253 strncpy (x + y, txt + (txt_len - (trunc_len / 2)), trunc_len / 2);
254 x[y] = '~';
256 x[trunc_len] = 0;
257 for (p = x; *p; p++)
258 if (!is_printable (*p))
259 *p = '?';
260 return x;
264 * path_trunc() is the same as name_trunc() above but
265 * it deletes possible password from path for security
266 * reasons.
268 const char *
269 path_trunc (const char *path, size_t trunc_len) {
270 const char *ret;
271 char *secure_path = strip_password (g_strdup (path), 1);
273 ret = name_trunc (secure_path, trunc_len);
274 g_free (secure_path);
276 return ret;
279 const char *
280 size_trunc (double size)
282 static char x [BUF_TINY];
283 long int divisor = 1;
284 const char *xtra = "";
286 if (size > 999999999L){
287 divisor = 1024;
288 xtra = "K";
289 if (size/divisor > 999999999L){
290 divisor = 1024*1024;
291 xtra = "M";
294 g_snprintf (x, sizeof (x), "%.0f%s", (size/divisor), xtra);
295 return x;
298 const char *
299 size_trunc_sep (double size)
301 static char x [60];
302 int count;
303 const char *p, *y;
304 char *d;
306 p = y = size_trunc (size);
307 p += strlen (p) - 1;
308 d = x + sizeof (x) - 1;
309 *d-- = 0;
310 while (p >= y && isalpha ((unsigned char) *p))
311 *d-- = *p--;
312 for (count = 0; p >= y; count++){
313 if (count == 3){
314 *d-- = ',';
315 count = 0;
317 *d-- = *p--;
319 d++;
320 if (*d == ',')
321 d++;
322 return d;
326 * Print file SIZE to BUFFER, but don't exceed LEN characters,
327 * not including trailing 0. BUFFER should be at least LEN+1 long.
328 * This function is called for every file on panels, so avoid
329 * floating point by any means.
331 * Units: size units (filesystem sizes are 1K blocks)
332 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
334 void
335 size_trunc_len (char *buffer, int len, off_t size, int units)
337 /* Avoid taking power for every file. */
338 static const off_t power10 [] =
339 {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
340 1000000000};
341 static const char * const suffix [] =
342 {"", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL};
343 int j = 0;
345 /* Don't print more than 9 digits - use suffix. */
346 if (len == 0 || len > 9)
347 len = 9;
349 for (j = units; suffix [j] != NULL; j++) {
350 if (size == 0) {
351 if (j == units) {
352 /* Empty files will print "0" even with minimal width. */
353 g_snprintf (buffer, len + 1, "0");
354 break;
357 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
358 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s",
359 (j > 1) ? suffix[j - 1] : "B");
360 break;
363 if (size < power10 [len - (j > 0)]) {
364 g_snprintf (buffer, len + 1, "%lu%s", (unsigned long) size, suffix[j]);
365 break;
368 /* Powers of 1024, with rounding. */
369 size = (size + 512) >> 10;
374 is_exe (mode_t mode)
376 if ((S_IXUSR & mode) || (S_IXGRP & mode) || (S_IXOTH & mode))
377 return 1;
378 return 0;
381 #define ismode(n,m) ((n & m) == m)
383 const char *
384 string_perm (mode_t mode_bits)
386 static char mode[11];
388 strcpy (mode, "----------");
389 if (S_ISDIR (mode_bits))
390 mode[0] = 'd';
391 if (S_ISCHR (mode_bits))
392 mode[0] = 'c';
393 if (S_ISBLK (mode_bits))
394 mode[0] = 'b';
395 if (S_ISLNK (mode_bits))
396 mode[0] = 'l';
397 if (S_ISFIFO (mode_bits))
398 mode[0] = 'p';
399 if (S_ISNAM (mode_bits))
400 mode[0] = 'n';
401 if (S_ISSOCK (mode_bits))
402 mode[0] = 's';
403 if (S_ISDOOR (mode_bits))
404 mode[0] = 'D';
405 if (ismode (mode_bits, S_IXOTH))
406 mode[9] = 'x';
407 if (ismode (mode_bits, S_IWOTH))
408 mode[8] = 'w';
409 if (ismode (mode_bits, S_IROTH))
410 mode[7] = 'r';
411 if (ismode (mode_bits, S_IXGRP))
412 mode[6] = 'x';
413 if (ismode (mode_bits, S_IWGRP))
414 mode[5] = 'w';
415 if (ismode (mode_bits, S_IRGRP))
416 mode[4] = 'r';
417 if (ismode (mode_bits, S_IXUSR))
418 mode[3] = 'x';
419 if (ismode (mode_bits, S_IWUSR))
420 mode[2] = 'w';
421 if (ismode (mode_bits, S_IRUSR))
422 mode[1] = 'r';
423 #ifdef S_ISUID
424 if (ismode (mode_bits, S_ISUID))
425 mode[3] = (mode[3] == 'x') ? 's' : 'S';
426 #endif /* S_ISUID */
427 #ifdef S_ISGID
428 if (ismode (mode_bits, S_ISGID))
429 mode[6] = (mode[6] == 'x') ? 's' : 'S';
430 #endif /* S_ISGID */
431 #ifdef S_ISVTX
432 if (ismode (mode_bits, S_ISVTX))
433 mode[9] = (mode[9] == 'x') ? 't' : 'T';
434 #endif /* S_ISVTX */
435 return mode;
438 /* p: string which might contain an url with a password (this parameter is
439 modified in place).
440 has_prefix = 0: The first parameter is an url without a prefix
441 (user[:pass]@]machine[:port][remote-dir). Delete
442 the password.
443 has_prefix = 1: Search p for known url prefixes. If found delete
444 the password from the url.
445 Caveat: only the first url is found
447 char *
448 strip_password (char *p, int has_prefix)
450 static const struct {
451 const char *name;
452 size_t len;
453 } prefixes[] = { {"/#ftp:", 6},
454 {"ftp://", 6},
455 {"/#mc:", 5},
456 {"mc://", 5},
457 {"/#smb:", 6},
458 {"smb://", 6},
459 {"/#sh:", 5},
460 {"sh://", 5},
461 {"ssh://", 6}
463 char *at, *inner_colon, *dir;
464 size_t i;
465 char *result = p;
467 for (i = 0; i < sizeof (prefixes)/sizeof (prefixes[0]); i++) {
468 char *q;
470 if (has_prefix) {
471 if((q = strstr (p, prefixes[i].name)) == 0)
472 continue;
473 else
474 p = q + prefixes[i].len;
477 if ((dir = strchr (p, PATH_SEP)) != NULL)
478 *dir = '\0';
480 /* search for any possible user */
481 at = strrchr (p, '@');
483 if (dir)
484 *dir = PATH_SEP;
486 /* We have a username */
487 if (at) {
488 inner_colon = memchr (p, ':', at - p);
489 if (inner_colon)
490 memmove (inner_colon, at, strlen(at) + 1);
492 break;
494 return (result);
497 const char *
498 strip_home_and_password(const char *dir)
500 size_t len;
501 static char newdir [MC_MAXPATHLEN];
503 if (home_dir && !strncmp (dir, home_dir, len = strlen (home_dir)) &&
504 (dir[len] == PATH_SEP || dir[len] == '\0')){
505 newdir [0] = '~';
506 g_strlcpy (&newdir [1], &dir [len], sizeof(newdir) - 1);
507 return newdir;
510 /* We do not strip homes in /#ftp tree, I do not like ~'s there
511 (see ftpfs.c why) */
512 g_strlcpy (newdir, dir, sizeof(newdir));
513 strip_password (newdir, 1);
514 return newdir;
517 static char *
518 maybe_start_group (char *d, int do_group, int *was_wildcard)
520 if (!do_group)
521 return d;
522 if (*was_wildcard)
523 return d;
524 *was_wildcard = 1;
525 *d++ = '\\';
526 *d++ = '(';
527 return d;
530 static char *
531 maybe_end_group (char *d, int do_group, int *was_wildcard)
533 if (!do_group)
534 return d;
535 if (!*was_wildcard)
536 return d;
537 *was_wildcard = 0;
538 *d++ = '\\';
539 *d++ = ')';
540 return d;
543 /* If shell patterns are on converts a shell pattern to a regular
544 expression. Called by regexp_match and mask_rename. */
545 /* Shouldn't we support [a-fw] type wildcards as well ?? */
546 char *
547 convert_pattern (const char *pattern, int match_type, int do_group)
549 char *d;
550 char *new_pattern;
551 int was_wildcard = 0;
552 const char *s;
554 if ((match_type != match_regex) && easy_patterns){
555 new_pattern = g_malloc (MC_MAXPATHLEN);
556 d = new_pattern;
557 if (match_type == match_file)
558 *d++ = '^';
559 for (s = pattern; *s; s++, d++){
560 switch (*s){
561 case '*':
562 d = maybe_start_group (d, do_group, &was_wildcard);
563 *d++ = '.';
564 *d = '*';
565 break;
567 case '?':
568 d = maybe_start_group (d, do_group, &was_wildcard);
569 *d = '.';
570 break;
572 case '.':
573 d = maybe_end_group (d, do_group, &was_wildcard);
574 *d++ = '\\';
575 *d = '.';
576 break;
578 default:
579 d = maybe_end_group (d, do_group, &was_wildcard);
580 *d = *s;
581 break;
584 d = maybe_end_group (d, do_group, &was_wildcard);
585 if (match_type == match_file)
586 *d++ = '$';
587 *d = 0;
588 return new_pattern;
589 } else
590 return g_strdup (pattern);
594 regexp_match (const char *pattern, const char *string, int match_type)
596 static regex_t r;
597 static char *old_pattern = NULL;
598 static int old_type;
599 int rval;
600 char *my_pattern;
602 if (!old_pattern || STRCOMP (old_pattern, pattern) || old_type != match_type){
603 if (old_pattern){
604 regfree (&r);
605 g_free (old_pattern);
606 old_pattern = NULL;
608 my_pattern = convert_pattern (pattern, match_type, 0);
609 if (regcomp (&r, my_pattern, REG_EXTENDED|REG_NOSUB|MC_ARCH_FLAGS)) {
610 g_free (my_pattern);
611 return -1;
613 old_pattern = my_pattern;
614 old_type = match_type;
616 rval = !regexec (&r, string, 0, NULL, 0);
617 return rval;
620 const char *
621 extension (const char *filename)
623 const char *d = strrchr (filename, '.');
624 return (d != NULL) ? d + 1 : "";
628 get_int (const char *file, const char *key, int def)
630 return GetPrivateProfileInt (app_text, key, def, file);
634 set_int (const char *file, const char *key, int value)
636 char buffer [BUF_TINY];
638 g_snprintf (buffer, sizeof (buffer), "%d", value);
639 return WritePrivateProfileString (app_text, key, buffer, file);
642 extern char *
643 get_config_string (const char *file, const char *key, const char *defval)
645 char buffer[1024];
646 (void)GetPrivateProfileString (app_text, key, defval, buffer, sizeof(buffer), file);
647 return g_strdup (buffer);
650 extern void
651 set_config_string (const char *file, const char *key, const char *val)
653 (void)WritePrivateProfileString (app_text, key, val, file);
657 exist_file (const char *name)
659 return access (name, R_OK) == 0;
662 char *
663 load_file (const char *filename)
665 FILE *data_file;
666 struct stat s;
667 char *data;
668 long read_size;
670 if ((data_file = fopen (filename, "r")) == NULL){
671 return 0;
673 if (fstat (fileno (data_file), &s) != 0){
674 fclose (data_file);
675 return 0;
677 data = g_malloc (s.st_size+1);
678 read_size = fread (data, 1, s.st_size, data_file);
679 data [read_size] = 0;
680 fclose (data_file);
682 if (read_size > 0)
683 return data;
684 else {
685 g_free (data);
686 return 0;
690 char *
691 load_mc_home_file (const char *filename, char **allocated_filename)
693 char *hintfile_base, *hintfile;
694 char *lang;
695 char *data;
697 hintfile_base = concat_dir_and_file (mc_home, filename);
698 lang = guess_message_value ();
700 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
701 data = load_file (hintfile);
703 if (!data) {
704 g_free (hintfile);
705 /* Fall back to the two-letter language code */
706 if (lang[0] && lang[1])
707 lang[2] = 0;
708 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
709 data = load_file (hintfile);
711 if (!data) {
712 g_free (hintfile);
713 hintfile = hintfile_base;
714 data = load_file (hintfile_base);
718 g_free (lang);
720 if (hintfile != hintfile_base)
721 g_free (hintfile_base);
723 if (allocated_filename)
724 *allocated_filename = hintfile;
725 else
726 g_free (hintfile);
728 return data;
731 /* Check strftime() results. Some systems (i.e. Solaris) have different
732 short-month-name sizes for different locales */
733 size_t
734 i18n_checktimelength (void)
736 time_t testtime = time (NULL);
737 struct tm* lt = localtime(&testtime);
738 size_t length;
740 if (lt == NULL) {
741 // huh, localtime() doesnt seem to work ... falling back to "(invalid)"
742 length = strlen(INVALID_TIME_TEXT);
743 } else {
744 char buf [MAX_I18NTIMELENGTH + 1];
745 size_t a, b;
746 a = strftime (buf, sizeof(buf)-1, _("%b %e %H:%M"), lt);
747 b = strftime (buf, sizeof(buf)-1, _("%b %e %Y"), lt);
748 length = max (a, b);
751 /* Don't handle big differences. Use standard value (email bug, please) */
752 if ( length > MAX_I18NTIMELENGTH || length < MIN_I18NTIMELENGTH )
753 length = STD_I18NTIMELENGTH;
755 return length;
758 const char *
759 file_date (time_t when)
761 static char timebuf [MAX_I18NTIMELENGTH + 1];
762 time_t current_time = time ((time_t) 0);
763 static size_t i18n_timelength = 0;
764 static const char *fmtyear, *fmttime;
765 const char *fmt;
767 if (i18n_timelength == 0){
768 i18n_timelength = i18n_checktimelength() + 1;
770 /* strftime() format string for old dates */
771 fmtyear = _("%b %e %Y");
772 /* strftime() format string for recent dates */
773 fmttime = _("%b %e %H:%M");
776 if (current_time > when + 6L * 30L * 24L * 60L * 60L /* Old. */
777 || current_time < when - 60L * 60L) /* In the future. */
778 /* The file is fairly old or in the future.
779 POSIX says the cutoff is 6 months old;
780 approximate this by 6*30 days.
781 Allow a 1 hour slop factor for what is considered "the future",
782 to allow for NFS server/client clock disagreement.
783 Show the year instead of the time of day. */
785 fmt = fmtyear;
786 else
787 fmt = fmttime;
789 FMT_LOCALTIME(timebuf, i18n_timelength, fmt, when);
791 return timebuf;
794 const char *
795 extract_line (const char *s, const char *top)
797 static char tmp_line [BUF_MEDIUM];
798 char *t = tmp_line;
800 while (*s && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line)-1 && s < top)
801 *t++ = *s++;
802 *t = 0;
803 return tmp_line;
806 /* FIXME: I should write a faster version of this (Aho-Corasick stuff) */
807 const char *
808 _icase_search (const char *text, const char *data, int *lng)
810 const char *d = text;
811 const char *e = data;
812 int dlng = 0;
814 if (lng)
815 *lng = 0;
816 for (;*e; e++) {
817 while (*(e+1) == '\b' && *(e+2)) {
818 e += 2;
819 dlng += 2;
821 if (toupper((unsigned char) *d) == toupper((unsigned char) *e))
822 d++;
823 else {
824 e -= d - text;
825 d = text;
826 dlng = 0;
828 if (!*d) {
829 if (lng)
830 *lng = strlen (text) + dlng;
831 return e+1;
834 return 0;
837 /* The basename routine */
838 const char *
839 x_basename (const char *s)
841 const char *where;
842 return ((where = strrchr (s, PATH_SEP))) ? where + 1 : s;
846 const char *
847 unix_error_string (int error_num)
849 static char buffer [BUF_LARGE];
850 #if GLIB_MAJOR_VERSION >= 2
851 gchar *strerror_currentlocale;
853 strerror_currentlocale = g_locale_from_utf8(g_strerror (error_num), -1, NULL, NULL, NULL);
854 g_snprintf (buffer, sizeof (buffer), "%s (%d)",
855 strerror_currentlocale, error_num);
856 g_free(strerror_currentlocale);
857 #else
858 g_snprintf (buffer, sizeof (buffer), "%s (%d)",
859 g_strerror (error_num), error_num);
860 #endif
861 return buffer;
864 const char *
865 skip_separators (const char *s)
867 for (;*s; s++)
868 if (*s != ' ' && *s != '\t' && *s != ',')
869 break;
870 return s;
873 const char *
874 skip_numbers (const char *s)
876 for (;*s; s++)
877 if (!isdigit ((unsigned char) *s))
878 break;
879 return s;
882 /* Remove all control sequences from the argument string. We define
883 * "control sequence", in a sort of pidgin BNF, as follows:
885 * control-seq = Esc non-'['
886 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
888 * This scheme works for all the terminals described in my termcap /
889 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
890 * terminals. If I hear from a single person who uses such a terminal
891 * with MC, I'll be glad to add support for it. (Dugan)
892 * Non-printable characters are also removed.
895 char *
896 strip_ctrl_codes (char *s)
898 char *w; /* Current position where the stripped data is written */
899 char *r; /* Current position where the original data is read */
901 if (!s)
902 return 0;
904 for (w = s, r = s; *r; ) {
905 if (*r == ESC_CHAR) {
906 /* Skip the control sequence's arguments */ ;
907 if (*(++r) == '[') {
908 /* strchr() matches trailing binary 0 */
909 while (*(++r) && strchr ("0123456789;?", *r));
913 * Now we are at the last character of the sequence.
914 * Skip it unless it's binary 0.
916 if (*r)
917 r++;
918 continue;
921 if (is_printable(*r))
922 *w++ = *r;
923 ++r;
925 *w = 0;
926 return s;
930 #ifndef USE_VFS
931 char *
932 get_current_wd (char *buffer, int size)
934 char *p;
935 int len;
937 p = g_get_current_dir ();
938 len = strlen(p) + 1;
940 if (len > size) {
941 g_free (p);
942 return NULL;
945 memcpy (buffer, p, len);
946 g_free (p);
948 return buffer;
950 #endif /* !USE_VFS */
952 enum compression_type
953 get_compression_type (int fd)
955 unsigned char magic[4];
957 /* Read the magic signature */
958 if (mc_read (fd, (char *) magic, 4) != 4)
959 return COMPRESSION_NONE;
961 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
962 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236)) {
963 return COMPRESSION_GZIP;
966 /* PKZIP_MAGIC */
967 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003
968 && magic[3] == 004) {
969 /* Read compression type */
970 mc_lseek (fd, 8, SEEK_SET);
971 if (mc_read (fd, (char *) magic, 2) != 2)
972 return COMPRESSION_NONE;
974 /* Gzip can handle only deflated (8) or stored (0) files */
975 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
976 return COMPRESSION_NONE;
978 /* Compatible with gzip */
979 return COMPRESSION_GZIP;
982 /* PACK_MAGIC and LZH_MAGIC and compress magic */
983 if (magic[0] == 037
984 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235)) {
985 /* Compatible with gzip */
986 return COMPRESSION_GZIP;
989 /* BZIP and BZIP2 files */
990 if ((magic[0] == 'B') && (magic[1] == 'Z') &&
991 (magic[3] >= '1') && (magic[3] <= '9')) {
992 switch (magic[2]) {
993 case '0':
994 return COMPRESSION_BZIP;
995 case 'h':
996 return COMPRESSION_BZIP2;
999 return 0;
1002 const char *
1003 decompress_extension (int type)
1005 switch (type){
1006 case COMPRESSION_GZIP: return "#ugz";
1007 case COMPRESSION_BZIP: return "#ubz";
1008 case COMPRESSION_BZIP2: return "#ubz2";
1010 /* Should never reach this place */
1011 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
1012 return 0;
1015 /* Hooks */
1016 void
1017 add_hook (Hook **hook_list, void (*hook_fn)(void *), void *data)
1019 Hook *new_hook = g_new (Hook, 1);
1021 new_hook->hook_fn = hook_fn;
1022 new_hook->next = *hook_list;
1023 new_hook->hook_data = data;
1025 *hook_list = new_hook;
1028 void
1029 execute_hooks (Hook *hook_list)
1031 Hook *new_hook = 0;
1032 Hook *p;
1034 /* We copy the hook list first so tahat we let the hook
1035 * function call delete_hook
1038 while (hook_list){
1039 add_hook (&new_hook, hook_list->hook_fn, hook_list->hook_data);
1040 hook_list = hook_list->next;
1042 p = new_hook;
1044 while (new_hook){
1045 (*new_hook->hook_fn)(new_hook->hook_data);
1046 new_hook = new_hook->next;
1049 for (hook_list = p; hook_list;){
1050 p = hook_list;
1051 hook_list = hook_list->next;
1052 g_free (p);
1056 void
1057 delete_hook (Hook **hook_list, void (*hook_fn)(void *))
1059 Hook *current, *new_list, *next;
1061 new_list = 0;
1063 for (current = *hook_list; current; current = next){
1064 next = current->next;
1065 if (current->hook_fn == hook_fn)
1066 g_free (current);
1067 else
1068 add_hook (&new_list, current->hook_fn, current->hook_data);
1070 *hook_list = new_list;
1074 hook_present (Hook *hook_list, void (*hook_fn)(void *))
1076 Hook *p;
1078 for (p = hook_list; p; p = p->next)
1079 if (p->hook_fn == hook_fn)
1080 return 1;
1081 return 0;
1084 void
1085 wipe_password (char *passwd)
1087 char *p = passwd;
1089 if (!p)
1090 return;
1091 for (;*p ; p++)
1092 *p = 0;
1093 g_free (passwd);
1096 /* Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key */
1097 /* Returns a newly allocated string */
1098 char *
1099 convert_controls (const char *p)
1101 char *valcopy = g_strdup (p);
1102 char *q;
1104 /* Parse the escape special character */
1105 for (q = valcopy; *p;){
1106 if (*p == '\\'){
1107 p++;
1108 if ((*p == 'e') || (*p == 'E')){
1109 p++;
1110 *q++ = ESC_CHAR;
1112 } else {
1113 if (*p == '^'){
1114 p++;
1115 if (*p == '^')
1116 *q++ = *p++;
1117 else {
1118 char c = (*p | 0x20);
1119 if (c >= 'a' && c <= 'z') {
1120 *q++ = c - 'a' + 1;
1121 p++;
1122 } else if (*p)
1123 p++;
1125 } else
1126 *q++ = *p++;
1129 *q = 0;
1130 return valcopy;
1133 static char *
1134 resolve_symlinks (const char *path)
1136 char *buf, *buf2, *q, *r, c;
1137 int len;
1138 struct stat mybuf;
1139 const char *p;
1141 if (*path != PATH_SEP)
1142 return NULL;
1143 r = buf = g_malloc (MC_MAXPATHLEN);
1144 buf2 = g_malloc (MC_MAXPATHLEN);
1145 *r++ = PATH_SEP;
1146 *r = 0;
1147 p = path;
1148 for (;;) {
1149 q = strchr (p + 1, PATH_SEP);
1150 if (!q) {
1151 q = strchr (p + 1, 0);
1152 if (q == p + 1)
1153 break;
1155 c = *q;
1156 *q = 0;
1157 if (mc_lstat (path, &mybuf) < 0) {
1158 g_free (buf);
1159 g_free (buf2);
1160 *q = c;
1161 return NULL;
1163 if (!S_ISLNK (mybuf.st_mode))
1164 strcpy (r, p + 1);
1165 else {
1166 len = mc_readlink (path, buf2, MC_MAXPATHLEN - 1);
1167 if (len < 0) {
1168 g_free (buf);
1169 g_free (buf2);
1170 *q = c;
1171 return NULL;
1173 buf2 [len] = 0;
1174 if (*buf2 == PATH_SEP)
1175 strcpy (buf, buf2);
1176 else
1177 strcpy (r, buf2);
1179 canonicalize_pathname (buf);
1180 r = strchr (buf, 0);
1181 if (!*r || *(r - 1) != PATH_SEP) {
1182 *r++ = PATH_SEP;
1183 *r = 0;
1185 *q = c;
1186 p = q;
1187 if (!c)
1188 break;
1190 if (!*buf)
1191 strcpy (buf, PATH_SEP_STR);
1192 else if (*(r - 1) == PATH_SEP && r != buf + 1)
1193 *(r - 1) = 0;
1194 g_free (buf2);
1195 return buf;
1198 /* Finds out a relative path from first to second, i.e. goes as many ..
1199 * as needed up in first and then goes down using second */
1200 char *
1201 diff_two_paths (const char *first, const char *second)
1203 char *p, *q, *r, *s, *buf = NULL;
1204 int i, j, prevlen = -1, currlen;
1205 char *my_first = NULL, *my_second = NULL;
1207 my_first = resolve_symlinks (first);
1208 if (my_first == NULL)
1209 return NULL;
1210 my_second = resolve_symlinks (second);
1211 if (my_second == NULL) {
1212 g_free (my_first);
1213 return NULL;
1215 for (j = 0; j < 2; j++) {
1216 p = my_first;
1217 q = my_second;
1218 for (;;) {
1219 r = strchr (p, PATH_SEP);
1220 s = strchr (q, PATH_SEP);
1221 if (!r || !s)
1222 break;
1223 *r = 0; *s = 0;
1224 if (strcmp (p, q)) {
1225 *r = PATH_SEP; *s = PATH_SEP;
1226 break;
1227 } else {
1228 *r = PATH_SEP; *s = PATH_SEP;
1230 p = r + 1;
1231 q = s + 1;
1233 p--;
1234 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++);
1235 currlen = (i + 1) * 3 + strlen (q) + 1;
1236 if (j) {
1237 if (currlen < prevlen)
1238 g_free (buf);
1239 else {
1240 g_free (my_first);
1241 g_free (my_second);
1242 return buf;
1245 p = buf = g_malloc (currlen);
1246 prevlen = currlen;
1247 for (; i >= 0; i--, p += 3)
1248 strcpy (p, "../");
1249 strcpy (p, q);
1251 g_free (my_first);
1252 g_free (my_second);
1253 return buf;
1256 /* If filename is NULL, then we just append PATH_SEP to the dir */
1257 char *
1258 concat_dir_and_file (const char *dir, const char *file)
1260 int i = strlen (dir);
1262 if (dir [i-1] == PATH_SEP)
1263 return g_strconcat (dir, file, (char *) NULL);
1264 else
1265 return g_strconcat (dir, PATH_SEP_STR, file, (char *) NULL);
1268 /* Append text to GList, remove all entries with the same text */
1269 GList *
1270 list_append_unique (GList *list, char *text)
1272 GList *link, *newlink;
1275 * Go to the last position and traverse the list backwards
1276 * starting from the second last entry to make sure that we
1277 * are not removing the current link.
1279 list = g_list_append (list, text);
1280 list = g_list_last (list);
1281 link = g_list_previous (list);
1283 while (link) {
1284 newlink = g_list_previous (link);
1285 if (!strcmp ((char *) link->data, text)) {
1286 g_free (link->data);
1287 g_list_remove_link (list, link);
1288 g_list_free_1 (link);
1290 link = newlink;
1293 return list;
1296 /* Following code heavily borrows from libiberty, mkstemps.c */
1298 /* Number of attempts to create a temporary file */
1299 #ifndef TMP_MAX
1300 #define TMP_MAX 16384
1301 #endif /* !TMP_MAX */
1304 * Arguments:
1305 * pname (output) - pointer to the name of the temp file (needs g_free).
1306 * NULL if the function fails.
1307 * prefix - part of the filename before the random part.
1308 * Prepend $TMPDIR or /tmp if there are no path separators.
1309 * suffix - if not NULL, part of the filename after the random part.
1311 * Result:
1312 * handle of the open file or -1 if couldn't open any.
1315 mc_mkstemps (char **pname, const char *prefix, const char *suffix)
1317 static const char letters[]
1318 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1319 static unsigned long value;
1320 struct timeval tv;
1321 char *tmpbase;
1322 char *tmpname;
1323 char *XXXXXX;
1324 int count;
1326 if (strchr (prefix, PATH_SEP) == NULL) {
1327 /* Add prefix first to find the position of XXXXXX */
1328 tmpbase = concat_dir_and_file (mc_tmpdir (), prefix);
1329 } else {
1330 tmpbase = g_strdup (prefix);
1333 tmpname = g_strconcat (tmpbase, "XXXXXX", suffix, (char *) NULL);
1334 *pname = tmpname;
1335 XXXXXX = &tmpname[strlen (tmpbase)];
1336 g_free (tmpbase);
1338 /* Get some more or less random data. */
1339 gettimeofday (&tv, NULL);
1340 value += (tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
1342 for (count = 0; count < TMP_MAX; ++count) {
1343 unsigned long v = value;
1344 int fd;
1346 /* Fill in the random bits. */
1347 XXXXXX[0] = letters[v % 62];
1348 v /= 62;
1349 XXXXXX[1] = letters[v % 62];
1350 v /= 62;
1351 XXXXXX[2] = letters[v % 62];
1352 v /= 62;
1353 XXXXXX[3] = letters[v % 62];
1354 v /= 62;
1355 XXXXXX[4] = letters[v % 62];
1356 v /= 62;
1357 XXXXXX[5] = letters[v % 62];
1359 fd = open (tmpname, O_RDWR | O_CREAT | O_TRUNC | O_EXCL,
1360 S_IRUSR | S_IWUSR);
1361 if (fd >= 0) {
1362 /* Successfully created. */
1363 return fd;
1366 /* This is a random value. It is only necessary that the next
1367 TMP_MAX values generated by adding 7777 to VALUE are different
1368 with (module 2^32). */
1369 value += 7777;
1372 /* Unsuccessful. Free the filename. */
1373 g_free (tmpname);
1374 *pname = NULL;
1376 return -1;
1380 * Read and restore position for the given filename.
1381 * If there is no stored data, return line 1 and col 0.
1383 void
1384 load_file_position (const char *filename, long *line, long *column)
1386 char *fn;
1387 FILE *f;
1388 char buf[MC_MAXPATHLEN + 20];
1389 int len;
1391 /* defaults */
1392 *line = 1;
1393 *column = 0;
1395 /* open file with positions */
1396 fn = concat_dir_and_file (home_dir, MC_FILEPOS);
1397 f = fopen (fn, "r");
1398 g_free (fn);
1399 if (!f)
1400 return;
1402 len = strlen (filename);
1404 while (fgets (buf, sizeof (buf), f)) {
1405 const char *p;
1407 /* check if the filename matches the beginning of string */
1408 if (strncmp (buf, filename, len) != 0)
1409 continue;
1411 /* followed by single space */
1412 if (buf[len] != ' ')
1413 continue;
1415 /* and string without spaces */
1416 p = &buf[len + 1];
1417 if (strchr (p, ' '))
1418 continue;
1420 *line = strtol(p, const_cast(char **, &p), 10);
1421 if (*p == ';') {
1422 *column = strtol(p+1, const_cast(char **, &p), 10);
1423 if (*p != '\n')
1424 *column = 0;
1425 } else
1426 *line = 1;
1428 fclose (f);
1431 /* Save position for the given file */
1432 void
1433 save_file_position (const char *filename, long line, long column)
1435 char *tmp, *fn;
1436 FILE *f, *t;
1437 char buf[MC_MAXPATHLEN + 20];
1438 int i = 1;
1439 int len;
1441 len = strlen (filename);
1443 tmp = concat_dir_and_file (home_dir, MC_FILEPOS_TMP);
1444 fn = concat_dir_and_file (home_dir, MC_FILEPOS);
1446 /* open temporary file */
1447 t = fopen (tmp, "w");
1448 if (!t) {
1449 g_free (tmp);
1450 g_free (fn);
1451 return;
1454 /* put the new record */
1455 if (line != 1 || column != 0) {
1456 fprintf (t, "%s %ld;%ld\n", filename, line, column);
1459 /* copy records from the old file */
1460 f = fopen (fn, "r");
1461 if (f) {
1462 while (fgets (buf, sizeof (buf), f)) {
1463 /* Skip entries for the current filename */
1464 if (strncmp (buf, filename, len) == 0 && buf[len] == ' '
1465 && !strchr (&buf[len + 1], ' '))
1466 continue;
1468 fprintf (t, "%s", buf);
1469 if (++i > MC_FILEPOS_ENTRIES)
1470 break;
1472 fclose (f);
1475 fclose (t);
1476 rename (tmp, fn);
1477 g_free (tmp);
1478 g_free (fn);
1481 extern const char *
1482 cstrcasestr (const char *haystack, const char *needle)
1484 const char *hptr;
1485 size_t i, needle_len;
1487 needle_len = strlen (needle);
1488 for (hptr = haystack; *hptr != '\0'; hptr++) {
1489 for (i = 0; i < needle_len; i++) {
1490 if (toupper ((unsigned char) hptr[i]) !=
1491 toupper ((unsigned char) needle[i]))
1492 goto next_try;
1494 return hptr;
1495 next_try:
1496 (void) 0;
1498 return NULL;
1501 const char *
1502 cstrstr (const char *haystack, const char *needle)
1504 return strstr(haystack, needle);
1507 extern char *
1508 str_unconst (const char *s)
1510 return (char *) s;
1513 #define ASCII_A (0x40 + 1)
1514 #define ASCII_Z (0x40 + 26)
1515 #define ASCII_a (0x60 + 1)
1516 #define ASCII_z (0x60 + 26)
1518 extern int
1519 ascii_alpha_to_cntrl (int ch)
1521 if ((ch >= ASCII_A && ch <= ASCII_Z)
1522 || (ch >= ASCII_a && ch <= ASCII_z)) {
1523 ch &= 0x1f;
1525 return ch;
1528 const char *
1529 Q_ (const char *s)
1531 const char *result, *sep;
1533 result = _(s);
1534 sep = strchr(result, '|');
1535 return (sep != NULL) ? sep + 1 : result;
1538 #define shell_escape_toesc(x) \
1539 (((x)==' ')||((x)=='!')||((x)=='#')||((x)=='$')||((x)=='%')|| \
1540 ((x)=='(')||((x)==')')||((x)=='\'')||((x)=='&')||((x)=='~')|| \
1541 ((x)=='{')||((x)=='}')||((x)=='[')||((x)==']')||((x)=='`')|| \
1542 ((x)=='?')||((x)=='|')||((x)=='<')||((x)=='>')||((x)==';')|| \
1543 ((x)=='*')||((x)=='\\')||((x)=='"'))
1545 #define shell_escape_nottoesc(x) \
1546 (((x)!=0) && (!shell_escape_toesc((x))))
1548 /** To be compatible with the general posix command lines we have to escape
1549 strings for the command line
1551 \params in
1552 string for escaping
1554 \returns
1555 return escaped string (which needs to be freed later)
1557 char*
1558 shell_escape(const char* src)
1560 GString *str;
1561 char *result = NULL;
1563 if ((src==NULL)||(!(*src)))
1564 return strdup("");
1566 str = g_string_new("");
1568 /* look for the first char to escape */
1569 while (1)
1571 char c;
1572 /* copy over all chars not to escape */
1573 while ((c=(*src)) && shell_escape_nottoesc(c))
1575 g_string_append_c(str,c);
1576 src++;
1579 /* at this point we either have an \0 or an char to escape */
1580 if (!c) {
1581 result = str->str;
1582 g_string_free(str,FALSE);
1583 return result;
1586 g_string_append_c(str,'\\');
1587 g_string_append_c(str,c);
1588 src++;
1592 /** Unescape paths or other strings for e.g the internal cd
1593 shell-unescape within a given buffer (writing to it!)
1595 \params src
1596 string for unescaping
1598 \returns
1599 return unescaped string (which needs to be freed)
1601 char*
1602 shell_unescape(const char* text)
1604 GString *str;
1605 char *result = NULL;
1607 if (!text)
1608 return NULL;
1611 /* look for the first \ - that's quick skipover if there's nothing to escape */
1612 const char* readptr = text;
1613 while ((*readptr) && ((*readptr)!='\\')) readptr++;
1614 if (!(*readptr)) {
1615 result = g_strdup(text);
1616 return result;
1618 str = g_string_new_len(text, readptr - text);
1620 /* if we're here, we're standing on the first '\' */
1621 char c;
1622 while ((c = *readptr))
1624 if (c=='\\')
1626 readptr++;
1627 switch ((c = *readptr))
1629 case '\0': /* end of string! malformed escape string */
1630 goto out;
1632 case 'n': g_string_append_c(str,'\n'); break;
1633 case 'r': g_string_append_c(str,'\r'); break;
1634 case 't': g_string_append_c(str,'\t'); break;
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 case '"':
1655 case ';':
1656 default:
1657 g_string_append_c(str,c); break;
1660 else /* got a normal character */
1662 g_string_append_c(str,c);
1664 readptr++;
1666 out:
1668 result = str->str;
1669 g_string_free(str,FALSE);
1670 return result;
1673 /** Check if char in pointer contain escape'd chars
1675 \params in
1676 string for checking
1678 \returns
1679 return TRUE if string contain escaped chars
1680 otherwise return FALSE
1682 gboolean
1683 shell_is_char_escaped ( const char *in )
1685 if (in == NULL || !*in || in[0] != '\\')
1686 return FALSE;
1687 if (shell_escape_toesc(in[1]))
1688 return TRUE;
1689 return FALSE;