548 lines
13 KiB
C
548 lines
13 KiB
C
/*
|
|
* timer.c -- handles timers in ircII
|
|
* Copyright 1993, 1996 Matthew Green
|
|
* This file organized/adapted by Jeremy Nelson
|
|
*
|
|
* This used to be in edit.c, and it used to only allow you to
|
|
* register ircII commands to be executed later. I needed more
|
|
* generality then that, specifically the ability to register
|
|
* any function to be called, so i pulled it all together into
|
|
* this file and called it timer.c
|
|
*/
|
|
|
|
#include "irc.h"
|
|
static char cvsrevision[] = "$Id$";
|
|
CVS_REVISION(timer_c)
|
|
#include "struct.h"
|
|
|
|
#include "ircaux.h"
|
|
#include "lastlog.h"
|
|
#include "window.h"
|
|
#include "timer.h"
|
|
#include "hook.h"
|
|
#include "output.h"
|
|
#include "commands.h"
|
|
#include "misc.h"
|
|
#include "vars.h"
|
|
#include "server.h"
|
|
#include "screen.h"
|
|
#include "tcl_bx.h"
|
|
#define MAIN_SOURCE
|
|
#include "modval.h"
|
|
|
|
static void show_timer (char *command);
|
|
void BX_delete_all_timers (void);
|
|
int timer_exists (char *);
|
|
static TimerList *get_timer (char *ref);
|
|
|
|
#define RETURN_INT(x) return m_strdup(ltoa(x))
|
|
|
|
/*
|
|
* timercmd: the bit that handles the TIMER command. If there are no
|
|
* arguments, then just list the currently pending timers; if we are
|
|
* given a -DELETE flag, attempt to delete the timer from the list. Else
|
|
* consider it to be a timer to add, and add it.
|
|
*/
|
|
BUILT_IN_COMMAND(timercmd)
|
|
{
|
|
char *waittime,
|
|
*flag;
|
|
char *want = empty_string;
|
|
char *ptr;
|
|
double interval;
|
|
long events = -2;
|
|
int update = 0;
|
|
int winref = current_window->refnum;
|
|
|
|
while (*args == '-' || *args == '/')
|
|
{
|
|
flag = next_arg(args, &args);
|
|
if (!flag || !*flag)
|
|
break;
|
|
|
|
if (!my_strnicmp(flag+1, "D", 1)) /* DELETE */
|
|
{
|
|
if (!(ptr = next_arg(args, &args)))
|
|
say("%s: Need a timer reference number for -DELETE", command);
|
|
else
|
|
{
|
|
if (timer_exists(ptr))
|
|
delete_timer(ptr);
|
|
else if (!my_strnicmp(ptr, "A", 1))
|
|
delete_all_timers();
|
|
}
|
|
return;
|
|
}
|
|
else if (!my_strnicmp(flag+1, "REP", 3))
|
|
{
|
|
char *na = next_arg(args, &args);
|
|
if (!na || !*na)
|
|
{
|
|
say("%s: Missing argument to -REPEAT", command);
|
|
return;
|
|
}
|
|
if (!strcmp(na, "*") || !strcmp(na, "-1"))
|
|
events = -1;
|
|
else if ((events = my_atol(na)) == 0)
|
|
return;
|
|
}
|
|
else if (!my_strnicmp(flag+1, "REF", 3)) /* REFNUM */
|
|
{
|
|
want = next_arg(args, &args);
|
|
if (!want || !*want)
|
|
{
|
|
say("%s: Missing argument to -REFNUM", command);
|
|
return;
|
|
}
|
|
}
|
|
else if (!my_strnicmp(flag + 1, "L", 1))
|
|
show_timer(command);
|
|
else if (!my_strnicmp(flag + 1, "W", 1)) /* WINDOW */
|
|
{
|
|
char *na;
|
|
|
|
if ((na = next_arg(args, &args)))
|
|
winref = get_winref_by_desc(na);
|
|
|
|
if (winref == -1 && my_stricmp(na, "-1"))
|
|
{
|
|
say("%s: That window doesn't exist!", command);
|
|
return;
|
|
}
|
|
}
|
|
else if (!my_strnicmp(flag + 1, "UPDATE", 1)) /* UPDATE */
|
|
update = 1;
|
|
else
|
|
say("%s: %s no such flag", command, flag);
|
|
}
|
|
|
|
/* else check to see if we have no args -> list */
|
|
|
|
waittime = next_arg(args, &args);
|
|
if (update || waittime)
|
|
{
|
|
if (update && !timer_exists(want))
|
|
{
|
|
say("%s: To use -UPDATE you must specify a valid refnum", command);
|
|
return;
|
|
}
|
|
|
|
if (!waittime)
|
|
interval = -1;
|
|
else
|
|
interval = atof(waittime) * 1000.0;
|
|
|
|
if (!update && events == -2)
|
|
events = 1;
|
|
else if (events == -2)
|
|
events = -1;
|
|
|
|
add_timer(update, want, interval, events, NULL, args, subargs, winref, NULL);
|
|
}
|
|
else
|
|
show_timer(command);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* This is put here on purpose -- we don't want any of the above functions
|
|
* to have any knowledge of this struct.
|
|
*/
|
|
static TimerList *PendingTimers;
|
|
static char *schedule_timer (TimerList *ntimer);
|
|
|
|
static char *current_exec_timer = empty_string;
|
|
|
|
/*
|
|
* ExecuteTimers: checks to see if any currently pending timers have
|
|
* gone off, and if so, execute them, delete them, etc, setting the
|
|
* current_exec_timer, so that we can't remove the timer while its
|
|
* still executing.
|
|
*
|
|
* changed the behavior: timers will not hook while we are waiting.
|
|
*/
|
|
extern void ExecuteTimers (void)
|
|
{
|
|
TimerList *current;
|
|
static int parsingtimer = 0;
|
|
int old_from_server = from_server;
|
|
struct timeval now1;
|
|
|
|
/* We do NOT want to parse timers while waiting
|
|
* cause it gets icky recursive
|
|
*/
|
|
if (!PendingTimers || parsingtimer)
|
|
return;
|
|
get_time(&now1);
|
|
|
|
parsingtimer = 1;
|
|
while (PendingTimers && time_cmp(&now1, &PendingTimers->time) >= 0)
|
|
{
|
|
int old_refnum = current_window->refnum;
|
|
|
|
current = PendingTimers;
|
|
PendingTimers = current->next;
|
|
|
|
make_window_current_by_winref(current->window);
|
|
/*
|
|
* Restore from_server and curr_scr_win from when the
|
|
* timer was registered
|
|
*/
|
|
|
|
if (is_server_connected(current->server))
|
|
from_server = current->server;
|
|
else if (is_server_connected(get_window_server(0)))
|
|
from_server = get_window_server(0);
|
|
else
|
|
from_server = -1;
|
|
|
|
/*
|
|
* If a callback function was registered, then
|
|
* we use it. If no callback function was registered,
|
|
* then we use ''parse_line''.
|
|
*/
|
|
current_exec_timer = current->ref;
|
|
if (current->callback)
|
|
(*current->callback)(current->command, current->subargs);
|
|
else
|
|
parse_line("TIMER",(char *)current->command, current->subargs, 0, 0, 1);
|
|
|
|
current_exec_timer = empty_string;
|
|
from_server = old_from_server;
|
|
make_window_current_by_winref(old_refnum);
|
|
|
|
/*
|
|
* Clean up or reschedule the timer
|
|
*/
|
|
switch (current->events)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
{
|
|
/* callback cleans up command */
|
|
if (!current->callback)
|
|
new_free(¤t->command);
|
|
new_free(¤t->subargs);
|
|
new_free(¤t->whom);
|
|
new_free(¤t);
|
|
break;
|
|
}
|
|
default:
|
|
current->events--;
|
|
case -1:
|
|
{
|
|
time_offset(¤t->time, current->interval);
|
|
schedule_timer(current);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
parsingtimer = 0;
|
|
}
|
|
|
|
/*
|
|
* show_timer: Display a list of all the TIMER commands that are
|
|
* pending to be executed.
|
|
*/
|
|
static void show_timer (char *command)
|
|
{
|
|
TimerList *tmp;
|
|
struct timeval current;
|
|
double time_left;
|
|
int count = 0;
|
|
|
|
for (tmp = PendingTimers; tmp; tmp = tmp->next)
|
|
count++;
|
|
|
|
if (!count)
|
|
{
|
|
say("%s: No commands pending to be executed", command);
|
|
return;
|
|
}
|
|
|
|
get_time(¤t);
|
|
put_it("%s", convert_output_format(fget_string_var(FORMAT_TIMER_FSET), "%s %s %s %s","Timer","Seconds","Events","Command"));
|
|
for (tmp = PendingTimers; tmp; tmp = tmp->next)
|
|
{
|
|
char buf[40];
|
|
time_left = BX_time_diff(current, tmp->time);
|
|
if (time_left < 0)
|
|
time_left = 0;
|
|
snprintf(buf, sizeof buf, "%0.3f", time_left);
|
|
put_it("%s", convert_output_format(fget_string_var(FORMAT_TIMER_FSET), "%s %s %d %s %s", tmp->ref, buf, tmp->events, tmp->callback? "(internal callback)" : (tmp->command? tmp->command : ""), tmp->whom ? tmp->whom : empty_string ));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* create_timer_ref: returns the lowest unused reference number for a timer
|
|
*
|
|
* This will never return 0 for a refnum because that is what atol() returns
|
|
* on case of error, so that it can never happen that a timer has a refnum
|
|
* of zero which would be tripped if the user did say,
|
|
* /TIMER -refnUm foobar 3 blah blah blah
|
|
* which should elicit an error, not be silently punted.
|
|
*/
|
|
static int create_timer_ref (char *refnum_want, char *refnum_gets)
|
|
{
|
|
TimerList *tmp;
|
|
int refnum = 0;
|
|
|
|
/* Max of 10 characters. */
|
|
if (strlen(refnum_want) > REFNUM_MAX)
|
|
refnum_want[REFNUM_MAX] = 0;
|
|
|
|
/* If the user doesn't care */
|
|
if (!strcmp(refnum_want, empty_string))
|
|
{
|
|
/* Find the lowest refnum available */
|
|
for (tmp = PendingTimers; tmp; tmp = tmp->next)
|
|
{
|
|
long ref;
|
|
ref = my_atol(tmp->ref);
|
|
if (refnum < ref)
|
|
refnum = ref;
|
|
}
|
|
strlcpy(refnum_gets, ltoa(refnum+1), REFNUM_MAX);
|
|
}
|
|
else
|
|
{
|
|
/* See if the refnum is available */
|
|
for (tmp = PendingTimers; tmp; tmp = tmp->next)
|
|
{
|
|
if (!my_stricmp(tmp->ref, refnum_want))
|
|
return -1;
|
|
}
|
|
strlcpy(refnum_gets, refnum_want, REFNUM_MAX);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Deletes a refnum. This does cleanup only if the timer is a
|
|
* user-defined timer, otherwise no clean up is done (the caller
|
|
* is responsible to handle it) This shouldnt output an error,
|
|
* it should be more general and return -1 and let the caller
|
|
* handle it. Probably will be that way in a future release.
|
|
*/
|
|
extern int BX_delete_timer (char *ref)
|
|
{
|
|
TimerList *tmp,
|
|
*prev;
|
|
|
|
if (current_exec_timer != empty_string)
|
|
{
|
|
say("You may not remove a TIMER from another TIMER");
|
|
return -1;
|
|
}
|
|
|
|
for (prev = tmp = PendingTimers; tmp; prev = tmp, tmp = tmp->next)
|
|
{
|
|
/* can only delete user created timers */
|
|
if (!my_stricmp(tmp->ref, ref))
|
|
{
|
|
if (tmp == prev)
|
|
PendingTimers = PendingTimers->next;
|
|
else
|
|
prev->next = tmp->next;
|
|
if (!tmp->callback)
|
|
new_free(&tmp->command);
|
|
new_free(&tmp->subargs);
|
|
new_free(&tmp->whom);
|
|
new_free((char **)&tmp);
|
|
return 0;
|
|
}
|
|
}
|
|
say("TIMER: Can't delete %s, no such refnum", ref);
|
|
return -1;
|
|
}
|
|
|
|
void BX_delete_all_timers (void)
|
|
{
|
|
while (PendingTimers)
|
|
delete_timer(PendingTimers->ref);
|
|
return;
|
|
}
|
|
|
|
int timer_exists (char *ref)
|
|
{
|
|
if (get_timer(ref))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
BUILT_IN_FUNCTION(function_istimer)
|
|
{
|
|
char *timer = NULL;
|
|
|
|
if ((timer = next_arg(input, &input)))
|
|
if (timer_exists(timer))
|
|
RETURN_INT(1);
|
|
RETURN_INT(0);
|
|
}
|
|
|
|
int timer_callback_exists (void *ref)
|
|
{
|
|
TimerList *t;
|
|
for (t = PendingTimers; t; t = t->next)
|
|
{
|
|
if (t->callback && ref == t->callback)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static TimerList *get_timer (char *ref)
|
|
{
|
|
TimerList *tmp;
|
|
|
|
for (tmp = PendingTimers; tmp; tmp = tmp->next)
|
|
{
|
|
if (!my_stricmp(tmp->ref, ref) || (tmp->whom && !my_stricmp(tmp->whom, ref)))
|
|
return tmp;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
char *function_timer(char *n, char *args)
|
|
{
|
|
char *ref = next_arg(args, &args);
|
|
TimerList *t = NULL;
|
|
double time_left;
|
|
|
|
if (ref && *ref)
|
|
t = get_timer(ref);
|
|
|
|
if (!t)
|
|
return m_strdup(empty_string);
|
|
|
|
time_left = time_until(&t->time);
|
|
if (time_left < 0)
|
|
time_left = 0.0;
|
|
|
|
return m_sprintf("%s %d %d %.16g %d %0.3f %s%s", t->ref, t->server,
|
|
t->window, t->interval, t->events, time_left,
|
|
t->callback ? "(internal callback) " : t->command,
|
|
t->whom ? t->whom : "");
|
|
}
|
|
|
|
/*
|
|
* You call this to register a timer callback.
|
|
*
|
|
* The arguments:
|
|
* refnum_want: The refnum requested. This should only be specified
|
|
* by the user, functions wanting callbacks should specify
|
|
* the value -1 which means "don't care".
|
|
* The rest of the arguments are dependent upon the value of "callback"
|
|
* -- if "callback" is NULL then:
|
|
* callback: NULL
|
|
* what: some ircII commands to run when the timer goes off
|
|
* subargs: what to use to expand $0's, etc in the 'what' variable.
|
|
*
|
|
* -- if "callback" is non-NULL then:
|
|
* callback: function to call when timer goes off
|
|
* what: argument to pass to "callback" function. Should be some
|
|
* non-auto storage, perhaps a struct or a malloced char *
|
|
* array. The caller is responsible for disposing of this
|
|
* area when it is called, since the timer mechanism does not
|
|
* know anything of the nature of the argument.
|
|
* subargs: should be NULL, its ignored anyhow.
|
|
*/
|
|
char *BX_add_timer(int update, char *refnum_want, double when, long events, int (callback) (void *, char *), char *what, char *subargs, int winref, char *whom)
|
|
{
|
|
TimerList *ntimer, *otimer = NULL;
|
|
char refnum_got[REFNUM_MAX+1] = "";
|
|
|
|
ntimer = (TimerList *) new_malloc(sizeof(TimerList));
|
|
|
|
get_time(&ntimer->time);
|
|
time_offset(&ntimer->time, when / 1000.0);
|
|
|
|
ntimer->interval = when / 1000;
|
|
ntimer->events = events;
|
|
ntimer->server = from_server;
|
|
ntimer->window = winref;
|
|
if (whom)
|
|
ntimer->whom = m_strdup(whom);
|
|
if (update)
|
|
otimer = get_timer(refnum_want);
|
|
|
|
if (otimer)
|
|
{
|
|
if (when == -1)
|
|
{
|
|
ntimer->time = otimer->time;
|
|
ntimer->interval = otimer->interval;
|
|
}
|
|
|
|
if (events == -1)
|
|
ntimer->events = otimer->events;
|
|
|
|
ntimer->callback = otimer->callback;
|
|
if (what && *what)
|
|
{
|
|
ntimer->command = (void *)what;
|
|
if (subargs)
|
|
ntimer->subargs = m_strdup(subargs);
|
|
}
|
|
else
|
|
{
|
|
ntimer->command = m_strdup(otimer->command);
|
|
ntimer->subargs = m_strdup(otimer->subargs);
|
|
}
|
|
|
|
delete_timer(refnum_want);
|
|
}
|
|
else
|
|
{
|
|
if ((ntimer->callback = callback) != NULL)
|
|
ntimer->command = (void *)what;
|
|
else
|
|
ntimer->command = m_strdup(what);
|
|
if (subargs)
|
|
ntimer->subargs = m_strdup(subargs);
|
|
}
|
|
|
|
if (create_timer_ref(refnum_want, refnum_got) == -1)
|
|
{
|
|
say("TIMER: Refnum %s already exists", refnum_want);
|
|
new_free(&ntimer->command);
|
|
new_free(&ntimer->subargs);
|
|
new_free(&ntimer->whom);
|
|
new_free(&ntimer);
|
|
return NULL;
|
|
}
|
|
|
|
strcpy(ntimer->ref, refnum_got);
|
|
|
|
/* we've created it, now put it in order */
|
|
return schedule_timer(ntimer);
|
|
}
|
|
|
|
static char *schedule_timer (TimerList *ntimer)
|
|
{
|
|
TimerList **slot;
|
|
|
|
/* we've created it, now put it in order */
|
|
for (slot = &PendingTimers; *slot; slot = &(*slot)->next)
|
|
{
|
|
if (time_cmp(&(*slot)->time, &ntimer->time) > 0)
|
|
break;
|
|
}
|
|
ntimer->next = *slot;
|
|
*slot = ntimer;
|
|
return ntimer->ref;
|
|
}
|
|
|
|
/*
|
|
* TimerTimeout: Called from irc_io to help create the timeout
|
|
* part of the call to select.
|
|
*/
|
|
|
|
void TimerTimeout(struct timeval *wake_time)
|
|
{
|
|
if (PendingTimers && time_cmp(wake_time, &PendingTimers->time) > 0)
|
|
*wake_time = PendingTimers->time;
|
|
}
|