r3196: Use apsymbols.h (from autopackage.org) if possible for binary compatibility
[rox-filer.git] / ROX-Filer / src / support.c
blobc15f23d26cc372a4d7eea820daa1a73592ebafc1
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2003, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* support.c - (non-GUI) useful routines */
24 #include "config.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <netdb.h>
29 #include <errno.h>
30 #include <ctype.h>
31 #include <sys/param.h>
32 #include <pwd.h>
33 #include <grp.h>
34 #include <fcntl.h>
35 #include <sys/wait.h>
36 #include <string.h>
37 #include <time.h>
38 #include <unistd.h>
39 #include <libxml/parser.h>
40 #include <math.h>
42 #include "global.h"
44 #include "choices.h"
45 #include "main.h"
46 #include "options.h"
47 #include "support.h"
48 #include "fscache.h"
49 #include "main.h"
50 #include "xml.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;
67 if (!xml_cache)
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)
77 return 1;
78 #else
79 FILE *out;
81 out = fopen(filename, "w");
82 if (!out)
83 return 1;
85 xmlDocDump(out, doc); /* Some versions return void */
87 if (fclose(out))
88 return 1;
89 #endif
91 return 0;
94 /* Create a new SOAP message and return the document and the (empty)
95 * body node.
97 xmlDocPtr soap_new(xmlNodePtr *ret_body)
99 xmlDocPtr doc;
100 xmlNodePtr root;
101 xmlNs *env_ns;
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");
113 return doc;
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;
138 if (!buffer)
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);
152 return buffer->str;
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)
159 return "";
160 return our_host_name();
163 /* Return our complete host name, unconditionally */
164 const char *our_host_name(void)
166 static char *name = NULL;
168 if (!name)
170 char buffer[4096];
172 if (gethostname(buffer, 4096) == 0)
174 /* gethostname doesn't always return the full name... */
175 struct hostent *ent;
177 buffer[4095] = '\0';
178 ent = gethostbyname(buffer);
179 name = g_strdup(ent ? ent->h_name : buffer);
181 else
183 g_warning("gethostname() failed - using localhost\n");
184 name = g_strdup("localhost");
188 return name;
191 void debug_free_string(void *data)
193 g_print("Freeing string '%s'\n", (char *) data);
194 g_free(data);
197 const char *user_name(uid_t uid)
199 const char *retval;
201 if (!uid_hash)
202 uid_hash = g_hash_table_new(NULL, NULL);
204 retval = g_hash_table_lookup(uid_hash, GINT_TO_POINTER(uid));
206 if (!retval)
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),
214 (gchar *) retval);
217 return retval;
220 const char *group_name(gid_t gid)
222 const char *retval;
224 if (!gid_hash)
225 gid_hash = g_hash_table_new(NULL, NULL);
227 retval = g_hash_table_lookup(gid_hash, GINT_TO_POINTER(gid));
229 if (!retval)
231 struct group *group;
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),
237 (gchar *) retval);
240 return retval;
243 /* Return a string in the form '23 M' in a static buffer valid until
244 * the next call.
246 const char *format_size(off_t size)
248 static char *buffer = NULL;
249 const char *units;
251 if (size >= PRETTY_SIZE_LIMIT)
253 size += 1023;
254 size >>= 10;
255 if (size >= PRETTY_SIZE_LIMIT)
257 size += 1023;
258 size >>= 10;
259 if (size >= PRETTY_SIZE_LIMIT)
261 size += 1023;
262 size >>= 10;
263 units = "G";
265 else
266 units = "M";
268 else
269 units = "K";
271 else
272 units = _("B");
274 g_free(buffer);
275 buffer = g_strdup_printf("%" SIZE_FMT " %s", size, units);
277 return buffer;
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;
286 char units;
288 if (size >= PRETTY_SIZE_LIMIT)
290 size += 1023;
291 size >>= 10;
292 if (size >= PRETTY_SIZE_LIMIT)
294 size += 1023;
295 size >>= 10;
296 if (size >= PRETTY_SIZE_LIMIT)
298 size += 1023;
299 size >>= 10;
300 units = 'G';
302 else
303 units = 'M';
305 else
306 units = 'K';
308 else
309 units = ' ';
311 g_free(buffer);
312 buffer = g_strdup_printf("%4" SIZE_FMT "%c", size, units);
314 return buffer;
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;
325 const char *units;
327 if (size >= PRETTY_SIZE_LIMIT)
329 size += 1023;
330 size /= 1024;
331 if (size >= PRETTY_SIZE_LIMIT)
333 size += 1023;
334 size /= 1024;
335 if (size >= PRETTY_SIZE_LIMIT)
337 size += 1023;
338 size /= 1024;
339 units = "G";
341 else
342 units = "M";
344 else
345 units = "K";
348 else if (size != 1)
349 units = _("bytes");
350 else
351 units = _("byte");
353 g_free(buf);
354 buf = g_strdup_printf("%.0f %s", floor(size), units);
356 return buf;
359 /* Fork and exec argv. Wait and return the child's exit status.
360 * -1 if spawn fails.
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)
368 int status;
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,
374 NULL, NULL,
375 NULL, &errors, &status, &error))
377 char *msg;
379 msg = g_strdup(error->message);
380 g_error_free(error);
381 return msg;
384 if (errors && !*errors)
385 null_g_free(&errors);
387 if (!WIFEXITED(status))
389 if (!errors)
390 errors = g_strdup("(Subprocess crashed?)");
392 else if (WEXITSTATUS(status))
394 if (!errors)
395 errors = g_strdup(_("ERROR"));
398 if (errors)
399 g_strstrip(errors);
401 return errors;
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)
409 int i;
411 if (uid == euid)
412 return 0;
414 if (gid == egid)
415 return 1;
417 for (i = 0; i < ngroups; i++)
419 if (supplemental_groups[i] == gid)
420 return 1;
423 return 2;
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"
432 #ifdef S_ISVTX
434 #endif
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' : '-';
451 #ifdef S_ISVTX
452 buffer[14] = m & S_ISVTX ? 'T' : '-';
453 #endif
455 return buffer;
458 /* Gets the canonical name for address and compares to our_host_name() */
459 static gboolean is_local_address(char *address)
461 struct hostent *ent;
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 points inside the input string.
470 * Possible formats:
471 * /path
472 * ///path
473 * //host/path
474 * file://host/path
476 const char *get_local_path(const char *uri)
478 if (*uri == '/')
480 char *path, *uri_host;
482 if (uri[1] != '/')
483 return uri; /* Just a local path - no host part */
485 path = strchr(uri + 2, '/');
486 if (!path)
487 return NULL; /* //something */
489 if (path - uri == 2)
490 return path; /* ///path */
492 uri_host = g_strndup(uri + 2, path - uri - 2);
493 if (is_local_address(uri_host))
495 g_free(uri_host);
496 return path; /* //myhost/path */
498 g_free(uri_host);
500 return NULL; /* From a different host */
502 else
504 if (strncasecmp(uri, "file:", 5))
505 return NULL; /* Don't know this format */
507 uri += 5;
509 if (*uri == '/')
510 return get_local_path(uri);
512 return NULL;
516 /* Set the close-on-exec flag for this FD.
517 * TRUE means that an exec()'d process will not get the FD.
519 void close_on_exec(int fd, gboolean close)
521 if (fcntl(fd, F_SETFD, close))
522 g_warning("fcntl() failed: %s\n", g_strerror(errno));
525 void set_blocking(int fd, gboolean blocking)
527 if (fcntl(fd, F_SETFL, blocking ? 0 : O_NONBLOCK))
528 g_warning("fcntl() failed: %s\n", g_strerror(errno));
531 /* Format this time nicely.
532 * g_free() the result.
534 char *pretty_time(const time_t *time)
536 char time_buf[32];
538 if (strftime(time_buf, sizeof(time_buf),
539 TIME_FORMAT, localtime(time)) == 0)
540 time_buf[0]= 0;
542 return to_utf8(time_buf);
545 #ifndef O_NOFOLLOW
546 # define O_NOFOLLOW 0x0
547 #endif
549 /* 'from' and 'to' are complete pathnames of files (not dirs or symlinks).
550 * This spawns 'cp' to do the copy if lstat() succeeds, otherwise we
551 * do the copy manually using vfs.
553 * Returns an error string, or NULL on success. g_free() the result.
555 * XXX: This was only used for libvfs...
557 guchar *copy_file(const guchar *from, const guchar *to)
559 const char *argv[] = {"cp", "-pRf", NULL, NULL, NULL};
561 argv[2] = from;
562 argv[3] = to;
564 return fork_exec_wait(argv);
567 /* 'word' has all special characters escaped so that it may be inserted
568 * into a shell command.
569 * Eg: 'My Dir?' becomes 'My\ Dir\?'. g_free() the result.
571 guchar *shell_escape(const guchar *word)
573 GString *tmp;
574 guchar *retval;
576 tmp = g_string_new(NULL);
578 while (*word)
580 if (strchr(" ?*['\"$~\\|();!`&", *word))
581 g_string_append_c(tmp, '\\');
582 g_string_append_c(tmp, *word);
583 word++;
586 retval = tmp->str;
587 g_string_free(tmp, FALSE);
588 return retval;
591 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
592 * (or the two are the same item/directory).
593 * FALSE if parent doesn't exist.
595 gboolean is_sub_dir(const char *sub_obj, const char *parent)
597 struct stat parent_info;
598 char *sub;
600 if (mc_lstat(parent, &parent_info))
601 return FALSE; /* Parent doesn't exist */
603 /* For checking Copy/Move operations do a realpath first on sub
604 * (the destination), since copying into a symlink is the same as
605 * copying into the thing it points to. Don't realpath 'parent' though;
606 * copying a symlink just makes a new symlink.
608 * When checking if an icon depends on a file (parent), use realpath on
609 * sub (the icon) too.
611 sub = pathdup(sub_obj);
613 while (1)
615 char *slash;
616 struct stat info;
618 if (mc_lstat(sub, &info) == 0)
620 if (info.st_dev == parent_info.st_dev &&
621 info.st_ino == parent_info.st_ino)
623 g_free(sub);
624 return TRUE;
628 slash = strrchr(sub, '/');
629 if (!slash)
630 break;
631 if (slash == sub)
633 if (sub[1])
634 sub[1] = '\0';
635 else
636 break;
638 else
639 *slash = '\0';
642 g_free(sub);
644 return FALSE;
647 /* True if the string 'list' contains 'item'.
648 * Eg ("close", "close, help") -> TRUE
650 gboolean in_list(const guchar *item, const guchar *list)
652 int len;
654 len = strlen(item);
656 while (*list)
658 if (strncmp(item, list, len) == 0 &&
659 !g_ascii_isalpha(list[len]))
660 return TRUE;
661 list = strchr(list, ',');
662 if (!list)
663 return FALSE;
664 while (g_ascii_isspace(*++list))
668 return FALSE;
671 /* Split a path into its components. Eg:
673 * /bob/fred -> ["bob", "fred"]
674 * ///a//b// -> ["a", "b"]
675 * / -> []
677 * The array and the strings in it must be freed after use.
679 GPtrArray *split_path(const guchar *path)
681 GPtrArray *array;
682 guchar *slash;
684 g_return_val_if_fail(path != NULL, NULL);
686 array = g_ptr_array_new();
688 while (1)
690 while (path[0] == '/')
691 path++;
692 if (path[0] == '\0')
693 break;
695 slash = strchr(path, '/');
696 if (slash)
698 g_ptr_array_add(array, g_strndup(path, slash - path));
699 path = slash + 1;
700 continue;
702 g_ptr_array_add(array, g_strdup(path));
703 break;
706 return array;
709 /* Return the shortest path from 'from' to 'to'.
710 * Eg: get_relative_path("/a/b/c", "a/d/e") -> "../d/e"
712 guchar *get_relative_path(const guchar *from, const guchar *to)
714 GString *path;
715 guchar *retval;
716 GPtrArray *src, *dst;
717 int i, j;
719 src = split_path(from);
720 dst = split_path(to);
722 /* The last component of src doesn't matter... */
723 if (src->len)
725 g_free(src->pdata[src->len - 1]);
726 g_ptr_array_remove_index(src, src->len - 1);
729 /* Strip off common path elements... */
730 i = 0;
731 while (i < src->len && i < dst->len)
733 guchar *a = (guchar *) src->pdata[i];
734 guchar *b = (guchar *) dst->pdata[i];
736 if (strcmp(a, b) != 0)
737 break;
738 i++;
741 /* Go up one dir for each element remaining in src */
742 path = g_string_new(NULL);
743 for (j = i; j < src->len; j++)
744 g_string_append(path, "../");
746 /* Go down one dir for each element remaining in dst */
747 for (j = i; j < dst->len; j++)
749 g_string_append(path, (guchar *) dst->pdata[j]);
750 g_string_append_c(path, '/');
753 if (path->str[path->len - 1] == '/')
754 g_string_truncate(path, path->len - 1);
755 if (path->len == 0)
756 g_string_assign(path, ".");
758 /* Free the arrays */
759 for (i = 0; i < src->len; i++)
760 g_free(src->pdata[i]);
761 g_ptr_array_free(src, TRUE);
762 for (i = 0; i < dst->len; i++)
763 g_free(dst->pdata[i]);
764 g_ptr_array_free(dst, TRUE);
766 retval = path->str;
767 g_string_free(path, FALSE);
769 return retval;
773 * Interperet text as a boolean value. Return defvalue if we don't
774 * recognise it
776 int text_to_boolean(const char *text, int defvalue)
778 if (g_strcasecmp(text, "true")==0)
779 return TRUE;
780 else if (g_strcasecmp(text, "false")==0)
781 return FALSE;
782 else if (g_strcasecmp(text, "yes")==0)
783 return TRUE;
784 else if (g_strcasecmp(text, "no")==0)
785 return FALSE;
786 else if (g_ascii_isdigit(text[0]))
787 return !!atoi(text);
789 return defvalue;
792 /* Return the pathname that this symlink points to.
793 * NULL on error (not a symlink, path too long) and errno set.
794 * g_free() the result.
796 char *readlink_dup(const char *source)
798 char path[MAXPATHLEN + 1];
799 int got;
801 got = readlink(source, path, MAXPATHLEN);
802 if (got < 0 || got > MAXPATHLEN)
803 return NULL;
805 return g_strndup(path, got);
809 * This code implements the MD5 message-digest algorithm.
810 * The algorithm is due to Ron Rivest. The original code was
811 * written by Colin Plumb in 1993, and put in the public domain.
813 * Modified to use glib datatypes. Put under GPL to simplify
814 * licensing for ROX-Filer. Taken from Debian's dpkg package.
817 #define md5byte unsigned char
819 typedef struct _MD5Context MD5Context;
821 struct _MD5Context {
822 guint32 buf[4];
823 guint32 bytes[2];
824 guint32 in[16];
827 #if G_BYTE_ORDER == G_BIG_ENDIAN
828 void byteSwap(guint32 *buf, unsigned words)
830 md5byte *p = (md5byte *)buf;
832 do {
833 *buf++ = (guint32)((unsigned)p[3] << 8 | p[2]) << 16 |
834 ((unsigned)p[1] << 8 | p[0]);
835 p += 4;
836 } while (--words);
838 #else
839 #define byteSwap(buf,words)
840 #endif
843 * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
844 * initialization constants.
846 static void MD5Init(MD5Context *ctx)
848 ctx->buf[0] = 0x67452301;
849 ctx->buf[1] = 0xefcdab89;
850 ctx->buf[2] = 0x98badcfe;
851 ctx->buf[3] = 0x10325476;
853 ctx->bytes[0] = 0;
854 ctx->bytes[1] = 0;
858 * Update context to reflect the concatenation of another buffer full
859 * of bytes.
861 static void MD5Update(MD5Context *ctx, md5byte const *buf, unsigned len)
863 guint32 t;
865 /* Update byte count */
867 t = ctx->bytes[0];
868 if ((ctx->bytes[0] = t + len) < t)
869 ctx->bytes[1]++; /* Carry from low to high */
871 t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */
872 if (t > len) {
873 memcpy((md5byte *)ctx->in + 64 - t, buf, len);
874 return;
876 /* First chunk is an odd size */
877 memcpy((md5byte *)ctx->in + 64 - t, buf, t);
878 byteSwap(ctx->in, 16);
879 MD5Transform(ctx->buf, ctx->in);
880 buf += t;
881 len -= t;
883 /* Process data in 64-byte chunks */
884 while (len >= 64) {
885 memcpy(ctx->in, buf, 64);
886 byteSwap(ctx->in, 16);
887 MD5Transform(ctx->buf, ctx->in);
888 buf += 64;
889 len -= 64;
892 /* Handle any remaining bytes of data. */
893 memcpy(ctx->in, buf, len);
897 * Final wrapup - pad to 64-byte boundary with the bit pattern
898 * 1 0* (64-bit count of bits processed, MSB-first)
899 * Returns the newly allocated string of the hash.
901 static char *MD5Final(MD5Context *ctx)
903 char *retval;
904 int i;
905 int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */
906 md5byte *p = (md5byte *)ctx->in + count;
907 guint8 *bytes;
909 /* Set the first char of padding to 0x80. There is always room. */
910 *p++ = 0x80;
912 /* Bytes of padding needed to make 56 bytes (-8..55) */
913 count = 56 - 1 - count;
915 if (count < 0) { /* Padding forces an extra block */
916 memset(p, 0, count + 8);
917 byteSwap(ctx->in, 16);
918 MD5Transform(ctx->buf, ctx->in);
919 p = (md5byte *)ctx->in;
920 count = 56;
922 memset(p, 0, count);
923 byteSwap(ctx->in, 14);
925 /* Append length in bits and transform */
926 ctx->in[14] = ctx->bytes[0] << 3;
927 ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29;
928 MD5Transform(ctx->buf, ctx->in);
930 byteSwap(ctx->buf, 4);
932 retval = g_malloc(33);
933 bytes = (guint8 *) ctx->buf;
934 for (i = 0; i < 16; i++)
935 sprintf(retval + (i * 2), "%02x", bytes[i]);
936 retval[32] = '\0';
938 return retval;
941 # ifndef ASM_MD5
943 /* The four core functions - F1 is optimized somewhat */
945 /* #define F1(x, y, z) (x & y | ~x & z) */
946 #define F1(x, y, z) (z ^ (x & (y ^ z)))
947 #define F2(x, y, z) F1(z, x, y)
948 #define F3(x, y, z) (x ^ y ^ z)
949 #define F4(x, y, z) (y ^ (x | ~z))
951 /* This is the central step in the MD5 algorithm. */
952 #define MD5STEP(f,w,x,y,z,in,s) \
953 (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
956 * The core of the MD5 algorithm, this alters an existing MD5 hash to
957 * reflect the addition of 16 longwords of new data. MD5Update blocks
958 * the data and converts bytes into longwords for this routine.
960 static void MD5Transform(guint32 buf[4], guint32 const in[16])
962 register guint32 a, b, c, d;
964 a = buf[0];
965 b = buf[1];
966 c = buf[2];
967 d = buf[3];
969 MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
970 MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
971 MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
972 MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
973 MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
974 MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
975 MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
976 MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
977 MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
978 MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
979 MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
980 MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
981 MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
982 MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
983 MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
984 MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
986 MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
987 MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
988 MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
989 MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
990 MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
991 MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
992 MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
993 MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
994 MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
995 MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
996 MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
997 MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
998 MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
999 MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
1000 MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
1001 MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
1003 MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
1004 MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
1005 MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
1006 MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
1007 MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
1008 MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
1009 MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
1010 MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
1011 MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
1012 MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
1013 MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
1014 MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
1015 MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
1016 MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
1017 MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
1018 MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
1020 MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
1021 MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
1022 MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
1023 MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
1024 MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
1025 MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
1026 MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
1027 MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
1028 MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
1029 MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
1030 MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
1031 MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
1032 MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
1033 MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
1034 MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
1035 MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
1037 buf[0] += a;
1038 buf[1] += b;
1039 buf[2] += c;
1040 buf[3] += d;
1043 # endif /* ASM_MD5 */
1045 char *md5_hash(const char *message)
1047 MD5Context ctx;
1049 MD5Init(&ctx);
1050 MD5Update(&ctx, message, strlen(message));
1051 return MD5Final(&ctx);
1054 /* Convert string 'src' from the current locale to UTF-8 */
1055 gchar *to_utf8(const gchar *src)
1057 gchar *retval;
1059 if (!src)
1060 return NULL;
1062 retval = g_locale_to_utf8(src, -1, NULL, NULL, NULL);
1063 if (!retval)
1064 retval = g_convert_with_fallback(src, -1, "utf-8", "iso-8859-1",
1065 "?", NULL, NULL, NULL);
1067 return retval ? retval : g_strdup(src);
1070 /* Ensure string at 'sp' is UTF-8. g_free() and replace by
1071 * UTF-8 version if not.
1073 void ensure_utf8(gchar **sp)
1075 gchar *s = *sp;
1077 if (!g_utf8_validate(s, -1, NULL))
1079 *sp = to_utf8(s);
1080 g_free(s);
1084 /* Removes trailing / chars and converts a leading '~/' (if any) to
1085 * the user's home dir. g_free() the result.
1087 gchar *expand_path(const gchar *path)
1089 guchar *retval;
1090 int path_len;
1092 g_return_val_if_fail(path != NULL, NULL);
1094 path_len = strlen(path);
1095 while (path_len > 1 && path[path_len - 1] == '/')
1096 path_len--;
1098 retval = g_strndup(path, path_len);
1100 if (path[0] == '~' && (path[1] == '\0' || path[1] == '/'))
1102 guchar *tmp = retval;
1104 retval = g_strconcat(home_dir, retval + 1, NULL);
1105 g_free(tmp);
1108 return retval;
1111 /* g_free() every element in the list, then free the list itself and
1112 * NULL the pointer to the list.
1114 void destroy_glist(GList **list)
1116 GList *l = *list;
1117 g_list_foreach(l, (GFunc) g_free, NULL);
1118 g_list_free(l);
1119 *list = NULL;
1122 void null_g_free(gpointer p)
1124 g_free(*(gpointer *)p);
1125 *(gpointer *)p = NULL;
1128 typedef struct _CollatePart CollatePart;
1130 struct _CollateKey {
1131 CollatePart *parts;
1132 gboolean caps;
1135 struct _CollatePart {
1136 guchar *text; /* NULL => end of list */
1137 long number;
1140 /* Break 'name' (a UTF-8 string) down into a list of (text, number) pairs.
1141 * The text parts processed for collating. This allows any two names to be
1142 * quickly compared later for intelligent sorting (comparing names is
1143 * speed-critical).
1145 CollateKey *collate_key_new(const guchar *name)
1147 const guchar *i;
1148 guchar *to_free = NULL;
1149 GArray *array;
1150 CollatePart new;
1151 CollateKey *retval;
1152 char *tmp;
1154 g_return_val_if_fail(name != NULL, NULL);
1156 array = g_array_new(FALSE, FALSE, sizeof(CollatePart));
1158 /* Ensure valid UTF-8 */
1159 if (!g_utf8_validate(name, -1, NULL))
1161 to_free = to_utf8(name);
1162 name = to_free;
1165 retval = g_new(CollateKey, 1);
1166 retval->caps = g_unichar_isupper(g_utf8_get_char(name));
1168 for (i = name; *i; i = g_utf8_next_char(i))
1170 /* We're in a (possibly blank) text section starting at 'name'.
1171 * Find the end of it (the next digit, or end of string).
1173 if (g_unichar_isdigit(g_utf8_get_char(i)))
1175 char *endp;
1177 /* i -> first digit character */
1178 tmp = g_utf8_strdown(name, i - name);
1179 new.text = g_utf8_collate_key(tmp, -1);
1180 g_free(tmp);
1181 new.number = strtol(i, &endp, 10);
1183 g_array_append_val(array, new);
1185 g_return_val_if_fail(endp > (char *) i, NULL);
1187 name = endp;
1188 i = name - 1;
1192 tmp = g_utf8_strdown(name, i - name);
1193 new.text = g_utf8_collate_key(tmp, -1);
1194 g_free(tmp);
1195 new.number = -1;
1196 g_array_append_val(array, new);
1198 new.text = NULL;
1199 g_array_append_val(array, new);
1201 retval->parts = (CollatePart *) array->data;
1202 g_array_free(array, FALSE);
1204 if (to_free)
1205 g_free(to_free); /* Only taken for invalid UTF-8 */
1207 return retval;
1210 void collate_key_free(CollateKey *key)
1212 CollatePart *part;
1214 for (part = key->parts; part->text; part++)
1215 g_free(part->text);
1216 g_free(key->parts);
1217 g_free(key);
1220 int collate_key_cmp(const CollateKey *key1, const CollateKey *key2,
1221 gboolean caps_first)
1223 CollatePart *n1 = key1->parts;
1224 CollatePart *n2 = key2->parts;
1225 int r;
1227 if (caps_first)
1229 if (key1->caps && !key2->caps)
1230 return -1;
1231 else if (key2->caps && !key1->caps)
1232 return 1;
1235 while (1)
1237 if (!n1->text)
1238 return n2->text ? -1 : 0;
1239 if (!n2->text)
1240 return 1;
1241 r = strcmp(n1->text, n2->text);
1242 if (r)
1243 return r;
1245 if (n1->number < n2->number)
1246 return -1;
1247 if (n1->number > n2->number)
1248 return 1;
1250 n1++;
1251 n2++;
1255 /* Returns TRUE if the object exists, FALSE if it doesn't.
1256 * For symlinks, the file pointed to must exist.
1258 gboolean file_exists(const char *path)
1260 struct stat info;
1262 return !mc_stat(path, &info);
1265 /* Escape path for future use in URI */
1266 gchar *escape_uri_path(const char *path)
1268 const char *safe = "-_./"; /* Plus isalnum() */
1269 const guchar *s;
1270 gchar *ans;
1271 GString *str;
1273 str = g_string_sized_new(strlen(path));
1275 for (s = path; *s; s++)
1277 if (!g_ascii_isalnum(*s) && !strchr(safe, *s))
1278 g_string_append_printf(str, "%%%02x", *s);
1279 else
1280 str = g_string_append_c(str, *s);
1283 ans = str->str;
1284 g_string_free(str, FALSE);
1286 return ans;
1289 gchar *encode_path_as_uri(const guchar *path)
1291 gchar *tpath = escape_uri_path(path);
1292 gchar *uri;
1294 uri = g_strconcat("file://", our_host_name_for_dnd(), tpath, NULL);
1295 g_free(tpath);
1297 return uri;
1300 gchar *unescape_uri(const char *uri)
1302 const gchar *s;
1303 gchar *d;
1304 gchar *tmp;
1306 tmp = g_malloc(strlen(uri) + 1);
1307 for (s = uri, d = tmp; *s; s++, d++)
1309 /*printf("%s\n", s);*/
1310 if (*s == '%' && g_ascii_isxdigit(s[1]) &&
1311 g_ascii_isxdigit(s[2]))
1313 int c;
1314 char buf[3];
1315 buf[0] = s[1];
1316 buf[1] = s[2];
1317 buf[2] = 0;
1318 c = (int) strtol(buf, NULL, 16);
1319 *d = c;
1320 s += 2;
1322 else
1323 *d = *s;
1325 *d = '\0';
1327 return tmp;
1330 /* Used as the sort function for sorting GPtrArrays */
1331 gint strcmp2(gconstpointer a, gconstpointer b)
1333 const char *aa = *(char **) a;
1334 const char *bb = *(char **) b;
1336 return g_strcasecmp(aa, bb);
1339 /* Returns an array listing all the names in the directory 'path'.
1340 * The array is sorted.
1341 * '.' and '..' are skipped.
1342 * On error, the error is reported with g_warning and NULL is returned.
1344 GPtrArray *list_dir(const guchar *path)
1346 GDir *dir;
1347 GError *error = NULL;
1348 const char *leaf;
1349 GPtrArray *names;
1351 dir = g_dir_open(path, 0, &error);
1352 if (error)
1354 g_warning("Can't list directory:\n%s", error->message);
1355 g_error_free(error);
1356 return NULL;
1359 names = g_ptr_array_new();
1361 while ((leaf = g_dir_read_name(dir))) {
1362 if (leaf[0] != '.')
1363 g_ptr_array_add(names, g_strdup(leaf));
1366 g_dir_close(dir);
1368 g_ptr_array_sort(names, strcmp2);
1370 return names;