/* Jungle Monkey
* Copyright (C) 1999, 2000 The Regents of the University of Michigan
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "sha_async.h"
typedef struct _GSHAAsyncState
{
gboolean waiting;
gchar* pathname;
int fd;
GIOChannel* iochannel;
guint watch;
GSHA* sha;
GSHAAsyncFunc func;
gpointer user_data;
} GSHAAsyncState;
#define MAX_ASYNC_SHA 8
static guint num_async_sha = 0;
static GSList* waiting = NULL;
static GSList* waiting_last = NULL;
static gboolean dispatch_sha (GSHAAsyncState* state);
static gboolean sha_cb (gpointer data);
GSHAAsyncID*
gnet_sha_new_file_async (const gchar* path, GSHAAsyncFunc func,
gpointer user_data)
{
GSHAAsyncState* state;
g_return_val_if_fail (path, NULL);
g_return_val_if_fail (func, NULL);
state = g_new0 (GSHAAsyncState, 1);
state->pathname = g_strdup (path);
state->sha = gnet_sha_new_incremental();
state->func = func;
state->user_data = user_data;
/* Dispatch now if no one is waiting, otherwise queue */
if (num_async_sha < MAX_ASYNC_SHA)
{
if (dispatch_sha (state))
state = NULL;
}
else
{
GSList* new_list;
state->waiting = TRUE;
/* Append waiting */
new_list = g_slist_alloc ();
new_list->data = state;
if (waiting_last)
waiting_last->next = new_list;
else
waiting = new_list;
waiting_last = new_list;
}
return (GSHAAsyncID) state;
}
/* Return FALSE if ok, TRUE otherwise */
static gboolean
dispatch_sha (GSHAAsyncState* state)
{
g_return_val_if_fail (state, TRUE);
g_return_val_if_fail (!state->fd, TRUE);
g_return_val_if_fail (state->pathname, TRUE);
state->waiting = FALSE;
++num_async_sha;
/* Fail if file does not exist */
state->fd = open (state->pathname, O_RDONLY);
/* TODO: A newer GLib may include an a way to open files
non-blocking. */
if (state->fd == -1)
{
(state->func)(NULL, state->user_data);
gnet_sha_new_file_async_cancel ((GSHAAsyncID) state);
return TRUE;
}
state->iochannel = g_io_channel_unix_new (state->fd);
state->watch = g_idle_add (sha_cb, state);
return FALSE;
}
static gboolean
sha_cb (gpointer data)
{
GSHAAsyncState* state = (GSHAAsyncState*) data;
gchar buffer [4096];
guint bytes_read;
g_return_val_if_fail (state, FALSE);
bytes_read = read (state->fd, buffer, sizeof(buffer));
if (bytes_read > 0)
{
gnet_sha_update (state->sha, buffer, bytes_read);
return TRUE;
}
else if (bytes_read == 0)
{
gnet_sha_final (state->sha);
(state->func)(state->sha, state->user_data);
state->sha = NULL;
gnet_sha_new_file_async_cancel ((GSHAAsyncID) state);
}
else
{
(state->func)(NULL, state->user_data);
gnet_sha_new_file_async_cancel ((GSHAAsyncID) state);
}
return FALSE;
}
/* Cancel or delete a SHA async state. This is used internally to
delete states. */
void
gnet_sha_new_file_async_cancel (GSHAAsyncID* id)
{
GSHAAsyncState* state = (GSHAAsyncState*) id;
g_return_if_fail (id);
g_free (state->pathname);
gnet_sha_delete (state->sha);
if (state->fd)
close (state->fd);
if (state->iochannel)
g_io_channel_unref (state->iochannel);
if (state->watch)
g_source_remove (state->watch);
if (state->waiting)
{
g_return_if_fail (waiting);
g_return_if_fail (waiting_last);
if (waiting_last->data == state)
waiting_last = NULL;
waiting = g_slist_remove (waiting, state);
if (waiting && !waiting_last)
waiting_last = g_slist_last (waiting);
}
else
{
--num_async_sha;
if (waiting && num_async_sha < MAX_ASYNC_SHA)
{
GSHAAsyncState* next_state;
/* Remove from front of list */
next_state = (GSHAAsyncState*) waiting->data;
if (waiting == waiting_last)
waiting_last = NULL;
waiting = g_slist_remove (waiting, next_state);
dispatch_sha (next_state);
}
}
g_free (state);
}
syntax highlighted by Code2HTML, v. 0.9.1