2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
20 /* support.c - (non-GUI) useful routines */
30 #include <sys/param.h>
38 #include <libxml/parser.h>
52 static GHashTable
*uid_hash
= NULL
; /* UID -> User name */
53 static GHashTable
*gid_hash
= NULL
; /* GID -> Group name */
55 /* Static prototypes */
56 static void MD5Transform(guint32 buf
[4], guint32
const in
[16]);
58 /****************************************************************
59 * EXTERNAL INTERFACE *
60 ****************************************************************/
62 /* g_object_unref() the result! */
63 XMLwrapper
*xml_cache_load(const gchar
*pathname
)
65 static GFSCache
*xml_cache
= NULL
;
68 xml_cache
= g_fscache_new((GFSLoadFunc
) xml_new
, NULL
, NULL
);
69 return g_fscache_lookup(xml_cache
, pathname
);
72 /* Save doc as XML as filename, 0 on success or 1 on failure */
73 int save_xml_file(xmlDocPtr doc
, const gchar
*filename
)
75 #if LIBXML_VERSION > 20400
76 if (xmlSaveFormatFileEnc(filename
, doc
, NULL
, 1) < 0)
81 out
= fopen(filename
, "w");
85 xmlDocDump(out
, doc
); /* Some versions return void */
94 /* Create a new SOAP message and return the document and the (empty)
97 xmlDocPtr
soap_new(xmlNodePtr
*ret_body
)
103 doc
= xmlNewDoc("1.0");
104 root
= xmlNewDocNode(doc
, NULL
, "Envelope", NULL
);
105 xmlDocSetRootElement(doc
, root
);
107 env_ns
= xmlNewNs(root
, SOAP_ENV_NS
, "env");
108 xmlSetNs(root
, env_ns
);
110 *ret_body
= xmlNewTextChild(root
, env_ns
, "Body", NULL
);
111 xmlNewNs(*ret_body
, ROX_NS
, "rox");
116 /* Like g_strdup, but does realpath() too (if possible) */
117 char *pathdup(const char *path
)
119 char real
[MAXPATHLEN
];
121 g_return_val_if_fail(path
!= NULL
, NULL
);
123 if (realpath(path
, real
))
124 return g_strdup(real
);
126 return g_strdup(path
);
129 /* Join the path to the leaf (adding a / between them) and
130 * return a pointer to a static buffer with the result. Buffer is valid
131 * until the next call to make_path.
132 * The return value may be used as 'dir' for the next call.
134 const guchar
*make_path(const char *dir
, const char *leaf
)
136 static GString
*buffer
= NULL
;
139 buffer
= g_string_new(NULL
);
141 g_return_val_if_fail(dir
!= NULL
, buffer
->str
);
142 g_return_val_if_fail(leaf
!= NULL
, buffer
->str
);
144 if (buffer
->str
!= dir
)
145 g_string_assign(buffer
, dir
);
147 if (dir
[0] != '/' || dir
[1] != '\0')
148 g_string_append_c(buffer
, '/'); /* For anything except "/" */
150 g_string_append(buffer
, leaf
);
155 /* Return our complete host name for DND */
156 const char *our_host_name_for_dnd(void)
158 if (o_dnd_no_hostnames
.int_value
)
160 return our_host_name();
163 /* Return our complete host name, unconditionally */
164 const char *our_host_name(void)
166 static char *name
= NULL
;
172 if (gethostname(buffer
, 4096) == 0)
174 /* gethostname doesn't always return the full name... */
178 ent
= gethostbyname(buffer
);
179 name
= g_strdup(ent
? ent
->h_name
: buffer
);
183 g_warning("gethostname() failed - using localhost\n");
184 name
= g_strdup("localhost");
191 void debug_free_string(void *data
)
193 g_print("Freeing string '%s'\n", (char *) data
);
197 const char *user_name(uid_t uid
)
202 uid_hash
= g_hash_table_new(NULL
, NULL
);
204 retval
= g_hash_table_lookup(uid_hash
, GINT_TO_POINTER(uid
));
208 struct passwd
*passwd
;
210 passwd
= getpwuid(uid
);
211 retval
= passwd
? g_strdup(passwd
->pw_name
)
212 : g_strdup_printf("[%d]", (int) uid
);
213 g_hash_table_insert(uid_hash
, GINT_TO_POINTER(uid
),
220 const char *group_name(gid_t gid
)
225 gid_hash
= g_hash_table_new(NULL
, NULL
);
227 retval
= g_hash_table_lookup(gid_hash
, GINT_TO_POINTER(gid
));
233 group
= getgrgid(gid
);
234 retval
= group
? g_strdup(group
->gr_name
)
235 : g_strdup_printf("[%d]", (int) gid
);
236 g_hash_table_insert(gid_hash
, GINT_TO_POINTER(gid
),
243 /* Return a string in the form '23 M' in a static buffer valid until
246 const char *format_size(off_t size
)
248 static char *buffer
= NULL
;
251 if (size
>= PRETTY_SIZE_LIMIT
)
255 if (size
>= PRETTY_SIZE_LIMIT
)
259 if (size
>= PRETTY_SIZE_LIMIT
)
275 buffer
= g_strdup_printf("%" SIZE_FMT
" %s", size
, units
);
280 /* Return a string in the form '23M' in a static buffer valid until
281 * the next call. Aligned to the right (5 chars).
283 const char *format_size_aligned(off_t size
)
285 static char *buffer
= NULL
;
288 if (size
>= PRETTY_SIZE_LIMIT
)
292 if (size
>= PRETTY_SIZE_LIMIT
)
296 if (size
>= PRETTY_SIZE_LIMIT
)
312 buffer
= g_strdup_printf("%4" SIZE_FMT
"%c", size
, units
);
318 * Similar to format_size(), but this one uses a double argument since
319 * unsigned long isn't wide enough on all platforms and we must be able to
320 * sum sizes above 4 GB.
322 const gchar
*format_double_size(double size
)
324 static gchar
*buf
= NULL
;
327 if (size
>= PRETTY_SIZE_LIMIT
)
331 if (size
>= PRETTY_SIZE_LIMIT
)
335 if (size
>= PRETTY_SIZE_LIMIT
)
354 buf
= g_strdup_printf("%.0f %s", floor(size
), units
);
359 /* Fork and exec argv. Wait and return the child's exit status.
361 * Returns the error string from the command if any, or NULL on success.
362 * If the process returns a non-zero exit status without producing a message,
363 * a suitable message is created.
364 * g_free() the result.
366 char *fork_exec_wait(const char **argv
)
369 gchar
*errors
= NULL
;
370 GError
*error
= NULL
;
372 if (!g_spawn_sync(NULL
, (char **) argv
, NULL
,
373 G_SPAWN_SEARCH_PATH
| G_SPAWN_STDOUT_TO_DEV_NULL
,
375 NULL
, &errors
, &status
, &error
))
379 msg
= g_strdup(error
->message
);
384 if (errors
&& !*errors
)
385 null_g_free(&errors
);
387 if (!WIFEXITED(status
))
390 errors
= g_strdup("(Subprocess crashed?)");
392 else if (WEXITSTATUS(status
))
395 errors
= g_strdup(_("ERROR"));
404 /* If a file has this UID and GID, which permissions apply to us?
405 * 0 = User, 1 = Group, 2 = World
407 gint
applicable(uid_t uid
, gid_t gid
)
417 for (i
= 0; i
< ngroups
; i
++)
419 if (supplemental_groups
[i
] == gid
)
426 /* Converts a file's mode to a string. Result is a pointer
427 * to a static buffer, valid until the next call.
429 const char *pretty_permissions(mode_t m
)
431 static char buffer
[] = "rwx,rwx,rwx/UG"
437 buffer
[0] = m
& S_IRUSR
? 'r' : '-';
438 buffer
[1] = m
& S_IWUSR
? 'w' : '-';
439 buffer
[2] = m
& S_IXUSR
? 'x' : '-';
441 buffer
[4] = m
& S_IRGRP
? 'r' : '-';
442 buffer
[5] = m
& S_IWGRP
? 'w' : '-';
443 buffer
[6] = m
& S_IXGRP
? 'x' : '-';
445 buffer
[8] = m
& S_IROTH
? 'r' : '-';
446 buffer
[9] = m
& S_IWOTH
? 'w' : '-';
447 buffer
[10] = m
& S_IXOTH
? 'x' : '-';
449 buffer
[12] = m
& S_ISUID
? 'U' : '-';
450 buffer
[13] = m
& S_ISGID
? 'G' : '-';
452 buffer
[14] = m
& S_ISVTX
? 'T' : '-';
458 /* Gets the canonical name for address and compares to our_host_name() */
459 static gboolean
is_local_address(char *address
)
463 ent
= gethostbyname(address
);
465 return strcmp(our_host_name(), ent
? ent
->h_name
: address
) == 0;
468 /* Convert a URI to a local pathname (or NULL if it isn't local).
469 * The returned pointer needs to be passed to g_free() when done (if not NULL).
470 * THIS IS A CHANGE. The return path has been processed by unescape_uri().
477 char *get_local_path(const EscapedPath
*escaped_uri
)
479 const char *uri
= (char *) escaped_uri
;
483 char *path
, *uri_host
;
485 /* Just a local path - no host part */
487 return unescape_uri((EscapedPath
*) uri
);
489 path
= strchr(uri
+ 2, '/');
491 return NULL
; /* //something */
496 return unescape_uri((EscapedPath
*) path
);
499 uri_host
= g_strndup(uri
+ 2, path
- uri
- 2);
500 if (is_local_address(uri_host
))
504 return unescape_uri((EscapedPath
*) path
);
508 return NULL
; /* From a different host */
512 if (strncasecmp(uri
, "file:", 5))
513 return NULL
; /* Don't know this format */
518 return get_local_path((EscapedPath
*) uri
);
524 /* Return the scheme part of a URI, or NULL if not a valid URI (see
525 * RFC 2396). g_free() the returned value.
527 #define SCHEME_CHARS "+-." /* In addition to alpha-numerics */
528 gchar
*get_uri_scheme(const EscapedPath
*uri
)
530 const gchar
*start
=(const gchar
*) uri
;
531 const gchar
*p
=start
;
533 if(!g_ascii_isalpha(p
[0]))
536 while(g_ascii_isalnum(p
[0]) ||
537 strchr(SCHEME_CHARS
, p
[0]))
543 return g_strndup(start
, p
-start
);
546 /* Set the close-on-exec flag for this FD.
547 * TRUE means that an exec()'d process will not get the FD.
549 void close_on_exec(int fd
, gboolean close
)
551 if (fcntl(fd
, F_SETFD
, close
))
552 g_warning("fcntl() failed: %s\n", g_strerror(errno
));
555 void set_blocking(int fd
, gboolean blocking
)
557 if (fcntl(fd
, F_SETFL
, blocking
? 0 : O_NONBLOCK
))
558 g_warning("fcntl() failed: %s\n", g_strerror(errno
));
561 /* Format this time nicely.
562 * g_free() the result.
564 char *pretty_time(const time_t *time
)
568 if (strftime(time_buf
, sizeof(time_buf
),
569 TIME_FORMAT
, localtime(time
)) == 0)
572 return to_utf8(time_buf
);
576 # define O_NOFOLLOW 0x0
579 /* 'from' and 'to' are complete pathnames of files (not dirs or symlinks).
580 * This spawns 'cp' to do the copy if lstat() succeeds, otherwise we
581 * do the copy manually using vfs.
583 * Returns an error string, or NULL on success. g_free() the result.
585 * XXX: This was only used for libvfs...
587 guchar
*copy_file(const guchar
*from
, const guchar
*to
)
589 const char *argv
[] = {"cp", "-pRf", NULL
, NULL
, NULL
};
594 return fork_exec_wait(argv
);
597 /* 'word' has all special characters escaped so that it may be inserted
598 * into a shell command.
599 * Eg: 'My Dir?' becomes 'My\ Dir\?'. g_free() the result.
601 guchar
*shell_escape(const guchar
*word
)
606 tmp
= g_string_new(NULL
);
610 if (strchr(" ?*['\"$~\\|();!`&", *word
))
611 g_string_append_c(tmp
, '\\');
612 g_string_append_c(tmp
, *word
);
617 g_string_free(tmp
, FALSE
);
621 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
622 * (or the two are the same item/directory).
623 * FALSE if parent doesn't exist.
625 gboolean
is_sub_dir(const char *sub_obj
, const char *parent
)
627 struct stat parent_info
;
630 if (mc_lstat(parent
, &parent_info
))
631 return FALSE
; /* Parent doesn't exist */
633 /* For checking Copy/Move operations do a realpath first on sub
634 * (the destination), since copying into a symlink is the same as
635 * copying into the thing it points to. Don't realpath 'parent' though;
636 * copying a symlink just makes a new symlink.
638 * When checking if an icon depends on a file (parent), use realpath on
639 * sub (the icon) too.
641 sub
= pathdup(sub_obj
);
648 if (mc_lstat(sub
, &info
) == 0)
650 if (info
.st_dev
== parent_info
.st_dev
&&
651 info
.st_ino
== parent_info
.st_ino
)
658 slash
= strrchr(sub
, '/');
677 /* True if the string 'list' contains 'item'.
678 * Eg ("close", "close, help") -> TRUE
680 gboolean
in_list(const guchar
*item
, const guchar
*list
)
688 if (strncmp(item
, list
, len
) == 0 &&
689 !g_ascii_isalpha(list
[len
]))
691 list
= strchr(list
, ',');
694 while (g_ascii_isspace(*++list
))
701 /* Split a path into its components. Eg:
703 * /bob/fred -> ["bob", "fred"]
704 * ///a//b// -> ["a", "b"]
707 * The array and the strings in it must be freed after use.
709 GPtrArray
*split_path(const guchar
*path
)
714 g_return_val_if_fail(path
!= NULL
, NULL
);
716 array
= g_ptr_array_new();
720 while (path
[0] == '/')
725 slash
= strchr(path
, '/');
728 g_ptr_array_add(array
, g_strndup(path
, slash
- path
));
732 g_ptr_array_add(array
, g_strdup(path
));
739 /* Return the shortest path from 'from' to 'to'.
740 * Eg: get_relative_path("/a/b/c", "a/d/e") -> "../d/e"
742 guchar
*get_relative_path(const guchar
*from
, const guchar
*to
)
746 GPtrArray
*src
, *dst
;
749 src
= split_path(from
);
750 dst
= split_path(to
);
752 /* The last component of src doesn't matter... */
755 g_free(src
->pdata
[src
->len
- 1]);
756 g_ptr_array_remove_index(src
, src
->len
- 1);
759 /* Strip off common path elements... */
761 while (i
< src
->len
&& i
< dst
->len
)
763 guchar
*a
= (guchar
*) src
->pdata
[i
];
764 guchar
*b
= (guchar
*) dst
->pdata
[i
];
766 if (strcmp(a
, b
) != 0)
771 /* Go up one dir for each element remaining in src */
772 path
= g_string_new(NULL
);
773 for (j
= i
; j
< src
->len
; j
++)
774 g_string_append(path
, "../");
776 /* Go down one dir for each element remaining in dst */
777 for (j
= i
; j
< dst
->len
; j
++)
779 g_string_append(path
, (guchar
*) dst
->pdata
[j
]);
780 g_string_append_c(path
, '/');
783 if (path
->str
[path
->len
- 1] == '/')
784 g_string_truncate(path
, path
->len
- 1);
786 g_string_assign(path
, ".");
788 /* Free the arrays */
789 for (i
= 0; i
< src
->len
; i
++)
790 g_free(src
->pdata
[i
]);
791 g_ptr_array_free(src
, TRUE
);
792 for (i
= 0; i
< dst
->len
; i
++)
793 g_free(dst
->pdata
[i
]);
794 g_ptr_array_free(dst
, TRUE
);
797 g_string_free(path
, FALSE
);
803 * Interperet text as a boolean value. Return defvalue if we don't
806 int text_to_boolean(const char *text
, int defvalue
)
808 if (g_strcasecmp(text
, "true")==0)
810 else if (g_strcasecmp(text
, "false")==0)
812 else if (g_strcasecmp(text
, "yes")==0)
814 else if (g_strcasecmp(text
, "no")==0)
816 else if (g_ascii_isdigit(text
[0]))
822 /* Return the pathname that this symlink points to.
823 * NULL on error (not a symlink, path too long) and errno set.
824 * g_free() the result.
826 char *readlink_dup(const char *source
)
828 char path
[MAXPATHLEN
+ 1];
831 got
= readlink(source
, path
, MAXPATHLEN
);
832 if (got
< 0 || got
> MAXPATHLEN
)
835 return g_strndup(path
, got
);
839 * This code implements the MD5 message-digest algorithm.
840 * The algorithm is due to Ron Rivest. The original code was
841 * written by Colin Plumb in 1993, and put in the public domain.
843 * Modified to use glib datatypes. Put under GPL to simplify
844 * licensing for ROX-Filer. Taken from Debian's dpkg package.
847 #define md5byte unsigned char
849 typedef struct _MD5Context MD5Context
;
857 #if G_BYTE_ORDER == G_BIG_ENDIAN
858 static void byteSwap(guint32
*buf
, unsigned words
)
860 md5byte
*p
= (md5byte
*)buf
;
863 *buf
++ = (guint32
)((unsigned)p
[3] << 8 | p
[2]) << 16 |
864 ((unsigned)p
[1] << 8 | p
[0]);
869 #define byteSwap(buf,words)
873 * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
874 * initialization constants.
876 static void MD5Init(MD5Context
*ctx
)
878 ctx
->buf
[0] = 0x67452301;
879 ctx
->buf
[1] = 0xefcdab89;
880 ctx
->buf
[2] = 0x98badcfe;
881 ctx
->buf
[3] = 0x10325476;
888 * Update context to reflect the concatenation of another buffer full
891 static void MD5Update(MD5Context
*ctx
, md5byte
const *buf
, unsigned len
)
895 /* Update byte count */
898 if ((ctx
->bytes
[0] = t
+ len
) < t
)
899 ctx
->bytes
[1]++; /* Carry from low to high */
901 t
= 64 - (t
& 0x3f); /* Space available in ctx->in (at least 1) */
903 memcpy((md5byte
*)ctx
->in
+ 64 - t
, buf
, len
);
906 /* First chunk is an odd size */
907 memcpy((md5byte
*)ctx
->in
+ 64 - t
, buf
, t
);
908 byteSwap(ctx
->in
, 16);
909 MD5Transform(ctx
->buf
, ctx
->in
);
913 /* Process data in 64-byte chunks */
915 memcpy(ctx
->in
, buf
, 64);
916 byteSwap(ctx
->in
, 16);
917 MD5Transform(ctx
->buf
, ctx
->in
);
922 /* Handle any remaining bytes of data. */
923 memcpy(ctx
->in
, buf
, len
);
927 * Final wrapup - pad to 64-byte boundary with the bit pattern
928 * 1 0* (64-bit count of bits processed, MSB-first)
929 * Returns the newly allocated string of the hash.
931 static char *MD5Final(MD5Context
*ctx
)
935 int count
= ctx
->bytes
[0] & 0x3f; /* Number of bytes in ctx->in */
936 md5byte
*p
= (md5byte
*)ctx
->in
+ count
;
939 /* Set the first char of padding to 0x80. There is always room. */
942 /* Bytes of padding needed to make 56 bytes (-8..55) */
943 count
= 56 - 1 - count
;
945 if (count
< 0) { /* Padding forces an extra block */
946 memset(p
, 0, count
+ 8);
947 byteSwap(ctx
->in
, 16);
948 MD5Transform(ctx
->buf
, ctx
->in
);
949 p
= (md5byte
*)ctx
->in
;
953 byteSwap(ctx
->in
, 14);
955 /* Append length in bits and transform */
956 ctx
->in
[14] = ctx
->bytes
[0] << 3;
957 ctx
->in
[15] = ctx
->bytes
[1] << 3 | ctx
->bytes
[0] >> 29;
958 MD5Transform(ctx
->buf
, ctx
->in
);
960 byteSwap(ctx
->buf
, 4);
962 retval
= g_malloc(33);
963 bytes
= (guint8
*) ctx
->buf
;
964 for (i
= 0; i
< 16; i
++)
965 sprintf(retval
+ (i
* 2), "%02x", bytes
[i
]);
973 /* The four core functions - F1 is optimized somewhat */
975 /* #define F1(x, y, z) (x & y | ~x & z) */
976 #define F1(x, y, z) (z ^ (x & (y ^ z)))
977 #define F2(x, y, z) F1(z, x, y)
978 #define F3(x, y, z) (x ^ y ^ z)
979 #define F4(x, y, z) (y ^ (x | ~z))
981 /* This is the central step in the MD5 algorithm. */
982 #define MD5STEP(f,w,x,y,z,in,s) \
983 (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
986 * The core of the MD5 algorithm, this alters an existing MD5 hash to
987 * reflect the addition of 16 longwords of new data. MD5Update blocks
988 * the data and converts bytes into longwords for this routine.
990 static void MD5Transform(guint32 buf
[4], guint32
const in
[16])
992 register guint32 a
, b
, c
, d
;
999 MD5STEP(F1
, a
, b
, c
, d
, in
[0] + 0xd76aa478, 7);
1000 MD5STEP(F1
, d
, a
, b
, c
, in
[1] + 0xe8c7b756, 12);
1001 MD5STEP(F1
, c
, d
, a
, b
, in
[2] + 0x242070db, 17);
1002 MD5STEP(F1
, b
, c
, d
, a
, in
[3] + 0xc1bdceee, 22);
1003 MD5STEP(F1
, a
, b
, c
, d
, in
[4] + 0xf57c0faf, 7);
1004 MD5STEP(F1
, d
, a
, b
, c
, in
[5] + 0x4787c62a, 12);
1005 MD5STEP(F1
, c
, d
, a
, b
, in
[6] + 0xa8304613, 17);
1006 MD5STEP(F1
, b
, c
, d
, a
, in
[7] + 0xfd469501, 22);
1007 MD5STEP(F1
, a
, b
, c
, d
, in
[8] + 0x698098d8, 7);
1008 MD5STEP(F1
, d
, a
, b
, c
, in
[9] + 0x8b44f7af, 12);
1009 MD5STEP(F1
, c
, d
, a
, b
, in
[10] + 0xffff5bb1, 17);
1010 MD5STEP(F1
, b
, c
, d
, a
, in
[11] + 0x895cd7be, 22);
1011 MD5STEP(F1
, a
, b
, c
, d
, in
[12] + 0x6b901122, 7);
1012 MD5STEP(F1
, d
, a
, b
, c
, in
[13] + 0xfd987193, 12);
1013 MD5STEP(F1
, c
, d
, a
, b
, in
[14] + 0xa679438e, 17);
1014 MD5STEP(F1
, b
, c
, d
, a
, in
[15] + 0x49b40821, 22);
1016 MD5STEP(F2
, a
, b
, c
, d
, in
[1] + 0xf61e2562, 5);
1017 MD5STEP(F2
, d
, a
, b
, c
, in
[6] + 0xc040b340, 9);
1018 MD5STEP(F2
, c
, d
, a
, b
, in
[11] + 0x265e5a51, 14);
1019 MD5STEP(F2
, b
, c
, d
, a
, in
[0] + 0xe9b6c7aa, 20);
1020 MD5STEP(F2
, a
, b
, c
, d
, in
[5] + 0xd62f105d, 5);
1021 MD5STEP(F2
, d
, a
, b
, c
, in
[10] + 0x02441453, 9);
1022 MD5STEP(F2
, c
, d
, a
, b
, in
[15] + 0xd8a1e681, 14);
1023 MD5STEP(F2
, b
, c
, d
, a
, in
[4] + 0xe7d3fbc8, 20);
1024 MD5STEP(F2
, a
, b
, c
, d
, in
[9] + 0x21e1cde6, 5);
1025 MD5STEP(F2
, d
, a
, b
, c
, in
[14] + 0xc33707d6, 9);
1026 MD5STEP(F2
, c
, d
, a
, b
, in
[3] + 0xf4d50d87, 14);
1027 MD5STEP(F2
, b
, c
, d
, a
, in
[8] + 0x455a14ed, 20);
1028 MD5STEP(F2
, a
, b
, c
, d
, in
[13] + 0xa9e3e905, 5);
1029 MD5STEP(F2
, d
, a
, b
, c
, in
[2] + 0xfcefa3f8, 9);
1030 MD5STEP(F2
, c
, d
, a
, b
, in
[7] + 0x676f02d9, 14);
1031 MD5STEP(F2
, b
, c
, d
, a
, in
[12] + 0x8d2a4c8a, 20);
1033 MD5STEP(F3
, a
, b
, c
, d
, in
[5] + 0xfffa3942, 4);
1034 MD5STEP(F3
, d
, a
, b
, c
, in
[8] + 0x8771f681, 11);
1035 MD5STEP(F3
, c
, d
, a
, b
, in
[11] + 0x6d9d6122, 16);
1036 MD5STEP(F3
, b
, c
, d
, a
, in
[14] + 0xfde5380c, 23);
1037 MD5STEP(F3
, a
, b
, c
, d
, in
[1] + 0xa4beea44, 4);
1038 MD5STEP(F3
, d
, a
, b
, c
, in
[4] + 0x4bdecfa9, 11);
1039 MD5STEP(F3
, c
, d
, a
, b
, in
[7] + 0xf6bb4b60, 16);
1040 MD5STEP(F3
, b
, c
, d
, a
, in
[10] + 0xbebfbc70, 23);
1041 MD5STEP(F3
, a
, b
, c
, d
, in
[13] + 0x289b7ec6, 4);
1042 MD5STEP(F3
, d
, a
, b
, c
, in
[0] + 0xeaa127fa, 11);
1043 MD5STEP(F3
, c
, d
, a
, b
, in
[3] + 0xd4ef3085, 16);
1044 MD5STEP(F3
, b
, c
, d
, a
, in
[6] + 0x04881d05, 23);
1045 MD5STEP(F3
, a
, b
, c
, d
, in
[9] + 0xd9d4d039, 4);
1046 MD5STEP(F3
, d
, a
, b
, c
, in
[12] + 0xe6db99e5, 11);
1047 MD5STEP(F3
, c
, d
, a
, b
, in
[15] + 0x1fa27cf8, 16);
1048 MD5STEP(F3
, b
, c
, d
, a
, in
[2] + 0xc4ac5665, 23);
1050 MD5STEP(F4
, a
, b
, c
, d
, in
[0] + 0xf4292244, 6);
1051 MD5STEP(F4
, d
, a
, b
, c
, in
[7] + 0x432aff97, 10);
1052 MD5STEP(F4
, c
, d
, a
, b
, in
[14] + 0xab9423a7, 15);
1053 MD5STEP(F4
, b
, c
, d
, a
, in
[5] + 0xfc93a039, 21);
1054 MD5STEP(F4
, a
, b
, c
, d
, in
[12] + 0x655b59c3, 6);
1055 MD5STEP(F4
, d
, a
, b
, c
, in
[3] + 0x8f0ccc92, 10);
1056 MD5STEP(F4
, c
, d
, a
, b
, in
[10] + 0xffeff47d, 15);
1057 MD5STEP(F4
, b
, c
, d
, a
, in
[1] + 0x85845dd1, 21);
1058 MD5STEP(F4
, a
, b
, c
, d
, in
[8] + 0x6fa87e4f, 6);
1059 MD5STEP(F4
, d
, a
, b
, c
, in
[15] + 0xfe2ce6e0, 10);
1060 MD5STEP(F4
, c
, d
, a
, b
, in
[6] + 0xa3014314, 15);
1061 MD5STEP(F4
, b
, c
, d
, a
, in
[13] + 0x4e0811a1, 21);
1062 MD5STEP(F4
, a
, b
, c
, d
, in
[4] + 0xf7537e82, 6);
1063 MD5STEP(F4
, d
, a
, b
, c
, in
[11] + 0xbd3af235, 10);
1064 MD5STEP(F4
, c
, d
, a
, b
, in
[2] + 0x2ad7d2bb, 15);
1065 MD5STEP(F4
, b
, c
, d
, a
, in
[9] + 0xeb86d391, 21);
1073 # endif /* ASM_MD5 */
1075 char *md5_hash(const char *message
)
1080 MD5Update(&ctx
, message
, strlen(message
));
1081 return MD5Final(&ctx
);
1084 /* Convert string 'src' from the current locale to UTF-8 */
1085 gchar
*to_utf8(const gchar
*src
)
1092 retval
= g_locale_to_utf8(src
, -1, NULL
, NULL
, NULL
);
1094 retval
= g_convert_with_fallback(src
, -1, "utf-8", "iso-8859-1",
1095 "?", NULL
, NULL
, NULL
);
1097 return retval
? retval
: g_strdup(src
);
1100 /* Ensure string at 'sp' is UTF-8. g_free() and replace by
1101 * UTF-8 version if not.
1103 void ensure_utf8(gchar
**sp
)
1107 if (!g_utf8_validate(s
, -1, NULL
))
1114 /* Removes trailing / chars and converts a leading '~/' (if any) to
1115 * the user's home dir. g_free() the result.
1117 gchar
*expand_path(const gchar
*path
)
1122 g_return_val_if_fail(path
!= NULL
, NULL
);
1124 path_len
= strlen(path
);
1125 while (path_len
> 1 && path
[path_len
- 1] == '/')
1128 retval
= g_strndup(path
, path_len
);
1130 if (path
[0] == '~' && (path
[1] == '\0' || path
[1] == '/'))
1132 guchar
*tmp
= retval
;
1134 retval
= g_strconcat(home_dir
, retval
+ 1, NULL
);
1141 /* g_free() every element in the list, then free the list itself and
1142 * NULL the pointer to the list.
1144 void destroy_glist(GList
**list
)
1147 g_list_foreach(l
, (GFunc
) g_free
, NULL
);
1152 void null_g_free(gpointer p
)
1154 g_free(*(gpointer
*)p
);
1155 *(gpointer
*)p
= NULL
;
1158 typedef struct _CollatePart CollatePart
;
1160 struct _CollateKey
{
1165 struct _CollatePart
{
1166 guchar
*text
; /* NULL => end of list */
1170 /* Break 'name' (a UTF-8 string) down into a list of (text, number) pairs.
1171 * The text parts processed for collating. This allows any two names to be
1172 * quickly compared later for intelligent sorting (comparing names is
1175 CollateKey
*collate_key_new(const guchar
*name
)
1178 guchar
*to_free
= NULL
;
1184 g_return_val_if_fail(name
!= NULL
, NULL
);
1186 array
= g_array_new(FALSE
, FALSE
, sizeof(CollatePart
));
1188 /* Ensure valid UTF-8 */
1189 if (!g_utf8_validate(name
, -1, NULL
))
1191 to_free
= to_utf8(name
);
1195 retval
= g_new(CollateKey
, 1);
1196 retval
->caps
= g_unichar_isupper(g_utf8_get_char(name
));
1198 for (i
= name
; *i
; i
= g_utf8_next_char(i
))
1200 gunichar first_char
;
1202 /* We're in a (possibly blank) text section starting at 'name'.
1203 * Find the end of it (the next digit, or end of string).
1204 * Note: g_ascii_isdigit takes char, not unichar, while
1205 * g_unicode_isdigit returns true for non ASCII digits.
1207 first_char
= g_utf8_get_char(i
);
1208 if (first_char
>= '0' && first_char
<= '9')
1212 /* i -> first digit character */
1213 tmp
= g_utf8_strdown(name
, i
- name
);
1214 new.text
= g_utf8_collate_key(tmp
, -1);
1216 new.number
= strtol(i
, &endp
, 10);
1218 g_array_append_val(array
, new);
1220 g_return_val_if_fail(endp
> (char *) i
, NULL
);
1227 tmp
= g_utf8_strdown(name
, i
- name
);
1228 new.text
= g_utf8_collate_key(tmp
, -1);
1231 g_array_append_val(array
, new);
1234 g_array_append_val(array
, new);
1236 retval
->parts
= (CollatePart
*) array
->data
;
1237 g_array_free(array
, FALSE
);
1240 g_free(to_free
); /* Only taken for invalid UTF-8 */
1245 void collate_key_free(CollateKey
*key
)
1249 for (part
= key
->parts
; part
->text
; part
++)
1255 int collate_key_cmp(const CollateKey
*key1
, const CollateKey
*key2
,
1256 gboolean caps_first
)
1258 CollatePart
*n1
= key1
->parts
;
1259 CollatePart
*n2
= key2
->parts
;
1264 if (key1
->caps
&& !key2
->caps
)
1266 else if (key2
->caps
&& !key1
->caps
)
1273 return n2
->text
? -1 : 0;
1276 r
= strcmp(n1
->text
, n2
->text
);
1280 if (n1
->number
< n2
->number
)
1282 if (n1
->number
> n2
->number
)
1290 /* Returns TRUE if the object exists, FALSE if it doesn't.
1291 * For symlinks, the file pointed to must exist.
1293 gboolean
file_exists(const char *path
)
1297 return !mc_stat(path
, &info
);
1300 /* Escape path for future use in URI */
1301 EscapedPath
*escape_uri_path(const char *path
)
1303 const char *safe
= ":-_./"; /* Plus isalnum() */
1308 str
= g_string_sized_new(strlen(path
));
1310 for (s
= path
; *s
; s
++)
1312 if (!g_ascii_isalnum(*s
) && !strchr(safe
, *s
))
1313 g_string_append_printf(str
, "%%%02x", *s
);
1315 str
= g_string_append_c(str
, *s
);
1319 g_string_free(str
, FALSE
);
1321 return (EscapedPath
*) ans
;
1324 EscapedPath
*encode_path_as_uri(const guchar
*path
)
1326 gchar
*tpath
= (gchar
*) escape_uri_path(path
);
1329 uri
= g_strconcat("file://", our_host_name_for_dnd(), tpath
, NULL
);
1332 return (EscapedPath
*) uri
;
1335 gchar
*unescape_uri(const EscapedPath
*uri
)
1337 const char *uri_string
= (char *) uri
;
1342 tmp
= g_malloc(strlen(uri_string
) + 1);
1343 for (s
= uri_string
, d
= tmp
; *s
; s
++, d
++)
1345 /*printf("%s\n", s);*/
1346 if (*s
== '%' && g_ascii_isxdigit(s
[1]) &&
1347 g_ascii_isxdigit(s
[2]))
1354 c
= (int) strtol(buf
, NULL
, 16);
1366 /* Used as the sort function for sorting GPtrArrays */
1367 gint
strcmp2(gconstpointer a
, gconstpointer b
)
1369 const char *aa
= *(char **) a
;
1370 const char *bb
= *(char **) b
;
1372 return g_strcasecmp(aa
, bb
);
1375 /* Returns an array listing all the names in the directory 'path'.
1376 * The array is sorted.
1377 * '.' and '..' are skipped.
1378 * On error, the error is reported with g_warning and NULL is returned.
1380 GPtrArray
*list_dir(const guchar
*path
)
1383 GError
*error
= NULL
;
1387 dir
= g_dir_open(path
, 0, &error
);
1390 g_warning("Can't list directory:\n%s", error
->message
);
1391 g_error_free(error
);
1395 names
= g_ptr_array_new();
1397 while ((leaf
= g_dir_read_name(dir
))) {
1399 g_ptr_array_add(names
, g_strdup(leaf
));
1404 g_ptr_array_sort(names
, strcmp2
);
1409 int stat_with_timeout(const char *path
, struct stat
*info
)
1418 g_warning("stat_with_timeout: fork(): %s", g_strerror(errno
));
1426 _exit(mc_stat(path
, info
) ? 1 : 0);
1429 waitpid(child
, &status
, 0);
1432 retval
= mc_stat(path
, info
);
1440 static gchar
*my_strchrnul(const gchar
*str
, gchar c
)
1442 gchar
*p
= (gchar
*) str
;
1443 while (*p
&& (*p
!= c
))
1449 /* Based on code from glib. */
1450 gboolean
available_in_path(const char *file
)
1452 const gchar
*path
, *p
;
1453 gchar
*name
, *freeme
;
1456 gboolean found
= FALSE
;
1458 path
= g_getenv("PATH");
1460 path
= "/bin:/usr/bin:.";
1462 len
= strlen(file
) + 1;
1463 pathlen
= strlen(path
);
1464 freeme
= name
= g_malloc(pathlen
+ len
+ 1);
1466 /* Copy the file name at the top, including '\0' */
1467 memcpy(name
+ pathlen
+ 1, file
, len
);
1468 name
= name
+ pathlen
;
1469 /* And add the slash before the filename */
1478 p
= my_strchrnul(path
, ':');
1481 /* Two adjacent colons, or a colon at the beginning or
1482 * the end of `PATH' means to search the current
1487 startp
= memcpy (name
- (p
- path
), path
, p
- path
);
1489 /* Try to execute this name. If it works, execv will not
1492 if (access(startp
, X_OK
) == 0)
1494 } while (!found
&& *p
++ != '\0');
1501 static char *get_value_from_desktop_data(const char *data
, size_t length
,
1502 const char *section
, const char *key
,
1505 int section_length
, key_length
;
1506 const char *last_possible_key_start
;
1507 const char *last_possible_section_start
;
1510 section_length
= strlen(section
);
1511 last_possible_section_start
= data
+ length
- section_length
- 3;
1513 for (next
= data
; next
<= last_possible_section_start
; next
++)
1515 if (next
[0] != '[' || (next
> data
&& next
[-1] != '\n'))
1518 if (next
[section_length
] != ']' ||
1519 next
[section_length
+ 1] != '\n')
1521 if (strncmp(next
, section
, section_length
) != 0)
1523 next
+= section_length
+ 1; /* Newline */
1529 key_length
= strlen(key
);
1530 last_possible_key_start
= data
+ length
- key_length
- 3;
1532 next
&& next
< last_possible_key_start
;
1533 next
= memchr(next
+ 1, '\n', last_possible_key_start
- next
))
1537 if (next
[1] == '\n')
1538 continue; /* Cope with blank lines */
1540 next
++; /* Skip newline */
1543 break; /* End of section */
1545 if (strncmp(next
, key
, key_length
) != 0)
1548 while (next
[0] == ' ' || next
[0] == '\t')
1551 /* Note: if we had GLib 2.6, we could use
1552 * g_get_language_names() to get translations here.
1557 while (next
[0] == ' ' || next
[0] == '\t')
1559 nl
= memchr(next
, '\n', data
+ length
- next
);
1562 return g_strndup(next
, nl
- next
);
1568 /* Load .desktop file 'path' and return the value of the named key.
1569 * Sets error if the desktop file cannot be parsed.
1570 * Returns NULL (but does not set error) if the key is not present.
1571 * Returned string must be g_free()d.
1573 char *get_value_from_desktop_file(const char *path
,
1574 const char *section
,
1583 fd
= open(path
, O_RDONLY
);
1584 if (fd
== -1 || fstat(fd
, &info
) != 0)
1588 g_file_error_from_errno(errno
),
1589 _("Failed to open and stat file '%s': %s"),
1594 start
= mmap(NULL
, info
.st_size
, PROT_READ
, MAP_SHARED
, fd
, 0);
1595 if (start
== MAP_FAILED
)
1599 g_file_error_from_errno(errno
),
1600 _("Failed to mmap file '%s': %s"),
1606 value
= get_value_from_desktop_data((const char *) start
,
1614 if (start
!= NULL
&& start
!= MAP_FAILED
)
1615 munmap(start
, info
.st_size
);
1620 /* Load .desktop file 'path' and set the value of the named keys in the
1621 * NULL terminated list.
1622 * Sets error if the desktop file cannot be parsed and returns false.
1623 * Sets NULL (but does not set error) if the key is not present.
1624 * String set must be g_free()d.
1626 gboolean
get_values_from_desktop_file(const char *path
,
1628 const char *section
,
1637 fd
= open(path
, O_RDONLY
);
1638 if (fd
== -1 || fstat(fd
, &info
) != 0)
1642 g_file_error_from_errno(errno
),
1643 _("Failed to open and stat file '%s': %s"),
1648 start
= mmap(NULL
, info
.st_size
, PROT_READ
, MAP_SHARED
, fd
, 0);
1649 if (start
== MAP_FAILED
)
1653 g_file_error_from_errno(errno
),
1654 _("Failed to mmap file '%s': %s"),
1660 va_start(list
, value
);
1661 while(section
&& key
&& value
) {
1662 *value
= get_value_from_desktop_data((const char *) start
,
1669 section
=va_arg(list
, const char *);
1670 key
=va_arg(list
, const char *);
1671 value
=va_arg(list
, char **);
1678 if (start
!= NULL
&& start
!= MAP_FAILED
)
1679 munmap(start
, info
.st_size
);
1681 return (*error
==NULL
);
1685 * Build a command to execute with a supplied path. If the command pattern
1686 * given contains @c $1 it is substituted with the path, otherwise the path
1687 * is appended to the pattern after a space character
1688 * @param[in] cmd command pattern, with optional @c $1
1689 * @param[in] path path to substitute or append
1690 * @return generated command line, pass to g_free() when done.
1692 gchar
*build_command_with_path(const char *cmd
, const char *path
)
1697 for(subs
=cmd
; *subs
; subs
++)
1699 if(subs
[0]=='\\' && subs
[1]=='$')
1704 if(strncmp(subs
, "$1", 2)==0)
1708 int size
=strlen(cmd
)+strlen(path
)+1;
1709 result
= g_new(char, size
);
1710 strncpy(result
, cmd
, subs
-cmd
);
1711 result
[subs
-cmd
] = 0;
1712 strcat(result
, path
);
1713 strcat(result
, subs
+2);
1715 result
= g_strconcat(cmd
, " ", path
, NULL
);