Fix umpnpmgr build
[reactos.git] / rosapps / mc / src / util.c
1 /* Various utilities
2 Copyright (C) 1994, 1995, 1996 the Free Software Foundation.
3 Written 1994, 1995, 1996 by:
4 Miguel de Icaza, Janne Kukonlehto, Dugan Porter,
5 Jakub Jelinek, Mauricio Plaza.
6
7 The file_date routine is mostly from GNU's fileutils package,
8 written by Richard Stallman and David MacKenzie.
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
23
24 #include <config.h>
25 #include <stdio.h>
26 #if defined(__os2__) /* OS/2 need io.h! .ado */
27 # include <io.h>
28 #endif
29 #include <stdlib.h>
30 #include <sys/types.h>
31 #ifdef HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
34 #include <fcntl.h>
35 #include <signal.h> /* my_system */
36 #include <limits.h> /* INT_MAX */
37 #ifndef SCO_FLAVOR
38 #if defined(_MSC_VER)
39 # include <sys/time.h___>
40 #else
41 # include <sys/time.h> /* alex: sys/select.h defines struct timeval */
42 #endif
43 #endif /* SCO_FLAVOR */
44 #include <sys/param.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <stdarg.h>
48 #include <errno.h> /* my_system */
49 #ifdef SCO_FLAVOR
50 # include <sys/timeb.h> /* alex: for struct timeb, used in time.h */
51 #endif /* SCO_FLAVOR */
52 #include <time.h>
53 #ifndef OS2_NT
54 # include <pwd.h>
55 # include <grp.h>
56 #endif
57 #include <string.h>
58 #include <ctype.h>
59 #ifdef HAVE_SYS_SELECT_H
60 # include <sys/select.h>
61 #endif
62
63 #ifdef __linux__
64 # if defined(__GLIBC__) && (__GLIBC__ < 2)
65 # include <linux/termios.h> /* This is needed for TIOCLINUX */
66 # else
67 # include <termios.h>
68 # endif
69 # include <sys/ioctl.h>
70 #endif
71
72 #include "fs.h"
73 #include "mountlist.h"
74
75 /* From dialog.h (not wanting to include it as
76 it requires including a lot of other files, too) */
77 int message (int error, char *header, char *text, ...);
78
79 #include "mad.h"
80 #if defined(HAVE_RX_H) && defined(HAVE_REGCOMP)
81 #include <rx.h>
82 #else
83 #include "regex.h"
84 #endif
85 #include "util.h"
86 #include "global.h"
87 #include "profile.h"
88 #include "user.h" /* expand_format */
89 #include "../vfs/vfs.h"
90
91 /* "$Id$" */
92
93 char app_text [] = "Midnight-Commander";
94
95 int easy_patterns = 1;
96 int align_extensions = 1;
97 int tilde_trunc = 1;
98
99 struct mount_entry *mount_list = NULL;
100
101 #ifndef HAVE_STRDUP
102 char *strdup (const char *s)
103 {
104 char *t = malloc (strlen (s)+1);
105 strcpy (t, s);
106 return t;
107 }
108 #endif
109
110 int is_printable (int c)
111 {
112 static const unsigned char xterm_printable[] = {
113 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
114 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
115 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
116 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
117 1,1,1,1,0,0,1,1,0,1,1,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,0,0,0,0,0,0,
118 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
119 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
120 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
121 };
122
123 extern int xterm_flag;
124 extern int eight_bit_clean;
125 extern int full_eight_bits;
126
127 c &= 0xff;
128 if (eight_bit_clean){
129 if (full_eight_bits){
130 if (xterm_flag)
131 return xterm_printable [c];
132 else
133 return (c > 31 && c != 127);
134 } else
135 return ((c >31 && c < 127) || c >= 160);
136 } else
137 return (c > 31 && c < 127);
138 }
139
140 /* Returns the message dimensions (lines and columns) */
141 int msglen (char *text, int *lines)
142 {
143 int max = 0;
144 int line_len = 0;
145
146 for (*lines = 1;*text; text++){
147 if (*text == '\n'){
148 line_len = 0;
149 (*lines)++;
150 } else {
151 line_len++;
152 if (line_len > max)
153 max = line_len;
154 }
155 }
156 return max;
157 }
158
159 char *trim (char *s, char *d, int len)
160 {
161 int source_len = strlen (s);
162
163 if (source_len > len){
164 strcpy (d, s+(source_len-len));
165 d [0] = '.';
166 d [1] = '.';
167 d [2] = '.';
168 } else
169 strcpy (d, s);
170 return d;
171 }
172
173 char *
174 name_quote (const char *s, int quote_percent)
175 {
176 char *ret, *d;
177
178 d = ret = xmalloc (strlen (s)*2 + 2 + 1, "quote_name");
179 if (*s == '-') {
180 *d++ = '.';
181 *d++ = '/';
182 }
183
184 for (; *s; s++, d++) {
185 switch (*s)
186 {
187 case '%':
188 if (quote_percent)
189 *d++ = '%';
190 break;
191 case '\'':
192 case '\\':
193 case '\r':
194 case '\n':
195 case '\t':
196 case '$':
197 case '?':
198 case '*':
199 case '(':
200 case ')':
201 case '[':
202 case ']':
203 case '"':
204 case '!':
205 case '&':
206 case '#':
207 case '`':
208 case ' ':
209 *d++ = '\\';
210 }
211 *d = *s;
212 }
213 *d = '\0';
214 return ret;
215 }
216
217 char *
218 fake_name_quote (const char *s, int quote_percent)
219 {
220 return strdup (s);
221 }
222
223 /* If passed an empty txt (this usually means that there is an error)
224 * in the upper layers, we return "/"
225 */
226 char *name_trunc (char *txt, int trunc_len)
227 {
228 static char x [MC_MAXPATHLEN+MC_MAXPATHLEN];
229 int txt_len;
230 char *p;
231
232 if (!txt)
233 txt = PATH_SEP_STR;
234
235 if (trunc_len > sizeof (x)-1){
236 fprintf (stderr, _("name_trunc: too big"));
237 trunc_len = sizeof (x)-1;
238 }
239 txt_len = strlen (txt);
240 if (txt_len <= trunc_len)
241 strcpy (x, txt);
242 else if (tilde_trunc){
243 int y = trunc_len % 2;
244 strncpy (x, txt, (trunc_len/2)+y);
245 strncpy (x+(trunc_len/2)+y, txt+txt_len-(trunc_len/2), trunc_len/2);
246 x [(trunc_len/2)+y] = '~';
247 } else {
248 strncpy (x, txt, trunc_len-1);
249 x [trunc_len-1] = '>';
250 }
251 x [trunc_len] = 0;
252 for (p = x; *p; p++)
253 if (!is_printable (*p))
254 *p = '?';
255 return x;
256 }
257
258 char *size_trunc (long int size)
259 {
260 static char x [30];
261 long int divisor = 1;
262 char *xtra = "";
263
264 if (size > 999999999L){
265 divisor = 1024;
266 xtra = "kb";
267 if (size/divisor > 999999999L){
268 divisor = 1024*1024;
269 xtra = "Mb";
270 }
271 }
272 sprintf (x, "%ld%s", (size/divisor), xtra);
273 return x;
274 }
275
276 char *size_trunc_sep (long int size)
277 {
278 static char x [60];
279 int count;
280 char *p, *d, *y;
281
282 p = y = size_trunc (size);
283 p += strlen (p) - 1;
284 d = x + sizeof (x) - 1;
285 *d-- = 0;
286 while (p >= y && isalpha (*p))
287 *d-- = *p--;
288 for (count = 0; p >= y; count++){
289 if (count == 3){
290 *d-- = ',';
291 count = 0;
292 }
293 *d-- = *p--;
294 }
295 d++;
296 if (*d == ',')
297 d++;
298 return d;
299 }
300
301 int is_exe (mode_t mode)
302 {
303 if ((S_IXUSR & mode) || (S_IXGRP & mode) || (S_IXOTH & mode))
304 return 1;
305 return 0;
306 }
307
308 #define ismode(n,m) ((n & m) == m)
309
310 char *string_perm (mode_t mode_bits)
311 {
312 static char mode [11];
313
314 strcpy (mode, "----------");
315 if (ismode (mode_bits, S_IFDIR)) mode [0] = 'd';
316 #ifdef S_IFSOCK
317 if (ismode (mode_bits, S_IFSOCK)) mode [0] = 's';
318 #endif
319 if (ismode (mode_bits, S_IXOTH)) mode [9] = 'x';
320 if (ismode (mode_bits, S_IWOTH)) mode [8] = 'w';
321 if (ismode (mode_bits, S_IROTH)) mode [7] = 'r';
322 if (ismode (mode_bits, S_IXGRP)) mode [6] = 'x';
323 if (ismode (mode_bits, S_IWGRP)) mode [5] = 'w';
324 if (ismode (mode_bits, S_IRGRP)) mode [4] = 'r';
325 if (ismode (mode_bits, S_IXUSR)) mode [3] = 'x';
326 if (ismode (mode_bits, S_IWUSR)) mode [2] = 'w';
327 if (ismode (mode_bits, S_IRUSR)) mode [1] = 'r';
328 #ifndef OS2_NT
329 if (ismode (mode_bits, S_ISUID)) mode [3] = (mode [3] == 'x') ? 's' : 'S';
330 if (ismode (mode_bits, S_ISGID)) mode [6] = (mode [6] == 'x') ? 's' : 'S';
331 if (ismode (mode_bits, S_IFCHR)) mode [0] = 'c';
332 if (ismode (mode_bits, S_IFBLK)) mode [0] = 'b';
333 if (ismode (mode_bits, S_ISVTX)) mode [9] = (mode [9] == 'x') ? 't' : 'T';
334 if (ismode (mode_bits, S_IFLNK)) mode [0] = 'l';
335 if (ismode (mode_bits, S_IFIFO)) mode [0] = 's';
336 #endif
337 return mode;
338 }
339
340 static char *
341 strip_password (char *path)
342 {
343 char *at, *inner_colon, *dir;
344
345 if ((dir = strchr (path, PATH_SEP)) != NULL)
346 *dir = '\0';
347 /* search for any possible user */
348 at = strchr (path, '@');
349
350 /* We have a username */
351 if (at) {
352 *at = 0;
353 inner_colon = strchr (path, ':');
354 *at = '@';
355 if (inner_colon)
356 strcpy (inner_colon, at);
357 }
358 if (dir)
359 *dir = PATH_SEP;
360 return (path);
361 }
362
363 char *strip_home_and_password(char *dir)
364 {
365 static char newdir [MC_MAXPATHLEN], *p, *q;
366
367 if (home_dir && !strncmp (dir, home_dir, strlen (home_dir))){
368 newdir [0] = '~';
369 strcpy (&newdir [1], &dir [strlen (home_dir)]);
370 return newdir;
371 }
372 #ifdef USE_NETCODE
373 else if (!strncmp (dir, "ftp://", 6)) {
374 strip_password (strcpy (newdir, dir) + 6);
375 if ((p = strchr (newdir + 6, PATH_SEP)) != NULL) {
376 *p = 0;
377 q = ftpfs_gethome (newdir);
378 *p = PATH_SEP;
379 if (q != 0 && strcmp (q, PATH_SEP_STR) && !strncmp (p, q, strlen (q) - 1)) {
380 strcpy (p, "/~");
381 strcat (newdir, p + strlen (q) - 1);
382 }
383 }
384 return newdir;
385 } else if (!strncmp (dir, "mc:", 3)) {
386 char *pth;
387 strcpy (newdir, dir);
388 if (newdir[3] == '/' && newdir[4] == '/') {
389 pth = newdir + 5;
390 strip_password ( newdir + 5);
391 } else {
392 pth = newdir + 3;
393 strip_password (newdir + 3);
394 }
395 if ((p = strchr (pth, PATH_SEP)) != NULL) {
396 *p = 0;
397 q = mcfs_gethome (newdir);
398 *p = PATH_SEP;
399 if (q != NULL ) {
400 if (strcmp (q, PATH_SEP_STR) && !strncmp (p, q, strlen (q) - 1)) {
401 strcpy (p, "/~");
402 strcat (newdir, p + strlen (q) - 1);
403 }
404 free (q);
405 }
406 }
407 return (newdir);
408 }
409 #endif
410 return dir;
411 }
412
413 static char *maybe_start_group (char *d, int do_group, int *was_wildcard)
414 {
415 if (!do_group)
416 return d;
417 if (*was_wildcard)
418 return d;
419 *was_wildcard = 1;
420 *d++ = '\\';
421 *d++ = '(';
422 return d;
423 }
424
425 static char *maybe_end_group (char *d, int do_group, int *was_wildcard)
426 {
427 if (!do_group)
428 return d;
429 if (!*was_wildcard)
430 return d;
431 *was_wildcard = 0;
432 *d++ = '\\';
433 *d++ = ')';
434 return d;
435 }
436
437 /* If shell patterns are on converts a shell pattern to a regular
438 expression. Called by regexp_match and mask_rename. */
439 /* Shouldn't we support [a-fw] type wildcards as well ?? */
440 char *convert_pattern (char *pattern, int match_type, int do_group)
441 {
442 char *s, *d;
443 char *new_pattern;
444 int was_wildcard = 0;
445
446 if (easy_patterns){
447 new_pattern = malloc (MC_MAXPATHLEN);
448 d = new_pattern;
449 if (match_type == match_file)
450 *d++ = '^';
451 for (s = pattern; *s; s++, d++){
452 switch (*s){
453 case '*':
454 d = maybe_start_group (d, do_group, &was_wildcard);
455 *d++ = '.';
456 *d = '*';
457 break;
458
459 case '?':
460 d = maybe_start_group (d, do_group, &was_wildcard);
461 *d = '.';
462 break;
463
464 case '.':
465 d = maybe_end_group (d, do_group, &was_wildcard);
466 *d++ = '\\';
467 *d = '.';
468 break;
469
470 default:
471 d = maybe_end_group (d, do_group, &was_wildcard);
472 *d = *s;
473 break;
474 }
475 }
476 d = maybe_end_group (d, do_group, &was_wildcard);
477 if (match_type == match_file)
478 *d++ = '$';
479 *d = 0;
480 return new_pattern;
481 } else
482 return strdup (pattern);
483 }
484
485 int regexp_match (char *pattern, char *string, int match_type)
486 {
487 static regex_t r;
488 static char *old_pattern = NULL;
489 static int old_type;
490 int rval;
491
492 if (!old_pattern || STRCOMP (old_pattern, pattern) || old_type != match_type){
493 if (old_pattern){
494 regfree (&r);
495 free (old_pattern);
496 }
497 pattern = convert_pattern (pattern, match_type, 0);
498 if (regcomp (&r, pattern, REG_EXTENDED|REG_NOSUB|MC_ARCH_FLAGS)) {
499 free (pattern);
500 return -1;
501 }
502 old_pattern = pattern;
503 old_type = match_type;
504 }
505 rval = !regexec (&r, string, 0, NULL, 0);
506 return rval;
507 }
508
509 char *extension (char *filename)
510 {
511 char *d;
512
513 if (!strlen (filename))
514 return "";
515
516 d = filename + strlen (filename) - 1;
517 for (;d >= filename; d--){
518 if (*d == '.')
519 return d+1;
520 }
521 return "";
522 }
523
524 /* This routine uses the fact that x is at most 14 chars or so */
525 char *split_extension (char *x, int pad)
526 {
527 return x;
528
529 /* Buggy code
530 if (!align_extensions)
531 return x;
532
533 if (strlen (x) >= pad)
534 return x;
535
536 if ((ext = extension (x)) == x || *ext == 0)
537 return x;
538
539 strcpy (xbuf, x);
540 for (i = strlen (x); i < pad; i++)
541 xbuf [i] = ' ';
542 xbuf [pad] = 0;
543
544 l = strlen (ext);
545 for (i = 0; i < l; i++)
546 xbuf [pad-i] = *(ext+l-i-1);
547 for (i = xbuf + (ext - x); i <
548 return xbuf; */
549 }
550
551 #ifndef HAVE_MAD
552 void *do_xmalloc (int size)
553 {
554 void *m = malloc (size);
555
556 if (!m){
557 fprintf (stderr, "memory exhausted\n");
558 exit (1);
559 }
560 return m;
561 }
562 #endif /* HAVE_MAD */
563
564 int get_int (char *file, char *key, int def)
565 {
566 return GetPrivateProfileInt (app_text, key, def, file);
567 }
568
569 int set_int (char *file, char *key, int value)
570 {
571 char buffer [30];
572
573 sprintf (buffer, "%d", value);
574 return WritePrivateProfileString (app_text, key, buffer, file);
575 }
576
577 int exist_file (char *name)
578 {
579 return access (name, R_OK) == 0;
580 }
581
582 char *load_file (char *filename)
583 {
584 FILE *data_file;
585 struct stat s;
586 char *data;
587 long read_size,i;
588
589 if (stat (filename, &s) != 0){
590 return 0;
591 }
592 #ifdef OS2_NT
593 if ((data_file = fopen (filename, "rt")) == NULL){
594 #else
595 if ((data_file = fopen (filename, "r")) == NULL){
596 #endif
597 return 0;
598 }
599 data = (char *) xmalloc (s.st_size+1, "util, load_file");
600 #ifdef OS2_NT
601 memset(data,0,s.st_size+1);
602 #endif
603 read_size = fread (data, 1, s.st_size, data_file);
604 data [read_size] = 0;
605 fclose (data_file);
606
607 if (read_size > 0){
608 return data;
609 }
610 else {
611 free (data);
612 return 0;
613 }
614 }
615
616 char *file_date (time_t when)
617 {
618 static char timebuf [40];
619 time_t current_time = time ((time_t) 0);
620
621 #ifdef OS2_NT
622 char *p;
623
624 p = ctime (&when);
625 strcpy (timebuf, p ? p : "-----");
626 #else
627 strcpy (timebuf, ctime (&when));
628 #endif
629 if (current_time > when + 6L * 30L * 24L * 60L * 60L /* Old. */
630 || current_time < when - 60L * 60L) /* In the future. */
631 {
632 /* The file is fairly old or in the future.
633 POSIX says the cutoff is 6 months old;
634 approximate this by 6*30 days.
635 Allow a 1 hour slop factor for what is considered "the future",
636 to allow for NFS server/client clock disagreement.
637 Show the year instead of the time of day. */
638 strcpy (timebuf + 11, timebuf + 19);
639 }
640 timebuf[16] = 0;
641 return &timebuf [4];
642 }
643
644 /* Like file_date, but packs the data to fit in 10 columns */
645 char *file_date_pck (time_t when)
646 {
647 /* FIXME: Should return only 10 chars, not 14 */
648 return file_date (when);
649 }
650
651 char *extract_line (char *s, char *top)
652 {
653 static char tmp_line [500];
654 char *t = tmp_line;
655
656 while (*s && *s != '\n' && (t - tmp_line) < sizeof (tmp_line)-1 && s < top)
657 *t++ = *s++;
658 *t = 0;
659 return tmp_line;
660 }
661
662 /* FIXME: I should write a faster version of this (Aho-Corasick stuff) */
663 char * _icase_search (char *text, char *data, int *lng)
664 {
665 char *d = text;
666 char *e = data;
667 int dlng = 0;
668
669 if (lng)
670 *lng = 0;
671 for (;*e; e++) {
672 while (*(e+1) == '\b' && *(e+2)) {
673 e += 2;
674 dlng += 2;
675 }
676 if (toupper((unsigned char) *d) == toupper((unsigned char) *e))
677 d++;
678 else {
679 e -= d - text;
680 d = text;
681 dlng = 0;
682 }
683 if (!*d) {
684 if (lng)
685 *lng = strlen (text) + dlng;
686 return e+1;
687 }
688 }
689 return 0;
690 }
691
692 /* The basename routine */
693 char *x_basename (char *s)
694 {
695 char *where;
696 return ((where = strrchr (s, PATH_SEP)))? where + 1 : s;
697 }
698
699 char *get_full_name (char *dir, char *file)
700 {
701 int i;
702 char *d = malloc (strlen (dir) + strlen (file) + 2);
703
704 strcpy (d, dir);
705 i = strlen (dir);
706 if (dir [i - 1] != PATH_SEP || dir [i] != 0)
707 strcat (d, PATH_SEP_STR);
708 file = x_basename (file);
709 strcat (d, file);
710 return d;
711 }
712
713 void my_putenv (char *name, char *data)
714 {
715 char *full;
716
717 full = xmalloc (strlen (name) + strlen (data) + 2, "util, my_putenv");
718 sprintf (full, "%s=%s", name, data);
719 putenv (full);
720 /* WARNING: NEVER FREE THE full VARIABLE!!!!!!!!!!!!!!!!!!!!!!!! */
721 /* It is used by putenv. Freeing it will corrupt the environment */
722 }
723
724 #if 0
725 static void my_putenv_expand (char *name, char macro_code)
726 {
727 char *data;
728
729 data = expand_format (macro_code);
730 my_putenv (name, data);
731 free (data);
732 }
733
734 /* Puts some status information in to the environment so that
735 processes to be executed can access it. */
736 static void prepare_environment (void)
737 {
738 my_putenv_expand ("MC_CURRENT_DIR", 'd');
739 my_putenv_expand ("MC_OTHER_DIR", 'D');
740 my_putenv_expand ("MC_CURRENT_FILE", 'f');
741 my_putenv_expand ("MC_OTHER_FILE", 'F');
742 my_putenv_expand ("MC_CURRENT_TAGGED", 't');
743 my_putenv_expand ("MC_OTHER_TAGGED", 'T');
744 /* MC_CONTROL_FILE has been added to environment on startup */
745 }
746 #endif
747
748 char *unix_error_string (int error_num)
749 {
750 static char buffer [256];
751 char *error_msg;
752
753 #ifdef HAVE_STRERROR
754 error_msg = strerror (error_num);
755 #else
756 extern int sys_nerr;
757 extern char *sys_errlist [];
758 if ((0 <= error_num) && (error_num < sys_nerr))
759 error_msg = sys_errlist[error_num];
760 else
761 error_msg = "strange errno";
762 #endif
763 sprintf (buffer, "%s (%d)", error_msg, error_num);
764 return buffer;
765 }
766
767 char *copy_strings (const char *first,...)
768 {
769 va_list ap;
770 int len;
771 char *data, *result;
772
773 if (!first)
774 return 0;
775
776 len = strlen (first);
777 va_start (ap, first);
778
779 while ((data = va_arg (ap, char *))!=0)
780 len += strlen (data);
781
782 len++;
783
784 result = xmalloc (len, "copy_strings");
785 va_end (ap);
786 va_start (ap, first);
787 strcpy (result, first);
788 while ((data = va_arg (ap, char *)) != 0)
789 strcat (result, data);
790 va_end (ap);
791
792 return result;
793 }
794
795 long blocks2kilos (int blocks, int bsize)
796 {
797 if (bsize > 1024){
798 return blocks * (bsize / 1024);
799 } else if (bsize < 1024){
800 return blocks / (1024 /bsize);
801 } else
802 return blocks;
803 }
804
805 void init_my_statfs (void)
806 {
807 #ifndef NO_INFOMOUNT
808 mount_list = read_filesystem_list (1, 1);
809 #endif
810 }
811
812 char *skip_separators (char *s)
813 {
814 for (;*s; s++)
815 if (*s != ' ' && *s != '\t' && *s != ',')
816 break;
817 return s;
818 }
819
820 char *skip_numbers (char *s)
821 {
822 for (;*s; s++)
823 if (!isdigit (*s))
824 break;
825 return s;
826 }
827
828 /* Remove all control sequences from the argument string. We define
829 * "control sequence", in a sort of pidgin BNF, as follows:
830 *
831 * control-seq = Esc non-'['
832 * | Esc '[' (0 or more digits or ';' or '?') (any other char)
833 *
834 * This scheme works for all the terminals described in my termcap /
835 * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
836 * terminals. If I hear from a single person who uses such a terminal
837 * with MC, I'll be glad to add support for it. (Dugan)
838 */
839
840 char *strip_ctrl_codes (char *s)
841 {
842 int i; /* Current length of the string's correct (stripped) prefix */
843 int j; /* Number of control characters we have skipped so far */
844
845 if (!s)
846 return 0;
847
848 for (i = 0, j = 0; s [i+j]; ++i)
849 if (s [i+j] != ESC_CHAR){
850 if (j)
851 s [i] = s [i+j];
852 } else {
853 ++j;
854 if (s [i+j++] == '[')
855 while (strchr ("0123456789;?", s [i+j++]))
856 /* Skip the control sequence's arguments */ ;
857 --i;
858 }
859 s[i] = 0;
860 return s;
861 }
862
863 #ifndef HAVE_STRCASECMP
864 /* At least one version of HP/UX lacks this */
865 /* Assumes ASCII encoding */
866 int strcasecmp (const char *s, const char *d)
867 {
868 register signed int result;
869
870 while (1){
871 if (result = (0x20 | *s) - (0x20 | *d))
872 break;
873 if (!*s)
874 return 0;
875 s++;
876 d++;
877 }
878 return result;
879 }
880 #endif /* HAVE_STRCASECMP */
881
882 /* getwd is better than getcwd, the later uses a popen ("pwd"); */
883 char *get_current_wd (char *buffer, int size)
884 {
885 char *p;
886
887 #ifdef HAVE_GETWD
888 p = (char *) getwd (buffer);
889 #else
890 p = getcwd (buffer, size);
891 #endif
892 return p;
893 }
894
895 #define CHECK(x) if (x == -1) return 0;
896
897 long get_small_endian_long (int fd)
898 {
899 unsigned char a, b, c, d;
900
901 /* It needs to be read one byte at the time to avoid endianess
902 portability problems */
903 CHECK (mc_read (fd, &a, 1));
904 CHECK (mc_read (fd, &b, 1));
905 CHECK (mc_read (fd, &c, 1));
906 CHECK (mc_read (fd, &d, 1));
907 return (d << 24) | (c << 16) | (b << 8) | a;
908 }
909
910 /* This function returns 0 if the file is not in gunzip format */
911 /* or how much memory must be allocated to load the gziped file */
912 /* Warning: this function moves the current file pointer */
913 long int is_gunzipable (int fd, int *type)
914 {
915 unsigned char magic [4];
916
917 *type = ISGUNZIPABLE_GUNZIP;
918
919 /* Read the magic signature */
920 CHECK (mc_read (fd, &magic [0], 1));
921 CHECK (mc_read (fd, &magic [1], 1));
922 CHECK (mc_read (fd, &magic [2], 1));
923 CHECK (mc_read (fd, &magic [3], 1));
924
925 /* GZIP_MAGIC and OLD_GZIP_MAGIC */
926 if (magic [0] == 037 && (magic [1] == 0213 || magic [1] == 0236)){
927 /* Read the uncompressed size of the file */
928 mc_lseek (fd, -4, SEEK_END);
929 return get_small_endian_long (fd);
930 }
931
932 /* PKZIP_MAGIC */
933 if (magic [0] == 0120 && magic [1] == 0113 && magic [2] == 003 && magic [3] == 004){
934 /* Read compression type */
935 mc_lseek (fd, 8, SEEK_SET);
936 CHECK (mc_read (fd, &magic [0], 1));
937 CHECK (mc_read (fd, &magic [1], 1));
938
939 /* Gzip can handle only deflated (8) or stored (0) files */
940 if ((magic [0] != 8 && magic [0] != 0) || magic [1] != 0)
941 return 0;
942 /* Read the uncompressed size of the first file in the archive */
943 mc_lseek (fd, 22, SEEK_SET);
944 return get_small_endian_long (fd);
945 }
946
947 /* PACK_MAGIC and LZH_MAGIC and compress magic */
948 if (magic [0] == 037 && (magic [1] == 036 || magic [1] == 0240 || magic [1] == 0235)){
949 /* In case the file is packed, sco lzhed or compress_magic, the */
950 /* program guesses that the uncompressed size is (at most) four */
951 /* times the length of the compressed size, if the compression */
952 /* ratio is more than 4:1 the end of the file is not displayed */
953 return 4*mc_lseek (fd, 0, SEEK_END);
954 }
955
956 /* BZIP and BZIP2 files */
957 if ((magic[0] == 'B') && (magic[1] == 'Z') &&
958 (magic [3] >= '1') && (magic [3] <= '9')){
959 switch (magic[2]) {
960 case '0':
961 *type = ISGUNZIPABLE_BZIP;
962 return 5*mc_lseek (fd, 0, SEEK_END);
963 case 'h':
964 *type = ISGUNZIPABLE_BZIP2;
965 return 5*mc_lseek (fd, 0, SEEK_END);
966 }
967 }
968 return 0;
969 }
970
971 char *
972 decompress_command (int type)
973 {
974 switch (type){
975 case ISGUNZIPABLE_GUNZIP:
976 return "gzip -cdf";
977
978 case ISGUNZIPABLE_BZIP:
979 return "bzip -d";
980
981 case ISGUNZIPABLE_BZIP2:
982 return "bzip2 -dc";
983 }
984 /* Should never reach this place */
985 fprintf (stderr, "Fatal: decompress_command called with an unknown argument\n");
986 return 0;
987 }
988
989 void
990 decompress_command_and_arg (int type, char **cmd, char **flags)
991 {
992 switch (type){
993 case ISGUNZIPABLE_GUNZIP:
994 *cmd = "gzip";
995 *flags = "-cdf";
996 return;
997
998 case ISGUNZIPABLE_BZIP:
999 *cmd = "bzip";
1000 *flags = "-d";
1001 return;
1002
1003
1004 case ISGUNZIPABLE_BZIP2:
1005 *cmd = "bzip2";
1006 *flags = "-dc";
1007 return;
1008 }
1009 *cmd = 0;
1010 *flags = 0;
1011
1012 /* Should never reach this place */
1013 fprintf (stderr, "Fatal: decompress_command called with an unknown argument\n");
1014 }
1015
1016 /* Hooks */
1017 void add_hook (Hook **hook_list, void (*hook_fn)(void *), void *data)
1018 {
1019 Hook *new_hook = xmalloc (sizeof (Hook), "add_hook");
1020
1021 new_hook->hook_fn = hook_fn;
1022 new_hook->next = *hook_list;
1023 new_hook->hook_data = data;
1024
1025 *hook_list = new_hook;
1026 }
1027
1028 void execute_hooks (Hook *hook_list)
1029 {
1030 Hook *new_hook = 0;
1031 Hook *p;
1032
1033 /* We copy the hook list first so tahat we let the hook
1034 * function call delete_hook
1035 */
1036
1037 while (hook_list){
1038 add_hook (&new_hook, hook_list->hook_fn, hook_list->hook_data);
1039 hook_list = hook_list->next;
1040 }
1041 p = new_hook;
1042
1043 while (new_hook){
1044 (*new_hook->hook_fn)(new_hook->hook_data);
1045 new_hook = new_hook->next;
1046 }
1047
1048 for (hook_list = p; hook_list;){
1049 p = hook_list;
1050 hook_list = hook_list->next;
1051 free (p);
1052 }
1053 }
1054
1055 void delete_hook (Hook **hook_list, void (*hook_fn)(void *))
1056 {
1057 Hook *current, *new_list, *next;
1058
1059 new_list = 0;
1060
1061 for (current = *hook_list; current; current = next){
1062 next = current->next;
1063 if (current->hook_fn == hook_fn)
1064 free (current);
1065 else
1066 add_hook (&new_list, current->hook_fn, current->hook_data);
1067 }
1068 *hook_list = new_list;
1069 }
1070
1071 int hook_present (Hook *hook_list, void (*hook_fn)(void *))
1072 {
1073 Hook *p;
1074
1075 for (p = hook_list; p; p = p->next)
1076 if (p->hook_fn == hook_fn)
1077 return 1;
1078 return 0;
1079 }
1080
1081 void wipe_password (char *passwd)
1082 {
1083 char *p = passwd;
1084
1085 for (;*p ; p++)
1086 *p = 0;
1087 free (passwd);
1088 }
1089
1090 /* Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key */
1091 /* Returns a newly allocated string */
1092 char *convert_controls (char *s)
1093 {
1094 char *valcopy = strdup (s);
1095 char *p, *q;
1096
1097 /* Parse the escape special character */
1098 for (p = s, q = valcopy; *p;){
1099 if (*p == '\\'){
1100 p++;
1101 if ((*p == 'e') || (*p == 'E')){
1102 p++;
1103 *q++ = ESC_CHAR;
1104 }
1105 } else {
1106 if (*p == '^'){
1107 p++;
1108 if (*p == '^')
1109 *q++ = *p++;
1110 else {
1111 *p = (*p | 0x20);
1112 if (*p >= 'a' && *p <= 'z') {
1113 *q++ = *p++ - 'a' + 1;
1114 } else
1115 p++;
1116 }
1117 } else
1118 *q++ = *p++;
1119 }
1120 }
1121 *q++ = 0;
1122 return valcopy;
1123 }
1124
1125 /* Reverse the string */
1126 char *reverse_string (char *string)
1127 {
1128 int len = strlen (string);
1129 int i;
1130 const int steps = len/2;
1131
1132 for (i = 0; i < steps; i++){
1133 char c = string [i];
1134
1135 string [i] = string [len-i-1];
1136 string [len-i-1] = c;
1137 }
1138 return string;
1139 }
1140
1141 char *resolve_symlinks (char *path)
1142 {
1143 char *buf, *buf2, *p, *q, *r, c;
1144 int len;
1145 struct stat mybuf;
1146
1147 if (*path != PATH_SEP)
1148 return NULL;
1149 r = buf = xmalloc (MC_MAXPATHLEN, "resolve symlinks");
1150 buf2 = xmalloc (MC_MAXPATHLEN, "resolve symlinks");
1151 *r++ = PATH_SEP;
1152 *r = 0;
1153 p = path;
1154 for (;;) {
1155 q = strchr (p + 1, PATH_SEP);
1156 if (!q) {
1157 q = strchr (p + 1, 0);
1158 if (q == p + 1)
1159 break;
1160 }
1161 c = *q;
1162 *q = 0;
1163 if (mc_lstat (path, &mybuf) < 0) {
1164 free (buf);
1165 free (buf2);
1166 *q = c;
1167 return NULL;
1168 }
1169 if (!S_ISLNK (mybuf.st_mode))
1170 strcpy (r, p + 1);
1171 else {
1172 len = mc_readlink (path, buf2, MC_MAXPATHLEN);
1173 if (len < 0) {
1174 free (buf);
1175 free (buf2);
1176 *q = c;
1177 return NULL;
1178 }
1179 buf2 [len] = 0;
1180 if (*buf2 == PATH_SEP)
1181 strcpy (buf, buf2);
1182 else
1183 strcpy (r, buf2);
1184 }
1185 canonicalize_pathname (buf);
1186 r = strchr (buf, 0);
1187 if (!*r || *(r - 1) != PATH_SEP) {
1188 *r++ = PATH_SEP;
1189 *r = 0;
1190 }
1191 *q = c;
1192 p = q;
1193 if (!c)
1194 break;
1195 }
1196 if (!*buf)
1197 strcpy (buf, PATH_SEP_STR);
1198 else if (*(r - 1) == PATH_SEP && r != buf + 1)
1199 *(r - 1) = 0;
1200 free (buf2);
1201 return buf;
1202 }
1203
1204 /* Finds out a relative path from first to second, i.e. goes as many ..
1205 * as needed up in first and then goes down using second */
1206 char *diff_two_paths (char *first, char *second)
1207 {
1208 char *p, *q, *r, *s, *buf = 0;
1209 int i, j, prevlen = -1, currlen;
1210
1211 first = resolve_symlinks (first);
1212 if (first == NULL)
1213 return NULL;
1214 for (j = 0; j < 2; j++) {
1215 p = first;
1216 if (j) {
1217 second = resolve_symlinks (second);
1218 if (second == NULL) {
1219 free (first);
1220 return buf;
1221 }
1222 }
1223 q = second;
1224 for (;;) {
1225 r = strchr (p, PATH_SEP);
1226 s = strchr (q, PATH_SEP);
1227 if (!r || !s)
1228 break;
1229 *r = 0; *s = 0;
1230 if (strcmp (p, q)) {
1231 *r = PATH_SEP; *s = PATH_SEP;
1232 break;
1233 } else {
1234 *r = PATH_SEP; *s = PATH_SEP;
1235 }
1236 p = r + 1;
1237 q = s + 1;
1238 }
1239 p--;
1240 for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++);
1241 currlen = (i + 1) * 3 + strlen (q) + 1;
1242 if (j) {
1243 if (currlen < prevlen)
1244 free (buf);
1245 else {
1246 free (first);
1247 free (second);
1248 return buf;
1249 }
1250 }
1251 p = buf = xmalloc (currlen, "diff 2 paths");
1252 prevlen = currlen;
1253 for (; i >= 0; i--, p += 3)
1254 strcpy (p, "../");
1255 strcpy (p, q);
1256 }
1257 free (first);
1258 free (second);
1259 return buf;
1260 }
1261
1262 #ifndef HAVE_TRUNCATE
1263 /* On SCO and Windows NT systems */
1264 int my_ftruncate (int fd, long size)
1265 {
1266 #ifdef OS2_NT
1267 if(_chsize(fd, size))
1268 return -1;
1269 else
1270 return 0;
1271 #else
1272 struct flock lk;
1273
1274 lk.l_whence = 0;
1275 lk.l_start = size;
1276 lk.l_len = 0;
1277
1278 return fcntl (fd, F_FREESP, &lk);
1279 #endif
1280 }
1281
1282 int truncate (const char *path, long size)
1283 {
1284 int fd;
1285 int res;
1286
1287 fd = open (path, O_RDWR, 0);
1288 if (fd < 0)
1289 return fd;
1290 res = my_ftruncate (fd, size);
1291 if (res < 0)
1292 return res;
1293 close (fd);
1294 return 0;
1295
1296 }
1297
1298 #endif
1299
1300 char *
1301 concat_dir_and_file (const char *dir, const char *file)
1302 {
1303 int l = strlen (dir);
1304
1305 if (dir [l-1] == PATH_SEP)
1306 return copy_strings (dir, file, 0);
1307 else
1308 return copy_strings (dir, PATH_SEP_STR, file, 0);
1309 }