commandline: strip_escape: strip xterm OSC commands in $PS1
[midnight-commander.git] / src / util.c
blob41f63c3143b206b196b8acf526a15ac936375b34
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>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
38 #include "global.h"
39 #include "profile.h"
40 #include "main.h" /* mc_home */
41 #include "cmd.h" /* guess_message_value */
42 #include "mountlist.h"
43 #include "win.h" /* xterm_flag */
44 #include "timefmt.h"
46 #ifdef HAVE_CHARSET
47 #include "charsets.h"
48 #endif
50 static const char app_text [] = "Midnight-Commander";
51 int easy_patterns = 1;
53 extern void str_replace(char *s, char from, char to)
55 for (; *s != '\0'; s++) {
56 if (*s == from)
57 *s = to;
61 static inline int
62 is_7bit_printable (unsigned char c)
64 return (c > 31 && c < 127);
67 static inline int
68 is_iso_printable (unsigned char c)
70 return ((c > 31 && c < 127) || c >= 160);
73 static inline int
74 is_8bit_printable (unsigned char c)
76 /* "Full 8 bits output" doesn't work on xterm */
77 if (xterm_flag)
78 return is_iso_printable (c);
80 return (c > 31 && c != 127 && c != 155);
83 int
84 is_printable (int c)
86 c &= 0xff;
88 #ifdef HAVE_CHARSET
89 /* "Display bits" is ignored, since the user controls the output
90 by setting the output codepage */
91 return is_8bit_printable (c);
92 #else
93 if (!eight_bit_clean)
94 return is_7bit_printable (c);
96 if (full_eight_bits) {
97 return is_8bit_printable (c);
98 } else
99 return is_iso_printable (c);
100 #endif /* !HAVE_CHARSET */
103 /* Calculates the message dimensions (lines and columns) */
104 void
105 msglen (const char *text, int *lines, int *columns)
107 int nlines = 1; /* even the empty string takes one line */
108 int ncolumns = 0;
109 int colindex = 0;
111 for (; *text != '\0'; text++) {
112 if (*text == '\n') {
113 nlines++;
114 colindex = 0;
115 } else {
116 colindex++;
117 if (colindex > ncolumns)
118 ncolumns = colindex;
122 *lines = nlines;
123 *columns = ncolumns;
127 * Copy from s to d, and trim the beginning if necessary, and prepend
128 * "..." in this case. The destination string can have at most len
129 * bytes, not counting trailing 0.
131 char *
132 trim (const char *s, char *d, int len)
134 int source_len;
136 /* Sanity check */
137 len = max (len, 0);
139 source_len = strlen (s);
140 if (source_len > len) {
141 /* Cannot fit the whole line */
142 if (len <= 3) {
143 /* We only have room for the dots */
144 memset (d, '.', len);
145 d[len] = 0;
146 return d;
147 } else {
148 /* Begin with ... and add the rest of the source string */
149 memset (d, '.', 3);
150 strcpy (d + 3, s + 3 + source_len - len);
152 } else
153 /* We can copy the whole line */
154 strcpy (d, s);
155 return d;
159 * Quote the filename for the purpose of inserting it into the command
160 * line. If quote_percent is 1, replace "%" with "%%" - the percent is
161 * processed by the mc command line.
163 char *
164 name_quote (const char *s, int quote_percent)
166 char *ret, *d;
168 d = ret = g_malloc (strlen (s) * 2 + 2 + 1);
169 if (*s == '-') {
170 *d++ = '.';
171 *d++ = '/';
174 for (; *s; s++, d++) {
175 switch (*s) {
176 case '%':
177 if (quote_percent)
178 *d++ = '%';
179 break;
180 case '\'':
181 case '\\':
182 case '\r':
183 case '\n':
184 case '\t':
185 case '"':
186 case ';':
187 case ' ':
188 case '?':
189 case '|':
190 case '[':
191 case ']':
192 case '{':
193 case '}':
194 case '<':
195 case '>':
196 case '`':
197 case '!':
198 case '$':
199 case '&':
200 case '*':
201 case '(':
202 case ')':
203 *d++ = '\\';
204 break;
205 case '~':
206 case '#':
207 if (d == ret)
208 *d++ = '\\';
209 break;
211 *d = *s;
213 *d = '\0';
214 return ret;
217 char *
218 fake_name_quote (const char *s, int quote_percent)
220 (void) quote_percent;
221 return g_strdup (s);
225 * Remove the middle part of the string to fit given length.
226 * Use "~" to show where the string was truncated.
227 * Return static buffer, no need to free() it.
229 const char *
230 name_trunc (const char *txt, size_t trunc_len)
232 static char x[MC_MAXPATHLEN + MC_MAXPATHLEN];
233 size_t txt_len;
234 char *p;
236 if (!txt)
237 return NULL;
238 if (!*txt)
239 return txt;
241 if (trunc_len > sizeof (x) - 1) {
242 trunc_len = sizeof (x) - 1;
244 txt_len = strlen (txt);
245 if (txt_len <= trunc_len) {
246 strcpy (x, txt);
247 } else {
248 size_t y = (trunc_len / 2) + (trunc_len % 2);
249 strncpy (x, txt, (size_t) y);
250 strncpy (x + y, txt + (txt_len - (trunc_len / 2)), trunc_len / 2);
251 x[y] = '~';
253 x[trunc_len] = 0;
254 for (p = x; *p; p++)
255 if (!is_printable (*p))
256 *p = '?';
257 return x;
261 * path_trunc() is the same as name_trunc() above but
262 * it deletes possible password from path for security
263 * reasons.
265 const char *
266 path_trunc (const char *path, size_t trunc_len) {
267 const char *ret;
268 char *secure_path = strip_password (g_strdup (path), 1);
270 ret = name_trunc (secure_path, trunc_len);
271 g_free (secure_path);
273 return ret;
276 const char *
277 size_trunc (double size)
279 static char x [BUF_TINY];
280 long int divisor = 1;
281 const char *xtra = "";
283 if (size > 999999999L){
284 divisor = 1024;
285 xtra = "K";
286 if (size/divisor > 999999999L){
287 divisor = 1024*1024;
288 xtra = "M";
291 g_snprintf (x, sizeof (x), "%.0f%s", (size/divisor), xtra);
292 return x;
295 const char *
296 size_trunc_sep (double size)
298 static char x [60];
299 int count;
300 const char *p, *y;
301 char *d;
303 p = y = size_trunc (size);
304 p += strlen (p) - 1;
305 d = x + sizeof (x) - 1;
306 *d-- = 0;
307 while (p >= y && isalpha ((unsigned char) *p))
308 *d-- = *p--;
309 for (count = 0; p >= y; count++){
310 if (count == 3){
311 *d-- = ',';
312 count = 0;
314 *d-- = *p--;
316 d++;
317 if (*d == ',')
318 d++;
319 return d;
323 * Print file SIZE to BUFFER, but don't exceed LEN characters,
324 * not including trailing 0. BUFFER should be at least LEN+1 long.
325 * This function is called for every file on panels, so avoid
326 * floating point by any means.
328 * Units: size units (filesystem sizes are 1K blocks)
329 * 0=bytes, 1=Kbytes, 2=Mbytes, etc.
331 void
332 size_trunc_len (char *buffer, int len, off_t size, int units)
334 /* Avoid taking power for every file. */
335 static const off_t power10 [] =
336 {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
337 1000000000};
338 static const char * const suffix [] =
339 {"", "K", "M", "G", "T", "P", "E", "Z", "Y", NULL};
340 int j = 0;
342 /* Don't print more than 9 digits - use suffix. */
343 if (len == 0 || len > 9)
344 len = 9;
346 for (j = units; suffix [j] != NULL; j++) {
347 if (size == 0) {
348 if (j == units) {
349 /* Empty files will print "0" even with minimal width. */
350 g_snprintf (buffer, len + 1, "0");
351 break;
354 /* Use "~K" or just "K" if len is 1. Use "B" for bytes. */
355 g_snprintf (buffer, len + 1, (len > 1) ? "~%s" : "%s",
356 (j > 1) ? suffix[j - 1] : "B");
357 break;
360 if (size < power10 [len - (j > 0)]) {
361 g_snprintf (buffer, len + 1, "%lu%s", (unsigned long) size, suffix[j]);
362 break;
365 /* Powers of 1024, with rounding. */
366 size = (size + 512) >> 10;
371 is_exe (mode_t mode)
373 if ((S_IXUSR & mode) || (S_IXGRP & mode) || (S_IXOTH & mode))
374 return 1;
375 return 0;
378 #define ismode(n,m) ((n & m) == m)
380 const char *
381 string_perm (mode_t mode_bits)
383 static char mode[11];
385 strcpy (mode, "----------");
386 if (S_ISDIR (mode_bits))
387 mode[0] = 'd';
388 if (S_ISCHR (mode_bits))
389 mode[0] = 'c';
390 if (S_ISBLK (mode_bits))
391 mode[0] = 'b';
392 if (S_ISLNK (mode_bits))
393 mode[0] = 'l';
394 if (S_ISFIFO (mode_bits))
395 mode[0] = 'p';
396 if (S_ISNAM (mode_bits))
397 mode[0] = 'n';
398 if (S_ISSOCK (mode_bits))
399 mode[0] = 's';
400 if (S_ISDOOR (mode_bits))
401 mode[0] = 'D';
402 if (ismode (mode_bits, S_IXOTH))
403 mode[9] = 'x';
404 if (ismode (mode_bits, S_IWOTH))
405 mode[8] = 'w';
406 if (ismode (mode_bits, S_IROTH))
407 mode[7] = 'r';
408 if (ismode (mode_bits, S_IXGRP))
409 mode[6] = 'x';
410 if (ismode (mode_bits, S_IWGRP))
411 mode[5] = 'w';
412 if (ismode (mode_bits, S_IRGRP))
413 mode[4] = 'r';
414 if (ismode (mode_bits, S_IXUSR))
415 mode[3] = 'x';
416 if (ismode (mode_bits, S_IWUSR))
417 mode[2] = 'w';
418 if (ismode (mode_bits, S_IRUSR))
419 mode[1] = 'r';
420 #ifdef S_ISUID
421 if (ismode (mode_bits, S_ISUID))
422 mode[3] = (mode[3] == 'x') ? 's' : 'S';
423 #endif /* S_ISUID */
424 #ifdef S_ISGID
425 if (ismode (mode_bits, S_ISGID))
426 mode[6] = (mode[6] == 'x') ? 's' : 'S';
427 #endif /* S_ISGID */
428 #ifdef S_ISVTX
429 if (ismode (mode_bits, S_ISVTX))
430 mode[9] = (mode[9] == 'x') ? 't' : 'T';
431 #endif /* S_ISVTX */
432 return mode;
435 /* p: string which might contain an url with a password (this parameter is
436 modified in place).
437 has_prefix = 0: The first parameter is an url without a prefix
438 (user[:pass]@]machine[:port][remote-dir). Delete
439 the password.
440 has_prefix = 1: Search p for known url prefixes. If found delete
441 the password from the url.
442 Caveat: only the first url is found
444 char *
445 strip_password (char *p, int has_prefix)
447 static const struct {
448 const char *name;
449 size_t len;
450 } prefixes[] = { {"/#ftp:", 6},
451 {"ftp://", 6},
452 {"/#mc:", 5},
453 {"mc://", 5},
454 {"/#smb:", 6},
455 {"smb://", 6},
456 {"/#sh:", 5},
457 {"sh://", 5},
458 {"ssh://", 6}
460 char *at, *inner_colon, *dir;
461 size_t i;
462 char *result = p;
464 for (i = 0; i < sizeof (prefixes)/sizeof (prefixes[0]); i++) {
465 char *q;
467 if (has_prefix) {
468 if((q = strstr (p, prefixes[i].name)) == 0)
469 continue;
470 else
471 p = q + prefixes[i].len;
474 if ((dir = strchr (p, PATH_SEP)) != NULL)
475 *dir = '\0';
477 /* search for any possible user */
478 at = strrchr (p, '@');
480 if (dir)
481 *dir = PATH_SEP;
483 /* We have a username */
484 if (at) {
485 inner_colon = memchr (p, ':', at - p);
486 if (inner_colon)
487 memmove (inner_colon, at, strlen(at) + 1);
489 break;
491 return (result);
494 const char *
495 strip_home_and_password(const char *dir)
497 size_t len;
498 static char newdir [MC_MAXPATHLEN];
500 if (home_dir && !strncmp (dir, home_dir, len = strlen (home_dir)) &&
501 (dir[len] == PATH_SEP || dir[len] == '\0')){
502 newdir [0] = '~';
503 g_strlcpy (&newdir [1], &dir [len], sizeof(newdir) - 1);
504 return newdir;
507 /* We do not strip homes in /#ftp tree, I do not like ~'s there
508 (see ftpfs.c why) */
509 g_strlcpy (newdir, dir, sizeof(newdir));
510 strip_password (newdir, 1);
511 return newdir;
514 static char *
515 maybe_start_group (char *d, int do_group, int *was_wildcard)
517 if (!do_group)
518 return d;
519 if (*was_wildcard)
520 return d;
521 *was_wildcard = 1;
522 *d++ = '\\';
523 *d++ = '(';
524 return d;
527 static char *
528 maybe_end_group (char *d, int do_group, int *was_wildcard)
530 if (!do_group)
531 return d;
532 if (!*was_wildcard)
533 return d;
534 *was_wildcard = 0;
535 *d++ = '\\';
536 *d++ = ')';
537 return d;
540 /* If shell patterns are on converts a shell pattern to a regular
541 expression. Called by regexp_match and mask_rename. */
542 /* Shouldn't we support [a-fw] type wildcards as well ?? */
543 char *
544 convert_pattern (const char *pattern, int match_type, int do_group)
546 char *d;
547 char *new_pattern;
548 int was_wildcard = 0;
549 const char *s;
551 if ((match_type != match_regex) && easy_patterns){
552 new_pattern = g_malloc (MC_MAXPATHLEN);
553 d = new_pattern;
554 if (match_type == match_file)
555 *d++ = '^';
556 for (s = pattern; *s; s++, d++){
557 switch (*s){
558 case '*':
559 d = maybe_start_group (d, do_group, &was_wildcard);
560 *d++ = '.';
561 *d = '*';
562 break;
564 case '?':
565 d = maybe_start_group (d, do_group, &was_wildcard);
566 *d = '.';
567 break;
569 case '.':
570 d = maybe_end_group (d, do_group, &was_wildcard);
571 *d++ = '\\';
572 *d = '.';
573 break;
575 default:
576 d = maybe_end_group (d, do_group, &was_wildcard);
577 *d = *s;
578 break;
581 d = maybe_end_group (d, do_group, &was_wildcard);
582 if (match_type == match_file)
583 *d++ = '$';
584 *d = 0;
585 return new_pattern;
586 } else
587 return g_strdup (pattern);
591 regexp_match (const char *pattern, const char *string, int match_type)
593 static regex_t r;
594 static char *old_pattern = NULL;
595 static int old_type;
596 int rval;
597 char *my_pattern;
599 if (!old_pattern || STRCOMP (old_pattern, pattern) || old_type != match_type){
600 if (old_pattern){
601 regfree (&r);
602 g_free (old_pattern);
603 old_pattern = NULL;
605 my_pattern = convert_pattern (pattern, match_type, 0);
606 if (regcomp (&r, my_pattern, REG_EXTENDED|REG_NOSUB|MC_ARCH_FLAGS)) {
607 g_free (my_pattern);
608 return -1;
610 old_pattern = my_pattern;
611 old_type = match_type;
613 rval = !regexec (&r, string, 0, NULL, 0);
614 return rval;
617 const char *
618 extension (const char *filename)
620 const char *d = strrchr (filename, '.');
621 return (d != NULL) ? d + 1 : "";
625 get_int (const char *file, const char *key, int def)
627 return GetPrivateProfileInt (app_text, key, def, file);
631 set_int (const char *file, const char *key, int value)
633 char buffer [BUF_TINY];
635 g_snprintf (buffer, sizeof (buffer), "%d", value);
636 return WritePrivateProfileString (app_text, key, buffer, file);
639 extern char *
640 get_config_string (const char *file, const char *key, const char *defval)
642 char buffer[1024];
643 (void)GetPrivateProfileString (app_text, key, defval, buffer, sizeof(buffer), file);
644 return g_strdup (buffer);
647 extern void
648 set_config_string (const char *file, const char *key, const char *val)
650 (void)WritePrivateProfileString (app_text, key, val, file);
654 exist_file (const char *name)
656 return access (name, R_OK) == 0;
659 char *
660 load_file (const char *filename)
662 FILE *data_file;
663 struct stat s;
664 char *data;
665 long read_size;
667 if ((data_file = fopen (filename, "r")) == NULL){
668 return 0;
670 if (fstat (fileno (data_file), &s) != 0){
671 fclose (data_file);
672 return 0;
674 data = g_malloc (s.st_size+1);
675 read_size = fread (data, 1, s.st_size, data_file);
676 data [read_size] = 0;
677 fclose (data_file);
679 if (read_size > 0)
680 return data;
681 else {
682 g_free (data);
683 return 0;
687 char *
688 load_mc_home_file (const char *filename, char **allocated_filename)
690 char *hintfile_base, *hintfile;
691 char *lang;
692 char *data;
694 hintfile_base = concat_dir_and_file (mc_home, filename);
695 lang = guess_message_value ();
697 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
698 data = load_file (hintfile);
700 if (!data) {
701 g_free (hintfile);
702 /* Fall back to the two-letter language code */
703 if (lang[0] && lang[1])
704 lang[2] = 0;
705 hintfile = g_strconcat (hintfile_base, ".", lang, (char *) NULL);
706 data = load_file (hintfile);
708 if (!data) {
709 g_free (hintfile);
710 hintfile = hintfile_base;
711 data = load_file (hintfile_base);
715 g_free (lang);
717 if (hintfile != hintfile_base)
718 g_free (hintfile_base);
720 if (allocated_filename)
721 *allocated_filename = hintfile;
722 else
723 g_free (hintfile);
725 return data;
728 /* Check strftime() results. Some systems (i.e. Solaris) have different
729 short-month-name sizes for different locales */
730 size_t
731 i18n_checktimelength (void)
733 time_t testtime = time (NULL);
734 struct tm* lt = localtime(&testtime);
735 size_t length;
737 if (lt == NULL) {
738 // huh, localtime() doesnt seem to work ... falling back to "(invalid)"
739 length = strlen(INVALID_TIME_TEXT);
740 } else {
741 char buf [MAX_I18NTIMELENGTH + 1];
742 size_t a, b;
743 a = strftime (buf, sizeof(buf)-1, _("%b %e %H:%M"), lt);
744 b = strftime (buf, sizeof(buf)-1, _("%b %e %Y"), lt);
745 length = max (a, b);
748 /* Don't handle big differences. Use standard value (email bug, please) */
749 if ( length > MAX_I18NTIMELENGTH || length < MIN_I18NTIMELENGTH )
750 length = STD_I18NTIMELENGTH;
752 return length;
755 const char *
756 file_date (time_t when)
758 static char timebuf [MAX_I18NTIMELENGTH + 1];
759 time_t current_time = time ((time_t) 0);
760 static size_t i18n_timelength = 0;
761 static const char *fmtyear, *fmttime;
762 const char *fmt;
764 if (i18n_timelength == 0){
765 i18n_timelength = i18n_checktimelength() + 1;
767 /* strftime() format string for old dates */
768 fmtyear = _("%b %e %Y");
769 /* strftime() format string for recent dates */
770 fmttime = _("%b %e %H:%M");
773 if (current_time > when + 6L * 30L * 24L * 60L * 60L /* Old. */
774 || current_time < when - 60L * 60L) /* In the future. */
775 /* The file is fairly old or in the future.
776 POSIX says the cutoff is 6 months old;
777 approximate this by 6*30 days.
778 Allow a 1 hour slop factor for what is considered "the future",
779 to allow for NFS server/client clock disagreement.
780 Show the year instead of the time of day. */
782 fmt = fmtyear;
783 else
784 fmt = fmttime;
786 FMT_LOCALTIME(timebuf, i18n_timelength, fmt, when);
788 return timebuf;
791 const char *
792 extract_line (const char *s, const char *top)
794 static char tmp_line [BUF_MEDIUM];
795 char *t = tmp_line;
797 while (*s && *s != '\n' && (size_t) (t - tmp_line) < sizeof (tmp_line)-1 && s < top)
798 *t++ = *s++;
799 *t = 0;
800 return tmp_line;
803 /* FIXME: I should write a faster version of this (Aho-Corasick stuff) */
804 const char *
805 _icase_search (const char *text, const char *data, int *lng)
807 const char *d = text;
808 const char *e = data;
809 int dlng = 0;
811 if (lng)
812 *lng = 0;
813 for (;*e; e++) {
814 while (*(e+1) == '\b' && *(e+2)) {
815 e += 2;
816 dlng += 2;
818 if (toupper((unsigned char) *d) == toupper((unsigned char) *e))
819 d++;
820 else {
821 e -= d - text;
822 d = text;
823 dlng = 0;
825 if (!*d) {
826 if (lng)
827 *lng = strlen (text) + dlng;
828 return e+1;
831 return 0;
834 /* The basename routine */
835 const char *
836 x_basename (const char *s)
838 const char *where;
839 return ((where = strrchr (s, PATH_SEP))) ? where + 1 : s;
843 const char *
844 unix_error_string (int error_num)
846 static char buffer [BUF_LARGE];
847 #if GLIB_MAJOR_VERSION >= 2
848 gchar *strerror_currentlocale;
850 strerror_currentlocale = g_locale_from_utf8(g_strerror (error_num), -1, NULL, NULL, NULL);
851 g_snprintf (buffer, sizeof (buffer), "%s (%d)",
852 strerror_currentlocale, error_num);
853 g_free(strerror_currentlocale);
854 #else
855 g_snprintf (buffer, sizeof (buffer), "%s (%d)",
856 g_strerror (error_num), error_num);
857 #endif
858 return buffer;
861 const char *
862 skip_separators (const char *s)
864 for (;*s; s++)
865 if (*s != ' ' && *s != '\t' && *s != ',')
866 break;
867 return s;
870 const char *
871 skip_numbers (const char *s)
873 for (;*s; s++)
874 if (!isdigit ((unsigned char) *s))
875 break;
876 return s;
879 /* Remove all control sequences from the argument string. We define
880 * "control sequence", in a sort of pidgin BNF, as follows:
882 * control-seq = Esc non-'['
883 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
885 * This scheme works for all the terminals described in my termcap /
886 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
887 * terminals. If I hear from a single person who uses such a terminal
888 * with MC, I'll be glad to add support for it. (Dugan)
889 * Non-printable characters are also removed.
892 char *
893 strip_ctrl_codes (char *s)
895 char *w; /* Current position where the stripped data is written */
896 char *r; /* Current position where the original data is read */
898 if (!s)
899 return 0;
901 for (w = s, r = s; *r; ) {
902 if (*r == ESC_CHAR) {
903 /* Skip the control sequence's arguments */ ;
904 if (*(++r) == '[') {
905 /* strchr() matches trailing binary 0 */
906 while (*(++r) && strchr ("0123456789;?", *r));
907 } else
908 if (*r == ']') {
910 * Skip xterm's OSC (Operating System Command)
911 * http://www.xfree86.org/current/ctlseqs.html
912 * OSC P s ; P t ST
913 * OSC P s ; P t BEL
915 char * new_r = r;
917 for (; *new_r; ++new_r)
919 switch (*new_r)
921 /* BEL */
922 case '\a':
923 r = new_r;
924 goto osc_out;
925 case ESC_CHAR:
926 /* ST */
927 if (*(new_r + 1) == '\\')
929 r = new_r + 1;
930 goto osc_out;
934 osc_out:;
938 * Now we are at the last character of the sequence.
939 * Skip it unless it's binary 0.
941 if (*r)
942 r++;
943 continue;
946 if (is_printable(*r))
947 *w++ = *r;
948 ++r;
950 *w = 0;
951 return s;
955 #ifndef USE_VFS
956 char *
957 get_current_wd (char *buffer, int size)
959 char *p;
960 int len;
962 p = g_get_current_dir ();
963 len = strlen(p) + 1;
965 if (len > size) {
966 g_free (p);
967 return NULL;
970 memcpy (buffer, p, len);
971 g_free (p);
973 return buffer;
975 #endif /* !USE_VFS */
977 enum compression_type
978 get_compression_type (int fd)
980 unsigned char magic[4];
982 /* Read the magic signature */
983 if (mc_read (fd, (char *) magic, 4) != 4)
984 return COMPRESSION_NONE;
986 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
987 if (magic[0] == 037 && (magic[1] == 0213 || magic[1] == 0236)) {
988 return COMPRESSION_GZIP;
991 /* PKZIP_MAGIC */
992 if (magic[0] == 0120 && magic[1] == 0113 && magic[2] == 003
993 && magic[3] == 004) {
994 /* Read compression type */
995 mc_lseek (fd, 8, SEEK_SET);
996 if (mc_read (fd, (char *) magic, 2) != 2)
997 return COMPRESSION_NONE;
999 /* Gzip can handle only deflated (8) or stored (0) files */
1000 if ((magic[0] != 8 && magic[0] != 0) || magic[1] != 0)
1001 return COMPRESSION_NONE;
1003 /* Compatible with gzip */
1004 return COMPRESSION_GZIP;
1007 /* PACK_MAGIC and LZH_MAGIC and compress magic */
1008 if (magic[0] == 037
1009 && (magic[1] == 036 || magic[1] == 0240 || magic[1] == 0235)) {
1010 /* Compatible with gzip */
1011 return COMPRESSION_GZIP;
1014 /* BZIP and BZIP2 files */
1015 if ((magic[0] == 'B') && (magic[1] == 'Z') &&
1016 (magic[3] >= '1') && (magic[3] <= '9')) {
1017 switch (magic[2]) {
1018 case '0':
1019 return COMPRESSION_BZIP;
1020 case 'h':
1021 return COMPRESSION_BZIP2;
1024 return 0;
1027 const char *
1028 decompress_extension (int type)
1030 switch (type){
1031 case COMPRESSION_GZIP: return "#ugz";
1032 case COMPRESSION_BZIP: return "#ubz";
1033 case COMPRESSION_BZIP2: return "#ubz2";
1035 /* Should never reach this place */
1036 fprintf (stderr, "Fatal: decompress_extension called with an unknown argument\n");
1037 return 0;
1040 /* Hooks */
1041 void
1042 add_hook (Hook **hook_list, void (*hook_fn)(void *), void *data)
1044 Hook *new_hook = g_new (Hook, 1);
1046 new_hook->hook_fn = hook_fn;
1047 new_hook->next = *hook_list;
1048 new_hook->hook_data = data;
1050 *hook_list = new_hook;
1053 void
1054 execute_hooks (Hook *hook_list)
1056 Hook *new_hook = 0;
1057 Hook *p;
1059 /* We copy the hook list first so tahat we let the hook
1060 * function call delete_hook
1063 while (hook_list){
1064 add_hook (&new_hook, hook_list->hook_fn, hook_list->hook_data);
1065 hook_list = hook_list->next;
1067 p = new_hook;
1069 while (new_hook){
1070 (*new_hook->hook_fn)(new_hook->hook_data);
1071 new_hook = new_hook->next;
1074 for (hook_list = p; hook_list;){
1075 p = hook_list;
1076 hook_list = hook_list->next;
1077 g_free (p);
1081 void
1082 delete_hook (Hook **hook_list, void (*hook_fn)(void *))
1084 Hook *current, *new_list, *next;
1086 new_list = 0;
1088 for (current = *hook_list; current; current = next){
1089 next = current->next;
1090 if (current->hook_fn == hook_fn)
1091 g_free (current);
1092 else
1093 add_hook (&new_list, current->hook_fn, current->hook_data);
1095 *hook_list = new_list;
1099 hook_present (Hook *hook_list, void (*hook_fn)(void *))
1101 Hook *p;
1103 for (p = hook_list; p; p = p->next)
1104 if (p->hook_fn == hook_fn)
1105 return 1;
1106 return 0;
1109 void
1110 wipe_password (char *passwd)
1112 char *p = passwd;
1114 if (!p)
1115 return;
1116 for (;*p ; p++)
1117 *p = 0;
1118 g_free (passwd);
1121 /* Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key */
1122 /* Returns a newly allocated string */
1123 char *
1124 convert_controls (const char *p)
1126 char *valcopy = g_strdup (p);
1127 char *q;
1129 /* Parse the escape special character */
1130 for (q = valcopy; *p;){
1131 if (*p == '\\'){
1132 p++;
1133 if ((*p == 'e') || (*p == 'E')){
1134 p++;
1135 *q++ = ESC_CHAR;
1137 } else {
1138 if (*p == '^'){
1139 p++;
1140 if (*p == '^')
1141 *q++ = *p++;
1142 else {
1143 char c = (*p | 0x20);
1144 if (c >= 'a' && c <= 'z') {
1145 *q++ = c - 'a' + 1;
1146 p++;
1147 } else if (*p)
1148 p++;
1150 } else
1151 *q++ = *p++;
1154 *q = 0;
1155 return valcopy;
1158 static char *
1159 resolve_symlinks (const char *path)
1161 char *buf, *buf2, *q, *r, c;
1162 int len;
1163 struct stat mybuf;
1164 const char *p;
1166 if (*path != PATH_SEP)
1167 return NULL;
1168 r = buf = g_malloc (MC_MAXPATHLEN);
1169 buf2 = g_malloc (MC_MAXPATHLEN);
1170 *r++ = PATH_SEP;
1171 *r = 0;
1172 p = path;
1173 for (;;) {
1174 q = strchr (p + 1, PATH_SEP);
1175 if (!q) {
1176 q = strchr (p + 1, 0);
1177 if (q == p + 1)
1178 break;
1180 c = *q;
1181 *q = 0;
1182 if (mc_lstat (path, &mybuf) < 0) {
1183 g_free (buf);
1184 g_free (buf2);
1185 *q = c;
1186 return NULL;
1188 if (!S_ISLNK (mybuf.st_mode))
1189 strcpy (r, p + 1);
1190 else {
1191 len = mc_readlink (path, buf2, MC_MAXPATHLEN - 1);
1192 if (len < 0) {
1193 g_free (buf);
1194 g_free (buf2);
1195 *q = c;
1196 return NULL;
1198 buf2 [len] = 0;
1199 if (*buf2 == PATH_SEP)
1200 strcpy (buf, buf2);
1201 else
1202 strcpy (r, buf2);
1204 canonicalize_pathname (buf);
1205 r = strchr (buf, 0);
1206 if (!*r || *(r - 1) != PATH_SEP) {
1207 *r++ = PATH_SEP;
1208 *r = 0;
1210 *q = c;
1211 p = q;
1212 if (!c)
1213 break;
1215 if (!*buf)
1216 strcpy (buf, PATH_SEP_STR);
1217 else if (*(r - 1) == PATH_SEP && r != buf + 1)
1218 *(r - 1) = 0;
1219 g_free (buf2);
1220 return buf;
1223 /* Finds out a relative path from first to second, i.e. goes as many ..
1224 * as needed up in first and then goes down using second */
1225 char *
1226 diff_two_paths (const char *first, const char *second)
1228 char *p, *q, *r, *s, *buf = NULL;
1229 int i, j, prevlen = -1, currlen;
1230 char *my_first = NULL, *my_second = NULL;
1232 my_first = resolve_symlinks (first);
1233 if (my_first == NULL)
1234 return NULL;
1235 my_second = resolve_symlinks (second);
1236 if (my_second == NULL) {
1237 g_free (my_first);
1238 return NULL;
1240 for (j = 0; j < 2; j++) {
1241 p = my_first;
1242 q = my_second;
1243 for (;;) {
1244 r = strchr (p, PATH_SEP);
1245 s = strchr (q, PATH_SEP);
1246 if (!r || !s)
1247 break;
1248 *r = 0; *s = 0;
1249 if (strcmp (p, q)) {
1250 *r = PATH_SEP; *s = PATH_SEP;
1251 break;
1252 } else {
1253 *r = PATH_SEP; *s = PATH_SEP;
1255 p = r + 1;
1256 q = s + 1;
1258 p--;
1259 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++);
1260 currlen = (i + 1) * 3 + strlen (q) + 1;
1261 if (j) {
1262 if (currlen < prevlen)
1263 g_free (buf);
1264 else {
1265 g_free (my_first);
1266 g_free (my_second);
1267 return buf;
1270 p = buf = g_malloc (currlen);
1271 prevlen = currlen;
1272 for (; i >= 0; i--, p += 3)
1273 strcpy (p, "../");
1274 strcpy (p, q);
1276 g_free (my_first);
1277 g_free (my_second);
1278 return buf;
1281 /* If filename is NULL, then we just append PATH_SEP to the dir */
1282 char *
1283 concat_dir_and_file (const char *dir, const char *file)
1285 int i = strlen (dir);
1287 if (dir [i-1] == PATH_SEP)
1288 return g_strconcat (dir, file, (char *) NULL);
1289 else
1290 return g_strconcat (dir, PATH_SEP_STR, file, (char *) NULL);
1293 /* Append text to GList, remove all entries with the same text */
1294 GList *
1295 list_append_unique (GList *list, char *text)
1297 GList *link, *newlink;
1300 * Go to the last position and traverse the list backwards
1301 * starting from the second last entry to make sure that we
1302 * are not removing the current link.
1304 list = g_list_append (list, text);
1305 list = g_list_last (list);
1306 link = g_list_previous (list);
1308 while (link) {
1309 newlink = g_list_previous (link);
1310 if (!strcmp ((char *) link->data, text)) {
1311 g_free (link->data);
1312 g_list_remove_link (list, link);
1313 g_list_free_1 (link);
1315 link = newlink;
1318 return list;
1321 /* Following code heavily borrows from libiberty, mkstemps.c */
1323 /* Number of attempts to create a temporary file */
1324 #ifndef TMP_MAX
1325 #define TMP_MAX 16384
1326 #endif /* !TMP_MAX */
1329 * Arguments:
1330 * pname (output) - pointer to the name of the temp file (needs g_free).
1331 * NULL if the function fails.
1332 * prefix - part of the filename before the random part.
1333 * Prepend $TMPDIR or /tmp if there are no path separators.
1334 * suffix - if not NULL, part of the filename after the random part.
1336 * Result:
1337 * handle of the open file or -1 if couldn't open any.
1340 mc_mkstemps (char **pname, const char *prefix, const char *suffix)
1342 static const char letters[]
1343 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1344 static unsigned long value;
1345 struct timeval tv;
1346 char *tmpbase;
1347 char *tmpname;
1348 char *XXXXXX;
1349 int count;
1351 if (strchr (prefix, PATH_SEP) == NULL) {
1352 /* Add prefix first to find the position of XXXXXX */
1353 tmpbase = concat_dir_and_file (mc_tmpdir (), prefix);
1354 } else {
1355 tmpbase = g_strdup (prefix);
1358 tmpname = g_strconcat (tmpbase, "XXXXXX", suffix, (char *) NULL);
1359 *pname = tmpname;
1360 XXXXXX = &tmpname[strlen (tmpbase)];
1361 g_free (tmpbase);
1363 /* Get some more or less random data. */
1364 gettimeofday (&tv, NULL);
1365 value += (tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
1367 for (count = 0; count < TMP_MAX; ++count) {
1368 unsigned long v = value;
1369 int fd;
1371 /* Fill in the random bits. */
1372 XXXXXX[0] = letters[v % 62];
1373 v /= 62;
1374 XXXXXX[1] = letters[v % 62];
1375 v /= 62;
1376 XXXXXX[2] = letters[v % 62];
1377 v /= 62;
1378 XXXXXX[3] = letters[v % 62];
1379 v /= 62;
1380 XXXXXX[4] = letters[v % 62];
1381 v /= 62;
1382 XXXXXX[5] = letters[v % 62];
1384 fd = open (tmpname, O_RDWR | O_CREAT | O_TRUNC | O_EXCL,
1385 S_IRUSR | S_IWUSR);
1386 if (fd >= 0) {
1387 /* Successfully created. */
1388 return fd;
1391 /* This is a random value. It is only necessary that the next
1392 TMP_MAX values generated by adding 7777 to VALUE are different
1393 with (module 2^32). */
1394 value += 7777;
1397 /* Unsuccessful. Free the filename. */
1398 g_free (tmpname);
1399 *pname = NULL;
1401 return -1;
1405 * Read and restore position for the given filename.
1406 * If there is no stored data, return line 1 and col 0.
1408 void
1409 load_file_position (const char *filename, long *line, long *column)
1411 char *fn;
1412 FILE *f;
1413 char buf[MC_MAXPATHLEN + 20];
1414 int len;
1416 /* defaults */
1417 *line = 1;
1418 *column = 0;
1420 /* open file with positions */
1421 fn = concat_dir_and_file (home_dir, MC_FILEPOS);
1422 f = fopen (fn, "r");
1423 g_free (fn);
1424 if (!f)
1425 return;
1427 len = strlen (filename);
1429 while (fgets (buf, sizeof (buf), f)) {
1430 const char *p;
1432 /* check if the filename matches the beginning of string */
1433 if (strncmp (buf, filename, len) != 0)
1434 continue;
1436 /* followed by single space */
1437 if (buf[len] != ' ')
1438 continue;
1440 /* and string without spaces */
1441 p = &buf[len + 1];
1442 if (strchr (p, ' '))
1443 continue;
1445 *line = strtol(p, const_cast(char **, &p), 10);
1446 if (*p == ';') {
1447 *column = strtol(p+1, const_cast(char **, &p), 10);
1448 if (*p != '\n')
1449 *column = 0;
1450 } else
1451 *line = 1;
1453 fclose (f);
1456 /* Save position for the given file */
1457 void
1458 save_file_position (const char *filename, long line, long column)
1460 char *tmp, *fn;
1461 FILE *f, *t;
1462 char buf[MC_MAXPATHLEN + 20];
1463 int i = 1;
1464 int len;
1466 len = strlen (filename);
1468 tmp = concat_dir_and_file (home_dir, MC_FILEPOS_TMP);
1469 fn = concat_dir_and_file (home_dir, MC_FILEPOS);
1471 /* open temporary file */
1472 t = fopen (tmp, "w");
1473 if (!t) {
1474 g_free (tmp);
1475 g_free (fn);
1476 return;
1479 /* put the new record */
1480 if (line != 1 || column != 0) {
1481 fprintf (t, "%s %ld;%ld\n", filename, line, column);
1484 /* copy records from the old file */
1485 f = fopen (fn, "r");
1486 if (f) {
1487 while (fgets (buf, sizeof (buf), f)) {
1488 /* Skip entries for the current filename */
1489 if (strncmp (buf, filename, len) == 0 && buf[len] == ' '
1490 && !strchr (&buf[len + 1], ' '))
1491 continue;
1493 fprintf (t, "%s", buf);
1494 if (++i > MC_FILEPOS_ENTRIES)
1495 break;
1497 fclose (f);
1500 fclose (t);
1501 rename (tmp, fn);
1502 g_free (tmp);
1503 g_free (fn);
1506 extern const char *
1507 cstrcasestr (const char *haystack, const char *needle)
1509 const char *hptr;
1510 size_t i, needle_len;
1512 needle_len = strlen (needle);
1513 for (hptr = haystack; *hptr != '\0'; hptr++) {
1514 for (i = 0; i < needle_len; i++) {
1515 if (toupper ((unsigned char) hptr[i]) !=
1516 toupper ((unsigned char) needle[i]))
1517 goto next_try;
1519 return hptr;
1520 next_try:
1521 (void) 0;
1523 return NULL;
1526 const char *
1527 cstrstr (const char *haystack, const char *needle)
1529 return strstr(haystack, needle);
1532 extern char *
1533 str_unconst (const char *s)
1535 return (char *) s;
1538 #define ASCII_A (0x40 + 1)
1539 #define ASCII_Z (0x40 + 26)
1540 #define ASCII_a (0x60 + 1)
1541 #define ASCII_z (0x60 + 26)
1543 extern int
1544 ascii_alpha_to_cntrl (int ch)
1546 if ((ch >= ASCII_A && ch <= ASCII_Z)
1547 || (ch >= ASCII_a && ch <= ASCII_z)) {
1548 ch &= 0x1f;
1550 return ch;
1553 const char *
1554 Q_ (const char *s)
1556 const char *result, *sep;
1558 result = _(s);
1559 sep = strchr(result, '|');
1560 return (sep != NULL) ? sep + 1 : result;
1563 #define shell_escape_toesc(x) \
1564 (((x)==' ')||((x)=='!')||((x)=='#')||((x)=='$')||((x)=='%')|| \
1565 ((x)=='(')||((x)==')')||((x)=='\'')||((x)=='&')||((x)=='~')|| \
1566 ((x)=='{')||((x)=='}')||((x)=='[')||((x)==']')||((x)=='`')|| \
1567 ((x)=='?')||((x)=='|')||((x)=='<')||((x)=='>')||((x)==';')|| \
1568 ((x)=='*')||((x)=='\\')||((x)=='"'))
1570 #define shell_escape_nottoesc(x) \
1571 (((x)!=0) && (!shell_escape_toesc((x))))
1572 /** To be compatible with the general posix command lines we have to escape
1573 strings for the command line
1575 \params in
1576 string for escaping
1578 \returns
1579 return escaped string (which needs to be freed later)
1581 char*
1582 shell_escape(const char* src)
1584 GString *str;
1585 char *result = NULL;
1587 if ((src==NULL)||(!(*src)))
1588 return strdup("");
1590 str = g_string_new("");
1592 /* look for the first char to escape */
1593 while (1)
1595 char c;
1596 /* copy over all chars not to escape */
1597 while ((c=(*src)) && shell_escape_nottoesc(c))
1599 g_string_append_c(str,c);
1600 src++;
1603 /* at this point we either have an \0 or an char to escape */
1604 if (!c) {
1605 result = str->str;
1606 g_string_free(str,FALSE);
1607 return result;
1610 g_string_append_c(str,'\\');
1611 g_string_append_c(str,c);
1612 src++;
1616 /** Unescape paths or other strings for e.g the internal cd
1617 shell-unescape within a given buffer (writing to it!)
1619 \params src
1620 string for unescaping
1622 \returns
1623 return unescaped string (which needs to be freed)
1625 char*
1626 shell_unescape(const char* text)
1628 GString *str;
1629 char *result = NULL;
1631 if (!text)
1632 return NULL;
1635 /* look for the first \ - that's quick skipover if there's nothing to escape */
1636 const char* readptr = text;
1637 while ((*readptr) && ((*readptr)!='\\')) readptr++;
1638 if (!(*readptr)) {
1639 result = g_strdup(text);
1640 return result;
1642 str = g_string_new_len(text, readptr - text);
1644 /* if we're here, we're standing on the first '\' */
1645 char c;
1646 while ((c = *readptr))
1648 if (c=='\\')
1650 readptr++;
1651 switch ((c = *readptr))
1653 case '\0': /* end of string! malformed escape string */
1654 goto out;
1656 case 'n': g_string_append_c(str,'\n'); break;
1657 case 'r': g_string_append_c(str,'\r'); break;
1658 case 't': g_string_append_c(str,'\t'); break;
1660 case ' ':
1661 case '\\':
1662 case '#':
1663 case '$':
1664 case '%':
1665 case '(':
1666 case ')':
1667 case '[':
1668 case ']':
1669 case '{':
1670 case '}':
1671 case '<':
1672 case '>':
1673 case '!':
1674 case '*':
1675 case '?':
1676 case '~':
1677 case '`':
1678 case '"':
1679 case ';':
1680 default:
1681 g_string_append_c(str,c); break;
1684 else /* got a normal character */
1686 g_string_append_c(str,c);
1688 readptr++;
1690 out:
1692 result = str->str;
1693 g_string_free(str,FALSE);
1694 return result;
1697 /** Check if char in pointer contain escape'd chars
1699 \params in
1700 string for checking
1702 \returns
1703 return TRUE if string contain escaped chars
1704 otherwise return FALSE
1706 gboolean
1707 shell_is_char_escaped ( const char *in )
1709 if (in == NULL || !*in || in[0] != '\\')
1710 return FALSE;
1711 if (shell_escape_toesc(in[1]))
1712 return TRUE;
1713 return FALSE;