4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, 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)
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
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 */
31 #include <sys/param.h>
51 # include "gconvert.h"
54 static GHashTable
*uid_hash
= NULL
; /* UID -> User name */
55 static GHashTable
*gid_hash
= NULL
; /* GID -> Group name */
57 /* Static prototypes */
58 #if defined(GTK2) || defined(THUMBS_USE_LIBPNG)
59 static void MD5Transform(guint32 buf
[4], guint32
const in
[16]);
62 /* Static prototypes */
63 static XMLwrapper
*xml_load(char *pathname
, gpointer data
);
64 static void xml_ref(XMLwrapper
*dir
, gpointer data
);
65 static void xml_unref(XMLwrapper
*dir
, gpointer data
);
66 static int xml_getref(XMLwrapper
*dir
, gpointer data
);
68 /****************************************************************
69 * EXTERNAL INTERFACE *
70 ****************************************************************/
72 XMLwrapper
*xml_cache_load(gchar
*pathname
)
74 static GFSCache
*xml_cache
= NULL
;
77 xml_cache
= g_fscache_new((GFSLoadFunc
) xml_load
,
79 (GFSRefFunc
) xml_unref
,
80 (GFSGetRefFunc
) xml_getref
,
82 return g_fscache_lookup(xml_cache
, pathname
);
85 void xml_cache_unref(XMLwrapper
*wrapper
)
87 xml_unref(wrapper
, NULL
);
90 /* Return the (first) child of this node with the given name.
93 xmlNode
*get_subnode(xmlNode
*node
, const char *namespaceURI
, const char *name
)
95 for (node
= node
->xmlChildrenNode
; node
; node
= node
->next
)
97 if (node
->type
!= XML_ELEMENT_NODE
)
100 if (strcmp(node
->name
, name
))
103 if (node
->ns
== NULL
|| namespaceURI
== NULL
)
105 if (node
->ns
== NULL
&& namespaceURI
== NULL
)
110 if (strcmp(node
->ns
->href
, namespaceURI
) == 0)
117 /* Save doc as XML as filename, 0 on success or -1 on failure */
118 int save_xml_file(xmlDocPtr doc
, gchar
*filename
)
120 #if LIBXML_VERSION > 20400
121 if (xmlSaveFormatFileEnc(filename
, doc
, NULL
, 1) < 0)
126 out
= fopen(filename
, "w");
130 xmlDocDump(out
, doc
); /* Some versions return void */
139 /* Create a new SOAP message and return the document and the (empty)
142 xmlDocPtr
soap_new(xmlNodePtr
*ret_body
)
148 doc
= xmlNewDoc("1.0");
149 root
= xmlNewDocNode(doc
, NULL
, "Envelope", NULL
);
150 xmlDocSetRootElement(doc
, root
);
152 env_ns
= xmlNewNs(root
, SOAP_ENV_NS
, "env");
153 xmlSetNs(root
, env_ns
);
155 *ret_body
= xmlNewTextChild(root
, env_ns
, "Body", NULL
);
156 xmlNewNs(*ret_body
, ROX_NS
, "rox");
161 /* Like g_strdup, but does realpath() too (if possible) */
162 char *pathdup(char *path
)
164 char real
[MAXPATHLEN
];
166 g_return_val_if_fail(path
!= NULL
, NULL
);
168 if (realpath(path
, real
))
169 return g_strdup(real
);
171 return g_strdup(path
);
174 /* Join the path to the leaf (adding a / between them) and
175 * return a pointer to a buffer with the result. Buffer is valid until
176 * the next call to make_path.
178 GString
*make_path(char *dir
, char *leaf
)
180 static GString
*buffer
= NULL
;
183 buffer
= g_string_new(NULL
);
185 g_return_val_if_fail(dir
!= NULL
, buffer
);
186 g_return_val_if_fail(leaf
!= NULL
, buffer
);
188 g_string_sprintf(buffer
, "%s%s%s",
190 dir
[0] == '/' && dir
[1] == '\0' ? "" : "/",
196 /* Return our complete host name for DND */
197 char *our_host_name_for_dnd(void)
199 if (option_get_int("dnd_no_hostnames"))
201 return our_host_name();
204 /* Return our complete host name, unconditionally */
205 char *our_host_name(void)
207 static char *name
= NULL
;
213 if (gethostname(buffer
, 4096) == 0)
215 /* gethostname doesn't always return the full name... */
219 ent
= gethostbyname(buffer
);
220 name
= g_strdup(ent
? ent
->h_name
: buffer
);
224 g_warning("gethostname() failed - using localhost\n");
225 name
= g_strdup("localhost");
232 /* Create a child process. cd to dir first (if dir is non-NULL).
233 * If from_stderr is set, create a pipe for stderr and return the readable
235 * Returns the PID of the child, or 0 on failure (from_stderr is still valid).
237 pid_t
spawn_full(char **argv
, char *dir
, int *from_stderr
)
245 *from_stderr
= fd
[0];
256 return 0; /* Failure */
259 /* We are the child process */
263 if (fd
[1] != STDERR_FILENO
)
265 dup2(fd
[1], STDERR_FILENO
);
267 close_on_exec(STDERR_FILENO
, FALSE
);
273 fprintf(stderr
, "chdir() failed: %s\n",
275 execvp(argv
[0], argv
);
276 fprintf(stderr
, "execvp(%s, ...) failed: %s\n",
285 /* We are the parent */
289 void debug_free_string(void *data
)
291 g_print("Freeing string '%s'\n", (char *) data
);
295 char *user_name(uid_t uid
)
300 uid_hash
= g_hash_table_new(NULL
, NULL
);
302 retval
= g_hash_table_lookup(uid_hash
, GINT_TO_POINTER(uid
));
306 struct passwd
*passwd
;
308 passwd
= getpwuid(uid
);
309 retval
= passwd
? g_strdup(passwd
->pw_name
)
310 : g_strdup_printf("[%d]", (int) uid
);
311 g_hash_table_insert(uid_hash
, GINT_TO_POINTER(uid
), retval
);
317 char *group_name(gid_t gid
)
322 gid_hash
= g_hash_table_new(NULL
, NULL
);
324 retval
= g_hash_table_lookup(gid_hash
, GINT_TO_POINTER(gid
));
330 group
= getgrgid(gid
);
331 retval
= group
? g_strdup(group
->gr_name
)
332 : g_strdup_printf("[%d]", (int) gid
);
333 g_hash_table_insert(gid_hash
, GINT_TO_POINTER(gid
), retval
);
339 /* Return a string in the form '23Mb' in a static buffer valid until
342 char *format_size(off_t size
)
344 static char *buffer
= NULL
;
347 if (size
>= PRETTY_SIZE_LIMIT
)
351 if (size
>= PRETTY_SIZE_LIMIT
)
355 if (size
>= PRETTY_SIZE_LIMIT
)
374 buffer
= g_strdup_printf("%" SIZE_FMT
" %s", size
, units
);
379 /* Return a string in the form '23Mb' in a static buffer valid until
380 * the next call. Aligned to the right (5 chars).
382 char *format_size_aligned(off_t size
)
384 static char *buffer
= NULL
;
387 if (size
>= PRETTY_SIZE_LIMIT
)
391 if (size
>= PRETTY_SIZE_LIMIT
)
395 if (size
>= PRETTY_SIZE_LIMIT
)
413 buffer
= g_strdup_printf("%4" SIZE_FMT
"%c", size
, units
);
419 * Similar to format_size(), but this one uses a double argument since
420 * unsigned long isn't wide enough on all platforms and we must be able to
421 * sum sizes above 4 GB.
423 gchar
*format_double_size(double size
)
425 static gchar
*buf
= NULL
;
428 if (size
>= PRETTY_SIZE_LIMIT
)
432 if (size
>= PRETTY_SIZE_LIMIT
)
436 if (size
>= PRETTY_SIZE_LIMIT
)
456 buf
= g_strdup_printf("%.0f %s", size
, units
);
461 /* Ensure that str ends with a newline (or is empty) */
462 static void newline(GString
*str
)
464 if (str
->len
&& str
->str
[str
->len
- 1] != '\n')
465 g_string_append_c(str
, '\n');
468 /* Fork and exec argv. Wait and return the child's exit status.
470 * Returns the error string from the command if any, or NULL on success.
471 * If the process returns a non-zero exit status without producing a message,
472 * a suitable message is created.
473 * g_free() the result.
475 char *fork_exec_wait(char **argv
)
483 errors
= g_string_new(NULL
);
485 child
= spawn_full(argv
, NULL
, &from_stderr
);
490 g_string_append(errors
, "fork: ");
491 g_string_append(errors
, g_strerror(errno
));
495 while (from_stderr
!= -1)
499 got
= read(from_stderr
, buffer
, sizeof(buffer
) - 1);
503 g_string_append(errors
, "read: ");
504 g_string_append(errors
, g_strerror(errno
));
509 g_string_append(errors
, buffer
);
514 if (waitpid(child
, &status
, 0) == -1)
519 g_string_append(errors
, "waitpid: ");
520 g_string_append(errors
, g_strerror(errno
));
526 if (!WIFEXITED(status
))
529 g_string_append(errors
, "(crashed?)");
531 else if (WEXITSTATUS(status
))
535 g_string_append(errors
, "ERROR");
542 if (from_stderr
!= -1)
545 if (errors
->len
&& errors
->str
[errors
->len
- 1] == '\n')
546 g_string_truncate(errors
, errors
->len
- 1);
550 char *retval
= errors
->str
;
551 g_string_free(errors
, FALSE
);
558 /* If a file has this UID and GID, which permissions apply to us?
559 * 0 = User, 1 = Group, 2 = World
561 gint
applicable(uid_t uid
, gid_t gid
)
571 for (i
= 0; i
< ngroups
; i
++)
573 if (supplemental_groups
[i
] == gid
)
580 /* Converts a file's mode to a string. Result is a pointer
581 * to a static buffer, valid until the next call.
583 char *pretty_permissions(mode_t m
)
585 static char buffer
[] = "rwx,rwx,rwx/UGT";
587 buffer
[0] = m
& S_IRUSR
? 'r' : '-';
588 buffer
[1] = m
& S_IWUSR
? 'w' : '-';
589 buffer
[2] = m
& S_IXUSR
? 'x' : '-';
591 buffer
[4] = m
& S_IRGRP
? 'r' : '-';
592 buffer
[5] = m
& S_IWGRP
? 'w' : '-';
593 buffer
[6] = m
& S_IXGRP
? 'x' : '-';
595 buffer
[8] = m
& S_IROTH
? 'r' : '-';
596 buffer
[9] = m
& S_IWOTH
? 'w' : '-';
597 buffer
[10] = m
& S_IXOTH
? 'x' : '-';
599 buffer
[12] = m
& S_ISUID
? 'U' : '-';
600 buffer
[13] = m
& S_ISGID
? 'G' : '-';
602 buffer
[14] = m
& S_ISVTX
? 'T' : '-';
611 /* Gets the canonical name for address and compares to our_host_name() */
612 static gboolean
is_local_address(char *address
)
616 ent
= gethostbyname(address
);
618 return strcmp(our_host_name(), ent
? ent
->h_name
: address
) == 0;
621 /* Convert a URI to a local pathname (or NULL if it isn't local).
622 * The returned pointer points inside the input string.
629 char *get_local_path(char *uri
)
633 char *path
, *uri_host
;
636 return uri
; /* Just a local path - no host part */
638 path
= strchr(uri
+ 2, '/');
640 return NULL
; /* //something */
643 return path
; /* ///path */
645 uri_host
= g_strndup(uri
+ 2, path
- uri
- 2);
646 if (is_local_address(uri_host
))
649 return path
; /* //myhost/path */
653 return NULL
; /* From a different host */
657 if (strncasecmp(uri
, "file:", 5))
658 return NULL
; /* Don't know this format */
663 return get_local_path(uri
);
669 /* Set the close-on-exec flag for this FD.
670 * TRUE means that an exec()'d process will not get the FD.
672 void close_on_exec(int fd
, gboolean close
)
674 if (fcntl(fd
, F_SETFD
, close
))
675 g_warning("fcntl() failed: %s\n", g_strerror(errno
));
678 void set_blocking(int fd
, gboolean blocking
)
680 if (fcntl(fd
, F_SETFL
, blocking
? 0 : O_NONBLOCK
))
681 g_warning("fcntl() failed: %s\n", g_strerror(errno
));
684 /* Format this time nicely. The result is a pointer to a static buffer,
685 * valid until the next call.
687 char *pretty_time(time_t *time
)
689 static char time_buf
[32];
691 if (strftime(time_buf
, sizeof(time_buf
),
692 TIME_FORMAT
, localtime(time
)) == 0)
699 # define O_NOFOLLOW 0x0
703 /* Copy data from 'read_fd' FD to 'write_fd' FD until EOF or error.
704 * Returns 0 on success, -1 on error (and sets errno).
706 static int copy_fd(int read_fd
, int write_fd
)
711 while ((got
= mc_read(read_fd
, buffer
, sizeof(buffer
))) > 0)
719 c
= mc_write(write_fd
, buffer
+ sent
, got
- sent
);
730 /* 'from' and 'to' are complete pathnames of files (not dirs or symlinks).
731 * This spawns 'cp' to do the copy if lstat() succeeds, otherwise we
732 * do the copy manually using vfs.
734 * Returns an error string, or NULL on success. g_free() the result.
736 guchar
*copy_file(guchar
*from
, guchar
*to
)
738 char *argv
[] = {"cp", "-pRf", NULL
, NULL
, NULL
};
743 if (lstat(from
, &info
))
749 if (mc_lstat(from
, &info
))
750 return g_strdup(g_strerror(errno
));
752 /* Regular lstat() can't find it, but mc_lstat() can,
753 * so try reading it with VFS.
756 if (!S_ISREG(info
.st_mode
))
759 read_fd
= mc_open(from
, O_RDONLY
);
762 write_fd
= mc_open(to
,
763 O_NOFOLLOW
| O_WRONLY
| O_CREAT
| O_TRUNC
,
764 info
.st_mode
& 0777);
768 if (copy_fd(read_fd
, write_fd
))
771 /* (yes, the single | is right) */
772 if (mc_close(read_fd
) | mc_close(write_fd
))
773 return g_strdup(g_strerror(errno
));
781 return g_strdup(error
? g_strerror(error
) : _("Copy error"));
788 return fork_exec_wait(argv
);
791 /* 'word' has all special characters escaped so that it may be inserted
792 * into a shell command.
793 * Eg: 'My Dir?' becomes 'My\ Dir\?'. g_free() the result.
795 guchar
*shell_escape(guchar
*word
)
800 tmp
= g_string_new(NULL
);
804 if (strchr(" ?*['\"$~\\|();!`&", *word
))
805 g_string_append_c(tmp
, '\\');
806 g_string_append_c(tmp
, *word
);
811 g_string_free(tmp
, FALSE
);
815 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
816 * (or the two are the same item/directory).
817 * FALSE if parent doesn't exist.
819 gboolean
is_sub_dir(char *sub
, char *parent
)
821 struct stat parent_info
;
823 if (mc_lstat(parent
, &parent_info
))
824 return FALSE
; /* Parent doesn't exist */
826 /* For checking Copy/Move operations do a realpath first on sub
827 * (the destination), since copying into a symlink is the same as
828 * copying into the thing it points to. Don't realpath 'parent' though;
829 * copying a symlink just makes a new symlink.
831 * When checking if an icon depends on a file (parent), use realpath on
832 * sub (the icon) too.
841 if (mc_lstat(sub
, &info
) == 0)
843 if (info
.st_dev
== parent_info
.st_dev
&&
844 info
.st_ino
== parent_info
.st_ino
)
851 slash
= strrchr(sub
, '/');
870 /* True if the string 'list' contains 'item'.
871 * Eg ("close", "close, help") -> TRUE
873 gboolean
in_list(guchar
*item
, guchar
*list
)
881 if (strncmp(item
, list
, len
) == 0 && !isalpha(list
[len
]))
883 list
= strchr(list
, ',');
886 while (isspace(*++list
))
893 /* Split a path into its components. Eg:
895 * /bob/fred -> ["bob", "fred"]
896 * ///a//b// -> ["a", "b"]
899 * The array and the strings in it must be freed after use.
901 GPtrArray
*split_path(guchar
*path
)
906 g_return_val_if_fail(path
!= NULL
, NULL
);
908 array
= g_ptr_array_new();
912 while (path
[0] == '/')
917 slash
= strchr(path
, '/');
920 g_ptr_array_add(array
, g_strndup(path
, slash
- path
));
924 g_ptr_array_add(array
, g_strdup(path
));
931 /* Return the shortest path from 'from' to 'to'.
932 * Eg: get_relative_path("/a/b/c", "a/d/e") -> "../d/e"
934 guchar
*get_relative_path(guchar
*from
, guchar
*to
)
938 GPtrArray
*src
, *dst
;
941 src
= split_path(from
);
942 dst
= split_path(to
);
944 /* The last component of src doesn't matter... */
947 g_free(src
->pdata
[src
->len
- 1]);
948 g_ptr_array_remove_index(src
, src
->len
- 1);
951 /* Strip off common path elements... */
953 while (i
< src
->len
&& i
< dst
->len
)
955 guchar
*a
= (guchar
*) src
->pdata
[i
];
956 guchar
*b
= (guchar
*) dst
->pdata
[i
];
958 if (strcmp(a
, b
) != 0)
963 /* Go up one dir for each element remaining in src */
964 path
= g_string_new(NULL
);
965 for (j
= i
; j
< src
->len
; j
++)
966 g_string_append(path
, "../");
968 /* Go down one dir for each element remaining in dst */
969 for (j
= i
; j
< dst
->len
; j
++)
971 g_string_append(path
, (guchar
*) dst
->pdata
[j
]);
972 g_string_append_c(path
, '/');
975 if (path
->str
[path
->len
- 1] == '/')
976 g_string_truncate(path
, path
->len
- 1);
978 g_string_assign(path
, ".");
980 /* Free the arrays */
981 for (i
= 0; i
< src
->len
; i
++)
982 g_free(src
->pdata
[i
]);
983 g_ptr_array_free(src
, TRUE
);
984 for (i
= 0; i
< dst
->len
; i
++)
985 g_free(dst
->pdata
[i
]);
986 g_ptr_array_free(dst
, TRUE
);
989 g_string_free(path
, FALSE
);
995 * Interperet text as a boolean value. Return defvalue if we don't
998 int text_to_boolean(const char *text
, int defvalue
)
1000 if(g_strcasecmp(text
, "true")==0)
1002 else if(g_strcasecmp(text
, "false")==0)
1004 else if(g_strcasecmp(text
, "yes")==0)
1006 else if(g_strcasecmp(text
, "no")==0)
1008 else if(isdigit(text
[0]))
1009 return !!atoi(text
);
1014 void set_to_null(gpointer
*data
)
1019 /* Return the pathname that this symlink points to.
1020 * NULL on error (not a symlink, path too long) and errno set.
1021 * g_free() the result.
1023 char *readlink_dup(char *source
)
1025 char path
[MAXPATHLEN
+ 1];
1028 got
= readlink(source
, path
, MAXPATHLEN
);
1029 if (got
< 0 || got
> MAXPATHLEN
)
1032 return g_strndup(path
, got
);
1035 #if defined(GTK2) || defined(THUMBS_USE_LIBPNG)
1037 * This code implements the MD5 message-digest algorithm.
1038 * The algorithm is due to Ron Rivest. The original code was
1039 * written by Colin Plumb in 1993, and put in the public domain.
1041 * Modified to use glib datatypes. Put under GPL to simplify
1042 * licensing for ROX-Filer. Taken from Debian's dpkg package.
1045 #define md5byte unsigned char
1047 typedef struct _MD5Context MD5Context
;
1049 struct _MD5Context
{
1055 #if G_BYTE_ORDER == G_BIG_ENDIAN
1056 void byteSwap(guint32
*buf
, unsigned words
)
1058 md5byte
*p
= (md5byte
*)buf
;
1061 *buf
++ = (guint32
)((unsigned)p
[3] << 8 | p
[2]) << 16 |
1062 ((unsigned)p
[1] << 8 | p
[0]);
1067 #define byteSwap(buf,words)
1071 * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
1072 * initialization constants.
1074 static void MD5Init(MD5Context
*ctx
)
1076 ctx
->buf
[0] = 0x67452301;
1077 ctx
->buf
[1] = 0xefcdab89;
1078 ctx
->buf
[2] = 0x98badcfe;
1079 ctx
->buf
[3] = 0x10325476;
1086 * Update context to reflect the concatenation of another buffer full
1089 static void MD5Update(MD5Context
*ctx
, md5byte
const *buf
, unsigned len
)
1093 /* Update byte count */
1096 if ((ctx
->bytes
[0] = t
+ len
) < t
)
1097 ctx
->bytes
[1]++; /* Carry from low to high */
1099 t
= 64 - (t
& 0x3f); /* Space available in ctx->in (at least 1) */
1101 memcpy((md5byte
*)ctx
->in
+ 64 - t
, buf
, len
);
1104 /* First chunk is an odd size */
1105 memcpy((md5byte
*)ctx
->in
+ 64 - t
, buf
, t
);
1106 byteSwap(ctx
->in
, 16);
1107 MD5Transform(ctx
->buf
, ctx
->in
);
1111 /* Process data in 64-byte chunks */
1113 memcpy(ctx
->in
, buf
, 64);
1114 byteSwap(ctx
->in
, 16);
1115 MD5Transform(ctx
->buf
, ctx
->in
);
1120 /* Handle any remaining bytes of data. */
1121 memcpy(ctx
->in
, buf
, len
);
1125 * Final wrapup - pad to 64-byte boundary with the bit pattern
1126 * 1 0* (64-bit count of bits processed, MSB-first)
1127 * Returns the newly allocated string of the hash.
1129 static char *MD5Final(MD5Context
*ctx
)
1133 int count
= ctx
->bytes
[0] & 0x3f; /* Number of bytes in ctx->in */
1134 md5byte
*p
= (md5byte
*)ctx
->in
+ count
;
1137 /* Set the first char of padding to 0x80. There is always room. */
1140 /* Bytes of padding needed to make 56 bytes (-8..55) */
1141 count
= 56 - 1 - count
;
1143 if (count
< 0) { /* Padding forces an extra block */
1144 memset(p
, 0, count
+ 8);
1145 byteSwap(ctx
->in
, 16);
1146 MD5Transform(ctx
->buf
, ctx
->in
);
1147 p
= (md5byte
*)ctx
->in
;
1150 memset(p
, 0, count
);
1151 byteSwap(ctx
->in
, 14);
1153 /* Append length in bits and transform */
1154 ctx
->in
[14] = ctx
->bytes
[0] << 3;
1155 ctx
->in
[15] = ctx
->bytes
[1] << 3 | ctx
->bytes
[0] >> 29;
1156 MD5Transform(ctx
->buf
, ctx
->in
);
1158 byteSwap(ctx
->buf
, 4);
1160 retval
= g_malloc(33);
1161 bytes
= (guint8
*) ctx
->buf
;
1162 for (i
= 0; i
< 16; i
++)
1163 sprintf(retval
+ (i
* 2), "%02x", bytes
[i
]);
1171 /* The four core functions - F1 is optimized somewhat */
1173 /* #define F1(x, y, z) (x & y | ~x & z) */
1174 #define F1(x, y, z) (z ^ (x & (y ^ z)))
1175 #define F2(x, y, z) F1(z, x, y)
1176 #define F3(x, y, z) (x ^ y ^ z)
1177 #define F4(x, y, z) (y ^ (x | ~z))
1179 /* This is the central step in the MD5 algorithm. */
1180 #define MD5STEP(f,w,x,y,z,in,s) \
1181 (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
1184 * The core of the MD5 algorithm, this alters an existing MD5 hash to
1185 * reflect the addition of 16 longwords of new data. MD5Update blocks
1186 * the data and converts bytes into longwords for this routine.
1188 static void MD5Transform(guint32 buf
[4], guint32
const in
[16])
1190 register guint32 a
, b
, c
, d
;
1197 MD5STEP(F1
, a
, b
, c
, d
, in
[0] + 0xd76aa478, 7);
1198 MD5STEP(F1
, d
, a
, b
, c
, in
[1] + 0xe8c7b756, 12);
1199 MD5STEP(F1
, c
, d
, a
, b
, in
[2] + 0x242070db, 17);
1200 MD5STEP(F1
, b
, c
, d
, a
, in
[3] + 0xc1bdceee, 22);
1201 MD5STEP(F1
, a
, b
, c
, d
, in
[4] + 0xf57c0faf, 7);
1202 MD5STEP(F1
, d
, a
, b
, c
, in
[5] + 0x4787c62a, 12);
1203 MD5STEP(F1
, c
, d
, a
, b
, in
[6] + 0xa8304613, 17);
1204 MD5STEP(F1
, b
, c
, d
, a
, in
[7] + 0xfd469501, 22);
1205 MD5STEP(F1
, a
, b
, c
, d
, in
[8] + 0x698098d8, 7);
1206 MD5STEP(F1
, d
, a
, b
, c
, in
[9] + 0x8b44f7af, 12);
1207 MD5STEP(F1
, c
, d
, a
, b
, in
[10] + 0xffff5bb1, 17);
1208 MD5STEP(F1
, b
, c
, d
, a
, in
[11] + 0x895cd7be, 22);
1209 MD5STEP(F1
, a
, b
, c
, d
, in
[12] + 0x6b901122, 7);
1210 MD5STEP(F1
, d
, a
, b
, c
, in
[13] + 0xfd987193, 12);
1211 MD5STEP(F1
, c
, d
, a
, b
, in
[14] + 0xa679438e, 17);
1212 MD5STEP(F1
, b
, c
, d
, a
, in
[15] + 0x49b40821, 22);
1214 MD5STEP(F2
, a
, b
, c
, d
, in
[1] + 0xf61e2562, 5);
1215 MD5STEP(F2
, d
, a
, b
, c
, in
[6] + 0xc040b340, 9);
1216 MD5STEP(F2
, c
, d
, a
, b
, in
[11] + 0x265e5a51, 14);
1217 MD5STEP(F2
, b
, c
, d
, a
, in
[0] + 0xe9b6c7aa, 20);
1218 MD5STEP(F2
, a
, b
, c
, d
, in
[5] + 0xd62f105d, 5);
1219 MD5STEP(F2
, d
, a
, b
, c
, in
[10] + 0x02441453, 9);
1220 MD5STEP(F2
, c
, d
, a
, b
, in
[15] + 0xd8a1e681, 14);
1221 MD5STEP(F2
, b
, c
, d
, a
, in
[4] + 0xe7d3fbc8, 20);
1222 MD5STEP(F2
, a
, b
, c
, d
, in
[9] + 0x21e1cde6, 5);
1223 MD5STEP(F2
, d
, a
, b
, c
, in
[14] + 0xc33707d6, 9);
1224 MD5STEP(F2
, c
, d
, a
, b
, in
[3] + 0xf4d50d87, 14);
1225 MD5STEP(F2
, b
, c
, d
, a
, in
[8] + 0x455a14ed, 20);
1226 MD5STEP(F2
, a
, b
, c
, d
, in
[13] + 0xa9e3e905, 5);
1227 MD5STEP(F2
, d
, a
, b
, c
, in
[2] + 0xfcefa3f8, 9);
1228 MD5STEP(F2
, c
, d
, a
, b
, in
[7] + 0x676f02d9, 14);
1229 MD5STEP(F2
, b
, c
, d
, a
, in
[12] + 0x8d2a4c8a, 20);
1231 MD5STEP(F3
, a
, b
, c
, d
, in
[5] + 0xfffa3942, 4);
1232 MD5STEP(F3
, d
, a
, b
, c
, in
[8] + 0x8771f681, 11);
1233 MD5STEP(F3
, c
, d
, a
, b
, in
[11] + 0x6d9d6122, 16);
1234 MD5STEP(F3
, b
, c
, d
, a
, in
[14] + 0xfde5380c, 23);
1235 MD5STEP(F3
, a
, b
, c
, d
, in
[1] + 0xa4beea44, 4);
1236 MD5STEP(F3
, d
, a
, b
, c
, in
[4] + 0x4bdecfa9, 11);
1237 MD5STEP(F3
, c
, d
, a
, b
, in
[7] + 0xf6bb4b60, 16);
1238 MD5STEP(F3
, b
, c
, d
, a
, in
[10] + 0xbebfbc70, 23);
1239 MD5STEP(F3
, a
, b
, c
, d
, in
[13] + 0x289b7ec6, 4);
1240 MD5STEP(F3
, d
, a
, b
, c
, in
[0] + 0xeaa127fa, 11);
1241 MD5STEP(F3
, c
, d
, a
, b
, in
[3] + 0xd4ef3085, 16);
1242 MD5STEP(F3
, b
, c
, d
, a
, in
[6] + 0x04881d05, 23);
1243 MD5STEP(F3
, a
, b
, c
, d
, in
[9] + 0xd9d4d039, 4);
1244 MD5STEP(F3
, d
, a
, b
, c
, in
[12] + 0xe6db99e5, 11);
1245 MD5STEP(F3
, c
, d
, a
, b
, in
[15] + 0x1fa27cf8, 16);
1246 MD5STEP(F3
, b
, c
, d
, a
, in
[2] + 0xc4ac5665, 23);
1248 MD5STEP(F4
, a
, b
, c
, d
, in
[0] + 0xf4292244, 6);
1249 MD5STEP(F4
, d
, a
, b
, c
, in
[7] + 0x432aff97, 10);
1250 MD5STEP(F4
, c
, d
, a
, b
, in
[14] + 0xab9423a7, 15);
1251 MD5STEP(F4
, b
, c
, d
, a
, in
[5] + 0xfc93a039, 21);
1252 MD5STEP(F4
, a
, b
, c
, d
, in
[12] + 0x655b59c3, 6);
1253 MD5STEP(F4
, d
, a
, b
, c
, in
[3] + 0x8f0ccc92, 10);
1254 MD5STEP(F4
, c
, d
, a
, b
, in
[10] + 0xffeff47d, 15);
1255 MD5STEP(F4
, b
, c
, d
, a
, in
[1] + 0x85845dd1, 21);
1256 MD5STEP(F4
, a
, b
, c
, d
, in
[8] + 0x6fa87e4f, 6);
1257 MD5STEP(F4
, d
, a
, b
, c
, in
[15] + 0xfe2ce6e0, 10);
1258 MD5STEP(F4
, c
, d
, a
, b
, in
[6] + 0xa3014314, 15);
1259 MD5STEP(F4
, b
, c
, d
, a
, in
[13] + 0x4e0811a1, 21);
1260 MD5STEP(F4
, a
, b
, c
, d
, in
[4] + 0xf7537e82, 6);
1261 MD5STEP(F4
, d
, a
, b
, c
, in
[11] + 0xbd3af235, 10);
1262 MD5STEP(F4
, c
, d
, a
, b
, in
[2] + 0x2ad7d2bb, 15);
1263 MD5STEP(F4
, b
, c
, d
, a
, in
[9] + 0xeb86d391, 21);
1271 # endif /* ASM_MD5 */
1273 char *md5_hash(char *message
)
1278 MD5Update(&ctx
, message
, strlen(message
));
1279 return MD5Final(&ctx
);
1281 #endif /* GTK2 or THUMBS_USE_LIBPNG */
1283 /* Removes trailing / chars and converts a leading '~/' (if any) to
1284 * the user's home dir. g_free() the result.
1286 gchar
*icon_convert_path(gchar
*path
)
1291 g_return_val_if_fail(path
!= NULL
, NULL
);
1293 path_len
= strlen(path
);
1294 while (path_len
> 1 && path
[path_len
- 1] == '/')
1297 retval
= g_strndup(path
, path_len
);
1299 if (path
[0] == '~' && (path
[1] == '\0' || path
[1] == '/'))
1301 guchar
*tmp
= retval
;
1303 retval
= g_strconcat(home_dir
, retval
+ 1, NULL
);
1310 /* Convert string 'src' from the current locale to UTF-8.
1311 * If conversion fails, try again using ISO8859-1 -> UTF-8.
1313 gchar
*to_utf8(gchar
*src
)
1320 retval
= g_locale_to_utf8(src
, -1, NULL
, NULL
, NULL
);
1324 return g_convert(src
, -1, "UTF-8", "ISO8859-1", NULL
, NULL
, NULL
);
1327 /* Convert string 'src' to the current locale from UTF-8 */
1328 gchar
*from_utf8(gchar
*src
)
1335 retval
= g_locale_from_utf8(src
, -1, NULL
, NULL
, NULL
);
1337 return retval
? retval
: g_strdup(src
);
1340 /****************************************************************
1341 * INTERNAL FUNCTIONS *
1342 ****************************************************************/
1344 static XMLwrapper
*xml_load(char *pathname
, gpointer data
)
1347 XMLwrapper
*xml_data
= NULL
;
1349 doc
= xmlParseFile(pathname
);
1351 return NULL
; /* Bad XML */
1353 xml_data
= g_new(XMLwrapper
, 1);
1355 xml_data
->doc
= doc
;
1360 static void xml_ref(XMLwrapper
*doc
, gpointer data
)
1366 static void xml_unref(XMLwrapper
*doc
, gpointer data
)
1368 if (doc
&& --doc
->ref
== 0)
1370 xmlFreeDoc(doc
->doc
);
1375 static int xml_getref(XMLwrapper
*doc
, gpointer data
)
1377 return doc
? doc
->ref
: 0;