[vhffs-dev] [1540] fixed a design bug about paths, we used to keep full pathname in memory, which is very hard to deal when directories are moved to parents dirs, now are now building a very simple tree with lookup through a hash table

[ Thread Index | Date Index | More vhffs.org/vhffs-dev Archives ]


Revision: 1540
Author:   gradator
Date:     2010-01-19 01:41:00 +0100 (Tue, 19 Jan 2010)
Log Message:
-----------
fixed a design bug about paths, we used to keep full pathname in memory, which is very hard to deal when directories are moved to parents dirs, now are now building a very simple tree with lookup through a hash table

Modified Paths:
--------------
    trunk/vhffs-fssync/vhffsfssync_master.c

Modified: trunk/vhffs-fssync/vhffsfssync_master.c
===================================================================
--- trunk/vhffs-fssync/vhffsfssync_master.c	2010-01-13 01:18:47 UTC (rev 1539)
+++ trunk/vhffs-fssync/vhffsfssync_master.c	2010-01-19 00:41:00 UTC (rev 1540)
@@ -26,7 +26,9 @@
 #define DEBUG_NET 0
 #define DEBUG_INOTIFY 0
 #define DEBUG_EVENTS 0
+//#define NDEBUG
 
+#include <assert.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -58,11 +60,17 @@
 // Will never be used: IN_ACCESS, IN_OPEN, IN_CLOSE_NOWRITE
 
 // each monitor entry is associated with a path, we need to keep it to compute the path
-//char **vhffsfssync_wd_to_path = NULL;
-//int vhffsfssync_wd_to_path_len = 0;  // number of allocated paths
-GHashTable *vhffsfssync_wd_to_path;
-GHashTable *vhffsfssync_path_to_wd;
+//char **vhffsfssync_wd_to_watch = NULL;
+//int vhffsfssync_wd_to_watch_len = 0;  // number of allocated paths
+GHashTable *vhffsfssync_wd_to_watch;
 
+
+typedef struct vhffsfssync_watch_ {
+	int wd;
+	char *dirname;
+	struct vhffsfssync_watch_ *parent;
+} vhffsfssync_watch;
+
 // return a timestamp in ms (it loops for 100000 sec)
 /*inline int vhffsfssync_timestamp()  {
 	struct timeval tv;
@@ -72,20 +80,21 @@
 
 struct vhffsfssync_cookie {
 	uint32_t id;
-	char *from;
+	vhffsfssync_watch *watch;
+	char *filename;
 	gboolean isdir;
 };
 static struct vhffsfssync_cookie vhffsfssync_cookie;
 
 // protos
-int vhffsfssync_add_watch(int inotifyfd, const char *pathname, uint32_t mask);
-int vhffsfssync_modify_watch(int inotifyfd, const char *from, const char *to);
-int vhffsfssync_del_watch(int inotifyfd, const char *pathname, int wd);
-int vhffsfssync_add_watch_recursively(int inotifyfd, const char *pathname, uint32_t mask);
-int vhffsfssync_manage_event_remove(int inotifyfd, char *pathname);
-int vhffsfssync_manage_event_create(int inotifyfd, char *pathname);
+char *vhffsfssync_pathname(vhffsfssync_watch *watch, const char *filename);
+vhffsfssync_watch *vhffsfssync_add_watch(int inotifyfd, vhffsfssync_watch *parent, const char *dirname, uint32_t mask);
+int vhffsfssync_del_watch(int inotifyfd, vhffsfssync_watch *watch);
+vhffsfssync_watch *vhffsfssync_add_watch_recursively(int inotifyfd, vhffsfssync_watch *parent, const char *dirname, uint32_t mask);
+int vhffsfssync_manage_event_remove(int inotifyfd, vhffsfssync_watch *watch, char *filename);
+int vhffsfssync_manage_event_create(int inotifyfd, vhffsfssync_watch *watch, char *filename);
 int vhffsfssync_manage_event(int inotifyfd, struct inotify_event *event);
-int vhffsfssync_fake_events_recursively(int inotifyfd, char *pathname);
+int vhffsfssync_fake_events_recursively(int inotifyfd, vhffsfssync_watch *watch);
 
 
 /* -- network stuff -- */
@@ -568,6 +577,7 @@
 }
 
 
+// TODO: improve that, using *watch is probably faster
 char *vhffsfssync_net_parent_mtime(char *pathname)  {
 	char *cur;
 	struct stat st;
@@ -1190,128 +1200,134 @@
 
 /* -- inotify stuff -- */
 
-int vhffsfssync_add_watch(int inotifyfd, const char *pathname, uint32_t mask)  {
+char *vhffsfssync_pathname(vhffsfssync_watch *watch, const char *filename)  {
+	
+	GString *pathname = g_string_sized_new(256);
 
-	int wd;
-	int *_wd;
-	char *_pathname;
+	// TODO: improve that, prepend is probably slow
+	while(watch) {
+		g_string_prepend_c(pathname, '/');
+		g_string_prepend(pathname, watch->dirname);
+		watch = watch->parent;
+	}
 
-	if( g_hash_table_lookup(vhffsfssync_path_to_wd, pathname) )  {
-		return -1;
+	if(filename) {
+		g_string_append(pathname, filename);
 	}
 
+//	if(watch) printf("=d> %s\n", watch->dirname);
+//	if(filename) printf("=f> %s\n", filename);
+//	printf("==> %s\n", pathname->str);
+	return g_string_free(pathname, FALSE);
+}
+
+
+vhffsfssync_watch *vhffsfssync_add_watch(int inotifyfd, vhffsfssync_watch *parent, const char *dirname, uint32_t mask)  {
+
+	int wd;
+	char *pathname;
+	vhffsfssync_watch *watch;
+
+	pathname = vhffsfssync_pathname(parent, dirname);
+#if DEBUG_INOTIFY
+	printf("t+ %s\n", pathname);
+#endif
+
 	wd = inotify_add_watch(inotifyfd, pathname, mask);
 	if(wd < 0) {
 		if(errno == ENOSPC)  {
 			fprintf(stderr, "Maximum number of watches reached, consider adding more...\n");
 		}
-		return wd;
+		free(pathname);
+		return NULL;
 	}
+ 
+ 	if( (watch = g_hash_table_lookup(vhffsfssync_wd_to_watch, &wd)) ) {
 
-	_wd = g_new(int, 1);
-	*_wd = wd;
-	_pathname = g_strdup(pathname);
-	g_hash_table_insert(vhffsfssync_wd_to_path, _wd, _pathname);
-	g_hash_table_insert(vhffsfssync_path_to_wd, _pathname, _wd);
+		// this was already watched, update name and reattach to the new parent
+		free(watch->dirname);
+		watch->dirname = g_strdup(dirname);
+		watch->parent = parent;
 
-//	if(wd >= vhffsfssync_wd_to_path_len)  {
-//		vhffsfssync_wd_to_path_len = ( (wd >>10) +1) <<10;
-//		vhffsfssync_wd_to_path = realloc( vhffsfssync_wd_to_path, vhffsfssync_wd_to_path_len * sizeof(void*) );
-//	}
-//	vhffsfssync_wd_to_path[wd] = strdup(pathname);
 #if DEBUG_INOTIFY
-	printf("+ %d %s\n", wd, pathname);
+		printf("u+ %d %s\n", wd, pathname);
 #endif
-	return wd;
-}
+		free(pathname);
+		return watch;
+	}
 
+	watch = malloc(sizeof(vhffsfssync_watch));
+	watch->wd = wd;
+	watch->dirname = g_strdup(dirname);
+	watch->parent = parent;
 
-int vhffsfssync_del_watch(int inotifyfd, const char *pathname, int wd)  {
+//	_wd = g_new(int, 1);
+//	*_wd = wd;
+	g_hash_table_insert(vhffsfssync_wd_to_watch, &watch->wd, watch);
 
-	if(!pathname && wd > 0)  {
-		pathname = (char*)g_hash_table_lookup(vhffsfssync_wd_to_path, &wd);
-		// this wd has already been deleted
-		if(!pathname) return -1;
-	}
-	else if(pathname && wd == 0) {
-		int *_wd;
-		_wd = g_hash_table_lookup(vhffsfssync_path_to_wd, pathname);
-		// this wd has already been deleted
-		if(!_wd) return -1;
- 		wd = *_wd;
-	}
-	else return -1;
+//	if(wd >= vhffsfssync_wd_to_watch_len)  {
+//		vhffsfssync_wd_to_watch_len = ( (wd >>10) +1) <<10;
+//		vhffsfssync_wd_to_watch = realloc( vhffsfssync_wd_to_watch, vhffsfssync_wd_to_watch_len * sizeof(void*) );
+//	}
+//	vhffsfssync_wd_to_watch[wd] = strdup(pathname);
 #if DEBUG_INOTIFY
-	printf("- %d %s\n", wd, pathname);
+	printf("a+ %d %s\n", wd, pathname);
 #endif
-	g_hash_table_remove(vhffsfssync_path_to_wd, pathname);
-	g_hash_table_remove(vhffsfssync_wd_to_path, &wd);
-	return inotify_rm_watch(inotifyfd, wd);
+	free(pathname);
+	return watch;
 }
 
 
-int vhffsfssync_modify_watch(int inotifyfd, const char *from, const char *to)  {
+int vhffsfssync_del_watch(int inotifyfd, vhffsfssync_watch *watch)  {
 
-	int wd;
-	int *_wd;
-	char *_to;
-
-	_wd = g_hash_table_lookup(vhffsfssync_path_to_wd, from);
-	if(!_wd) return -1;
-	wd = *_wd;
-
-	g_hash_table_remove(vhffsfssync_path_to_wd, from);
-	g_hash_table_remove(vhffsfssync_wd_to_path, _wd);
-
-	_wd = g_new(int, 1);
-	*_wd = wd;
-	_to = strdup(to);
-	g_hash_table_insert(vhffsfssync_wd_to_path, _wd, _to);
-	g_hash_table_insert(vhffsfssync_path_to_wd, _to, _wd);
-
 #if DEBUG_INOTIFY
-	printf("= %d %s -> %s\n", wd, from, to);
+	char *pathname = vhffsfssync_pathname(watch, NULL);
+	printf("- %d %s\n", watch->wd, pathname);
+	free(pathname);
 #endif
+	g_hash_table_remove(vhffsfssync_wd_to_watch, &watch->wd);
+	inotify_rm_watch(inotifyfd, watch->wd);
+	free(watch->dirname);
+	free(watch);
 	return 0;
 }
 
 
-int vhffsfssync_add_watch_recursively(int inotifyfd, const char *pathname, uint32_t mask)  {
+vhffsfssync_watch *vhffsfssync_add_watch_recursively(int inotifyfd, vhffsfssync_watch *parent, const char *dirname, uint32_t mask)  {
 
-	int wd;
+	vhffsfssync_watch *watch;
+	char *pathname;
 	DIR *d;
 
-	wd = vhffsfssync_add_watch(inotifyfd, pathname, mask);
-	if(wd < 0) return wd;
+	watch = vhffsfssync_add_watch(inotifyfd, parent, dirname, mask);
+	if(!watch) return NULL;
 
+	pathname = vhffsfssync_pathname(parent, dirname);
 	d = opendir(pathname);
+	free(pathname);
 	if(d) {
 		struct dirent *dir;
 		while( (dir = readdir(d)) )  {
 			if(dir->d_type == DT_DIR && strcmp(dir->d_name, ".") && strcmp(dir->d_name, "..") )  {
-				char *path;
-				path = g_strdup_printf("%s/%s", pathname, dir->d_name);
-				wd = vhffsfssync_add_watch_recursively(inotifyfd, path, mask);
-				free(path);
+				vhffsfssync_add_watch_recursively(inotifyfd, watch, dir->d_name, mask);
 			}
 		}
 		closedir(d);
 	}
 
-	return wd;
+	return watch;
 }
 
 
-int vhffsfssync_manage_event_remove(int inotifyfd, char *pathname)  {
+int vhffsfssync_manage_event_remove(int inotifyfd, vhffsfssync_watch *watch, char *filename)  {
+	char *pathname;
 	GList *conns;
-	int *_wd;
 
+	pathname = vhffsfssync_pathname(watch, filename);
+
 #if DEBUG_INOTIFY
 	printf("==> REMOVE %s\n", pathname);
 #endif
-	if( (_wd = g_hash_table_lookup(vhffsfssync_path_to_wd, pathname)) )  {
-		vhffsfssync_del_watch(inotifyfd, NULL, *_wd);
-	}
 
 	/* connections */
 	for(conns = g_list_first(vhffsfssync_conns) ; conns ; )  {
@@ -1321,13 +1337,18 @@
 		vhffsfssync_net_send_event(conn, g_strdup_printf("remove%c%s%c", '\0', pathname, '\0') , VHFFSFSSYNC_NET_PRIO_MEDIUM);
 		vhffsfssync_net_send_event(conn, vhffsfssync_net_parent_mtime(pathname) , VHFFSFSSYNC_NET_PRIO_MEDIUM);
 	}
+
+	free(pathname);
 	return 0;
 }
 
 
-int vhffsfssync_manage_event_create(int inotifyfd, char *pathname)  {
+int vhffsfssync_manage_event_create(int inotifyfd, vhffsfssync_watch *watch, char *filename)  {
 	struct stat st;
+	char *pathname;
 
+	pathname = vhffsfssync_pathname(watch, filename);
+
 	if(! lstat(pathname, &st) )  {
 
 		if( S_ISREG(st.st_mode) )  {
@@ -1344,15 +1365,16 @@
 		}
 
 		else if( S_ISDIR(st.st_mode) )  {
+			vhffsfssync_watch *newwatch;
 #if DEBUG_INOTIFY
 			printf("==> MKDIR %s\n", pathname);
 #endif
-			vhffsfssync_add_watch(inotifyfd, pathname, VHFFSFSSYNC_WATCH_MASK);
+			newwatch = vhffsfssync_add_watch(inotifyfd, watch, filename, VHFFSFSSYNC_WATCH_MASK);
 			vhffsfssync_net_broadcast_event( g_strdup_printf("mkdir%c%s%c%ld%c", '\0', pathname, '\0', st.st_mtime, '\0') , VHFFSFSSYNC_NET_PRIO_MEDIUM);
 			vhffsfssync_net_broadcast_event( vhffsfssync_net_parent_mtime(pathname) , VHFFSFSSYNC_NET_PRIO_MEDIUM);
 			/* there is a short delay between the mkdir() and the add_watch(),
 			   we need to send events about the data which have already been written */
-			vhffsfssync_fake_events_recursively( inotifyfd, pathname );
+			vhffsfssync_fake_events_recursively( inotifyfd, newwatch );
 		}
 
 		else if( S_ISLNK(st.st_mode) )  {
@@ -1374,6 +1396,7 @@
 					// file already disappeared (common for temporary files)
 				} else {
 					fprintf(stderr, "cannot readlink() '%s': %s\n", pathname, strerror(errno));
+					free(pathname);
 					return -1;
 				}
 			}
@@ -1386,17 +1409,20 @@
 			// file already disappeared (common for temporary files)
 		} else {
 			fprintf(stderr, "cannot lstat() '%s': %s\n", pathname, strerror(errno));
+			free(pathname);
 			return -1;
 		}
 	}
 
+	free(pathname);
 	return 0;
 }
 
 
 int vhffsfssync_manage_event(int inotifyfd, struct inotify_event *event)  {
 
-	char *dirpath, *pathname;
+	vhffsfssync_watch *watch;
+	char *pathname;
 #if DEBUG_INOTIFY
 	printf("wd=%d mask=%x cookie=%d len=%d", event->wd, event->mask, event->cookie, event->len);
 	if(event->len > 0) printf(" name=%s", event->name);
@@ -1408,22 +1434,21 @@
 		return -1;
 	}
 
-	dirpath = (char*)g_hash_table_lookup(vhffsfssync_wd_to_path, &event->wd);
-	// this wd has been deleted
-	if(!dirpath) return -1;
+	watch = g_hash_table_lookup(vhffsfssync_wd_to_watch, &event->wd);
+	assert( watch != NULL );
 
 	if(event->len > 0)  {
-		pathname = g_strdup_printf("%s/%s", dirpath, event->name);
+		pathname = vhffsfssync_pathname(watch, event->name);
 	} else {
-		pathname = strdup(dirpath);
+		pathname = vhffsfssync_pathname(watch, NULL);
 	}
 
 	// this event is not waiting for a cookie, delete file if necessary (IN_MOVED_FROM not followed with IN_MOVED_TO)
 	if( !(event->mask & IN_MOVED_TO) && vhffsfssync_cookie.id )  {
 
-		vhffsfssync_manage_event_remove(inotifyfd, vhffsfssync_cookie.from);
+		vhffsfssync_manage_event_remove(inotifyfd, vhffsfssync_cookie.watch, vhffsfssync_cookie.filename);
 		vhffsfssync_cookie.id = 0;
-		free(vhffsfssync_cookie.from);
+		free(vhffsfssync_cookie.filename);
 	}
 
 	// new mtime (and also atime, chmod and chown, but we are not using them)
@@ -1448,14 +1473,14 @@
 #if DEBUG_INOTIFY
 		printf("IN_CREATE\n");
 #endif
-		vhffsfssync_manage_event_create(inotifyfd, pathname);
+		vhffsfssync_manage_event_create(inotifyfd, watch, event->name);
 
 	// deleted file, directory or symlink
 	} else if( event->mask & IN_DELETE )  {
 #if DEBUG_INOTIFY
 		printf("IN_DELETE\n");
 #endif
-		vhffsfssync_manage_event_remove(inotifyfd, pathname);
+		vhffsfssync_manage_event_remove(inotifyfd, watch, event->name);
 
 	// watch deleted, not used
 	} else if( event->mask & IN_DELETE_SELF )  {
@@ -1466,15 +1491,15 @@
 		// event was added, in this case the add_watch failed to monitor this dir
 		// and we'll not receive a IN_DELETE_SELF for it
 		//
-		//implicit deletion (IN_IGNORED will follow this event)
-		//vhffsfssync_del_watch(inotifyfd, event->wd);
+		// Anyway, a IN_IGNORE event will be sent, IN_DELETE_SELF is only
+		// useful for monitored files, that is not used here.
 
 	// file modified
 	} else if( event->mask & IN_MODIFY )  {
 #if DEBUG_INOTIFY
 		printf("IN_MODIFY\n");
 		/* we can send the data here */
-		printf("==> SEND %s\n", pathname);
+//		printf("==> SEND %s\n", pathname);
 #endif
 	// file modified and closed
 	} else if( event->mask & IN_CLOSE_WRITE )  {
@@ -1504,7 +1529,8 @@
 #endif
 		// set the cookie
 		vhffsfssync_cookie.id = event->cookie;
-		vhffsfssync_cookie.from = strdup(pathname);
+		vhffsfssync_cookie.watch = watch;
+		vhffsfssync_cookie.filename = strdup(event->name);
 		vhffsfssync_cookie.isdir = !!( event->mask & IN_ISDIR );
 
 	} else if( event->mask & IN_MOVED_TO )  {
@@ -1514,25 +1540,30 @@
 		// mv
 		if(vhffsfssync_cookie.id == event->cookie)  {
 #if DEBUG_INOTIFY
-			printf("==> MOVE %s -> %s  (used cookie %d)\n", vhffsfssync_cookie.from, pathname, vhffsfssync_cookie.id);
+			char *tmp = vhffsfssync_pathname(vhffsfssync_cookie.watch, vhffsfssync_cookie.filename);
+			printf("==> MOVE (%d -> %d) %s -> %s  (used cookie %d)\n", vhffsfssync_cookie.watch->wd, watch->wd, tmp, pathname, vhffsfssync_cookie.id);
+			free(tmp);
 #endif
 			if( vhffsfssync_cookie.isdir )  {
-				vhffsfssync_modify_watch(inotifyfd, vhffsfssync_cookie.from, pathname);
-				vhffsfssync_net_broadcast_event( g_strdup_printf("move%c%s%c%s%c", '\0', vhffsfssync_cookie.from, '\0', pathname, '\0') , VHFFSFSSYNC_NET_PRIO_MEDIUM);
-				vhffsfssync_net_broadcast_event( vhffsfssync_net_parent_mtime(vhffsfssync_cookie.from) , VHFFSFSSYNC_NET_PRIO_MEDIUM);
+				
+				char *frompathname = vhffsfssync_pathname(vhffsfssync_cookie.watch, vhffsfssync_cookie.filename);
+				vhffsfssync_add_watch(inotifyfd, watch, event->name, VHFFSFSSYNC_WATCH_MASK);
+				vhffsfssync_net_broadcast_event( g_strdup_printf("move%c%s%c%s%c", '\0', frompathname, '\0', pathname, '\0') , VHFFSFSSYNC_NET_PRIO_MEDIUM);
+				vhffsfssync_net_broadcast_event( vhffsfssync_net_parent_mtime(frompathname) , VHFFSFSSYNC_NET_PRIO_MEDIUM);
 				vhffsfssync_net_broadcast_event( vhffsfssync_net_parent_mtime(pathname) , VHFFSFSSYNC_NET_PRIO_MEDIUM);
+				free(frompathname);
 			}
 			else {
-				vhffsfssync_manage_event_remove(inotifyfd, vhffsfssync_cookie.from);
-				vhffsfssync_manage_event_create(inotifyfd, pathname);
+				vhffsfssync_manage_event_remove(inotifyfd, vhffsfssync_cookie.watch, vhffsfssync_cookie.filename);
+				vhffsfssync_manage_event_create(inotifyfd, watch, event->name);
 			}
 
 			vhffsfssync_cookie.id = 0;
-			free(vhffsfssync_cookie.from);
+			free(vhffsfssync_cookie.filename);
 		}
 		// create
 		else  {
-			vhffsfssync_manage_event_create(inotifyfd, pathname);
+			vhffsfssync_manage_event_create(inotifyfd, watch, event->name);
 		}
 
 	// watch deleted, clean it
@@ -1540,7 +1571,7 @@
 #if DEBUG_INOTIFY
 		printf("IN_IGNORED\n");
 #endif
-		vhffsfssync_del_watch(inotifyfd, NULL, event->wd);
+		vhffsfssync_del_watch(inotifyfd, watch);
 
 	// this event is not handled, this should not happen
 	} else {
@@ -1554,21 +1585,21 @@
 }
 
 
-int vhffsfssync_fake_events_recursively(int inotifyfd, char *pathname)  {
+int vhffsfssync_fake_events_recursively(int inotifyfd, vhffsfssync_watch *watch)  {
 	DIR *d;
+	char *pathname;
+	
+	pathname = vhffsfssync_pathname(watch, NULL);
+	d = opendir(pathname);
+	free(pathname);
 
-	d = opendir(pathname);
 	if(d) {
 		struct dirent *dir;
 		while( (dir = readdir(d)) ) {
 			if( strcmp(dir->d_name, ".") && strcmp(dir->d_name, "..") ) {
-				char *path;
-				path = g_strdup_printf("%s/%s", pathname, dir->d_name);
-
 				// recursivity is done through vhffsfssync_manage_event_create()
 				// which calls this function
-				vhffsfssync_manage_event_create(inotifyfd, path);
-				free(path);
+				vhffsfssync_manage_event_create(inotifyfd, watch, dir->d_name);
 			}
 		}
 		closedir(d);
@@ -1594,7 +1625,7 @@
 int main(int argc, char *argv[])  {
 
 	int inotifyfd, flags;
-	int wd;
+	vhffsfssync_watch *watch;
 
 	int listenfd, opt;
 	struct sockaddr_in src;
@@ -1683,10 +1714,10 @@
 
 	/* -- inotify stuff -- */
 
-	vhffsfssync_wd_to_path = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free);
-	vhffsfssync_path_to_wd = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
+	vhffsfssync_wd_to_watch = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, NULL);
+	vhffsfssync_cookie.watch = NULL;
 	vhffsfssync_cookie.id = 0;
-	vhffsfssync_cookie.from = NULL;
+	vhffsfssync_cookie.filename = NULL;
 
 	inotifyfd = inotify_init();
 
@@ -1697,8 +1728,8 @@
 		fcntl(inotifyfd, F_SETFL, flags);
 	}
 
-	wd = vhffsfssync_add_watch_recursively(inotifyfd, root, VHFFSFSSYNC_WATCH_MASK);
-	if(wd < 0)  {
+	watch = vhffsfssync_add_watch_recursively(inotifyfd, NULL, root, VHFFSFSSYNC_WATCH_MASK);
+	if(!watch)  {
 		fprintf(stderr, "Maximum number of watches probably reached, consider adding more or fixing what is being wrong before running me again (strace is your friend)... byebye!\n");
 		return -1;
 	}


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/