#include <sys/time.h>
#include <time.h>
#include <ctype.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

#include <slang.h>

#include "slirc.h"

static int abort_char = 3;   /* For MSDOS, use 34 as scan code */

static char Top2[100] = "";

struct _ProgressMeter {
   struct _ProgressMeter *prev;
   struct _ProgressMeter *next;
   
   char *title;
   int number;
   int max;
   int point;
};

static struct _ProgressMeter *PMfirst = NULL;
static struct _ProgressMeter *PMlast = NULL;
static int PMnum = 0;  /* number of active PMs */
static char PM_dirty = 0;

static void DoExit(char *) __attribute__((noreturn));
void Fatal(char *) __attribute__((noreturn));
void SLirc_UpdateDisplay(void);
void SLirc_Log(char *);

char NullString[] = "";
static char *NullStringP = NullString;

int UseColours = 0;

SLang_Name_Type *VF_reset = NULL;
/*
 * siz is the MAX # chars to write to dest,
 * if siz>0 there will be a terminal '\0' written
 * and return val points to that terminal zero
 */
char *strmcpy(char *dest, const char *src, int siz)
{
   if (siz > 0) {
      while (--siz)
	if (!(*dest++ = *src++))
	  return --dest;
      *dest = '\0';
   }
   return dest;
}

int IrcCmp(char *nika, char *nikb, int n)
{
   register char cha;
   register char chb;
   char bang = 0;
   
   while (n--) {
      cha = *nika++;
      chb = *nikb++;
      if (cha != chb && !bang && LOWER_CASE(cha) != LOWER_CASE(chb))
	return 1;
      if (!cha) break;
      if (cha == '!') bang = cha;
   }
   return 0;
}

/* void Topcat(char*cp) {strcat(Top2,cp);}*/

static void dflt_sig_handler(int signum)
{
   char buf[130];
   
   sprintf(buf, "Caught sig %d (%s)", signum, Top2);
   SLang_doerror(buf);
   if (VF_reset) SLexecute_function(VF_reset);
   exit(1);
}

#define MAXPMS 50
int Rpms_ct;
static int R_trailing; /* nonzero is ix of trailing field */
char *Rpms[MAXPMS];

static void Create_Rpms_Array(void)
{
   int i, r;
   for (i = MAXPMS; i > 0;)
     Rpms[--i] = "";
     r = SLang_add_intrinsic_array(
         "Rpms",      /* slang name */
         SLANG_STRING_TYPE,
         1,           /* Readonly array */
         Rpms,        /* location of the array */
         1,           /* number of dimensions */
         MAXPMS);     /* number of elements in X direction */
   if (r)
     Fatal("Failed to add Rpms array");
}

/*
 * int ParseReturn(char *cp) 
 *   cp points to a null-terminated line of data,
 *   which MAY have a \n or \r\n at the end.
 *   it is copied to an internal buffer, less it's
 *   \n or \r\n, then parsed in place into null-terminated
 *   parameter strings.
 *
 * Rpms[n] will be a pointer to the nth string.
 *   Rpms[0] will be the PREFIX field (if any), otherwise a NullString.
 * Rpms_ct will be the total number of params, counting
 *   the possibly NullString prefix.
 *   Rpms_ct is also the return-value of the function,
 *   0 indicates some sort of error.
 * R_trailing will be set to the INDEX of the 'trailing'
 *   parameter (if any), otherwise to 0.  So that if it
 *   is nonzero, it will obviously equal (Rpms_ct - 1).
 */
static int ParseReturn(char *cp0)
{
   static char RBuf[1025];
   /*
    * Static because I hate autoing tonnes of stuff all the time...
    */
   int Num;
   char *cp,*pm;
   char stop, ch;
   
   cp = strmcpy(RBuf, cp0, sizeof(RBuf));
   /* now cp points to the terminating null in RBuf */
   while(1) {
      if (cp > RBuf && *(cp-1) == '\n') { /* back up past '\n' if is one. */
	 cp--;
	 if (cp > RBuf && *(cp-1) == '\r') cp--;
	 *cp = '\0';
      }
      if (NULL == (cp = index(RBuf, '\n'))) break;
      SLirc_Log("Buffer has multiple lines. Weird.");
      SLirc_UpdateDisplay();
   }
   
   cp = RBuf;
   
   /* strmcpy(Top2, cp, sizeof(Top2) - 15); SetTop(cp); */
   
   for (Num = 10; Num > 0;) Rpms[--Num] = "";
   /* now Num=0 */
   R_trailing = 0;
   if (*cp++ != ':') {						/* no prefix */
      cp--;
      Num++;
   }
   stop = ' ';	/* field delimiter */
   
   while (*cp == stop)
     cp++;	/*skip space */
   
   while (1) {
      pm = cp;
      while ((ch = *cp) && ch != stop)
	cp++;			/*skip to space or \0 */
      *cp++ = '\0';
      if (Num < MAXPMS - 1)
	Rpms[Num++] = pm;
      if (!ch)
	break;
      while ((ch = *cp) == ' ')
	cp++;		/*skip space */
      if (ch != ':')
	continue;
      stop = '\0';
      cp++;
      R_trailing = Num;
   }
   Rpms_ct = Num;
   return Num;
}

/* because we don't like the "\r\n" used in slang itself: */
static void Local_vmessage(char *fmt, va_list ap)
{
   vfprintf (stdout, fmt, ap);
   fputs ("\n", stdout);
}


char Target[81] = "NOTARGET";

static char Top[75];
static char Status[256];
static char StatMsg[50] = "NOSTATUS";

#define EntryBuffLen 510
struct _EntryBox {
	int Offset;   /* s[Offset] is 1st char in window  */
	int Insert;   /* s[Insert] is the cursor location */
	int mode;     /* was CommandMode */
	char dirty;   /* nonzero means need redraw        */
	char s[EntryBuffLen];
	char Prompts[2][12]; /* normal and over prompts   */
};

static struct _EntryBox CommandBox = {
	0, 0, 0, 1,
	"", {"> ", "$ "}
};
static char *CommandTextP = CommandBox.s;

static char GotWinch = 0;
static char Top_dirty = 1;
static char Status_dirty = 1;

static char *TargetP = Target;

char ServerName[128] = "";
static char ClientName[128] = "localhost";
char NickName[10] = "";    /* for various display funcs */
static char UserName[10] = "";
static char *ServerNameP = ServerName;
static char *ClientNameP = ClientName;
static char *NickNameP = NickName;
static char *UserNameP = UserName;
static const char *ProgNameP = "SLirc";
static const char *ProgVersionP = VERSION;
static const char *CFG_DIRP = CFG_DIR;

/* static unsigned long LastRecvTime = 0; */

static int ShowInfo = 1;
static int SrvTime = 0;

static void DrawStatus(int start)
{
   char secs[8];
	 
	 snprintf(Status, sizeof(Status), "%s -> %s  Server[%s]  %s",
	   NickName, Target, ServerName, StatMsg);
   
   SLsmg_gotorc(start, 0);
   SLsmg_set_color(UseColours? ColStatusSay + CommandBox.mode : 1); /* HACK! */
   SLsmg_write_string(Status);
   SLsmg_erase_eol();

   sprintf(secs,"%3d",SrvTime);
	 SLsmg_gotorc(start, SLtt_Screen_Cols - 10);
   SLsmg_write_string(secs);

   if (!ShowInfo) {
      SLsmg_gotorc(start, SLtt_Screen_Cols - 6);
      SLsmg_write_string("NOINFO");
   }
   SLsmg_set_color(UseColours? ColDefault : 0);
   Status_dirty = 0;
}

static void DrawTop(int start)
{
   SLsmg_gotorc(start, 0);
   SLsmg_set_color((UseColours) ? ColTopNorm : 1);
   SLsmg_write_string(Top);
   SLsmg_erase_eol();
   SLsmg_set_color((UseColours) ? ColDefault : 0);
   Top_dirty = 0;
}

static void SetTop(char *st)
{
   if (strcmp(Top,st)) {
      strmcpy(Top, st, sizeof(Top));
      Top_dirty = 1;
   }
}

static void DrawEntryBox(struct _EntryBox *b, int y0)
{
   char *prompt;
   int promptlen, over,roff;
   int x0 = 0;
   int x1 = SLtt_Screen_Cols;

   if (b->Offset > b->Insert) b->Offset = b->Insert;
   while (1) {
     int rshift;
		 over = (b->Offset > 0)? 1:0;
     prompt = b->Prompts[over];
     promptlen = strlen(prompt);
     roff = x0 + promptlen + b->Insert - b->Offset;
     rshift = roff - (x1 - 1);
     if (rshift <= 0) break; 
     b->Offset += rshift;
   }

   SLsmg_gotorc(y0, x0);
   SLsmg_set_color((UseColours) ? ColBufferPromptSayNorm + 2*b->mode + over : 0);
   SLsmg_write_string(prompt);
   SLsmg_set_color((UseColours) ? ColBufferTextSay + b->mode : 0);
   SLsmg_write_nstring(b->s + b->Offset, x1-x0-promptlen);
   /* SLsmg_erase_eol(); */
   SLsmg_gotorc(y0, roff);
	 SLsmg_set_color((UseColours) ? ColDefault : 0);
   b->dirty = 0;
}

static void SLirc_SetCommandBox(char *st)
{
	struct _EntryBox *b;
	b = &CommandBox;
	if (b->s == strmcpy(b->s, st, EntryBuffLen)) { /* was "" */
      b->Insert = 0;
      b->Offset = 0;
      b->mode = 0;
   }
   b->dirty = 1;
}

/*
 * SLirc API stuff here.
 */

static void SLirc_InitVarsDefault(void)
{
   char *p, *q;
   
   if (!(p = getenv("USER")) && !(p = getenv("LOGNAME")))
     p = "ircuser";
   strmcpy(UserName, p, sizeof(UserName));
   
   if (!(q = getenv("IRCNICK"))) q = p;
   strmcpy(NickName, q, sizeof(NickName));
   
   /* now for scripts search-path */
   if((q = getenv("HOME")))
     snprintf(SLirc_Load_Path, 196, "%s/.slirc/scripts",q);
   else
     snprintf(SLirc_Load_Path, 196, "/home/%s/.slirc/scripts",p);

	 if (2 != SLpath_file_exists(SLirc_Load_Path))
	   SLirc_Load_Path[0] = '\0';
   	 else
	   strcat(SLirc_Load_Path, ":");

	 strcat(SLirc_Load_Path,SCRIPTPATH); /* SCRIPTPATH defined in Makefile */
}

/*
 * SetStatus(char *) -> set_status(string)
 * Sets StatMsg part of status line.
 */
static void SetStatus(char *st)
{
   if (strcmp(StatMsg,st)) {
      strmcpy(StatMsg,st,sizeof(StatMsg));
      Status_dirty = 1;
   }
}

static void SLirc_SetNick(char *st)
{
   if (strcmp(NickName,st)) {
      strmcpy(NickName, st, sizeof(NickName));
      Status_dirty = 1;
      DirtyWindow();
   }
}

static void SLirc_SetServerName(char *st)
{
   if (strcmp(ServerName,st)) {
      strmcpy(ServerName, st, sizeof(ServerName));
      Status_dirty = 1;
   }
}

static void SLirc_SetTarget(char *st)
{
   if (IrcCmp(Target,st,-1)) {
      /* really changed */
      strmcpy(Target, st, sizeof(Target));
      Status_dirty = 1;
      DirtyWindow();
   }
}

static void SLirc_SetSrvTime(int *y)
{
   if (SrvTime != *y) {
	    SrvTime = *y;
      Status_dirty = 1;
      DirtyWindow();
   }
}

static void SLirc_SetShowInfo(int *y)
{
   int v = (*y != 0);
   if (ShowInfo != v) {
      /* really changed */
      ShowInfo = v;
      Status_dirty = 1;
      DirtyWindow();
   }
}

static void SetLoadPath(char *st)
{
   strmcpy(SLirc_Load_Path, st, 196);
}

static void SLirc_Colour(int *which, char *fg, char *bg)
{
   /*
    * which comes from a bunch of iconstants setup here.
    * Bit of a pain, but easier than having weird numbers in the scripts.
    */
   SLtt_set_color(*which, NULL, fg, bg);
}

void SLirc_Log(char *text)
{
   SLirc_Multn(text, NULL, NULL, IRC_TYPE_LOG);
}

static void SLirc_Info(char *text, char *to)
{
   SLirc_Multn(text, NULL, to, IRC_TYPE_INFO);
}

static void SLirc_Action(char *text, char *from, char *to)
{
   SLirc_Multn(text, from, to, IRC_TYPE_ACTION);
}

static void SLirc_Say(char *text, char *from, char *to)
{
   SLirc_Multn(text, from, to, IRC_TYPE_SAY);
}

void SLirc_Log_Error(char *st)
{
   static FILE *fp = NULL;
   if (!fp)
     fp = fopen("error_log", "w");
   SLirc_Log(st);
   if (fp)
     fprintf(fp, "%s\n", st);
}

static void InitMainScreen(void)
{
   SLsmg_cls();
   
   Init_Windows();
   strcpy(Top, " *** SLirc *** ");
   strcpy(Status, "*|*@*->*: Init");
   /* CommandBox.s[0] = '\0'; */
   
   DrawTop(1);
   DrawStatus(SLtt_Screen_Rows - 2);
   DrawEntryBox(&CommandBox, SLtt_Screen_Rows - 1);
   
   SLsmg_refresh();
}

/* shouldn't do much inside a signal-handler */
static void whoops_the_screen_size_has_changed(int sig)
{
   GotWinch = 1;
   SLsignal(SIGWINCH, whoops_the_screen_size_has_changed);
}

static int PU_dirty = 0;
static char *PU_title = NULL;
static char *PU_func;/* function to be executed with keypresses */
static int PU_maxln;
static int PU_i0;    /* 1st in window (advisory)  */
static int PU_ix;    /* currently selected        */
static int PU_ct;    /* number of items           */
static char* PU_items[250];

static void HadWinch(void)
{
   char buf[80];
   
   SLang_init_tty(abort_char, 0, 0); /* cslang.txt says do this after WINCH */
   SLtt_get_screen_size(); /* this IS necessary */
   SLsmg_init_smg(); /* does SLsmg_reset_smg() inside. */
   /* Note: SLsmg_init_smg() unblocks signals */
   
   sprintf(buf, "SIGWINCH: New size=%dx%d", SLtt_Screen_Rows, SLtt_Screen_Cols);
   SLirc_Log(buf);
   
   SLsmg_touch_lines(0, SLtt_Screen_Rows); /* probably redundant */
   LRU_dirty = 1;
   Top_dirty = 1;
   DirtyWindow();
   PM_dirty = 1;
   PU_dirty = 1;
   Status_dirty = 1;
   CommandBox.dirty = 1;
   GotWinch = 0;
}

static void DrawPopUp(void)
{
   int c0,r0;    /* column,row of upper-left corner of window box */
   int cs,rs;    /* # cols,rows available for items in window     */
   int i,r,c;
   int col_item;
   int col_isel;
   char *p0;
   char format[20];
   
   col_item = UseColours? ColPopItem:0;
   col_isel = UseColours? ColPopISel:1;
   
   /* these calculations try to center popup INSIDE the LW-window area: */
   rs = SLtt_Screen_Rows-6-PMnum; if (rs > PU_ct) rs = PU_ct;
   r0 =	(SLtt_Screen_Rows + PMnum - rs - 2)/2;
   cs = SLtt_Screen_Cols-4; if (cs > PU_maxln) cs = PU_maxln;
   c0 =	(SLtt_Screen_Cols - cs - 2)/2;
   
   p0 = SLmalloc(cs+1);  /* a buffer for truncation if necessary */
   
   /* make minimal adjustment of PU_i0, if necessary, so that */
   /* selected item PU_ix is IN window:                       */
   if (PU_ix < PU_i0)
     PU_i0 = PU_ix;
   else if (PU_ix - rs + 1 > PU_i0)
     PU_i0 = PU_ix - rs + 1;
   
   SLsmg_set_color(UseColours? ColPopBord:1);
   SLsmg_draw_box(r0,c0,rs+2,cs+2);
   if (!p0) return;
   strmcpy(p0,PU_title,cs+1);
   SLsmg_gotorc(r0, c0 + 1 + (cs-strlen(p0))/2);
   SLsmg_write_string(p0);
   
   sprintf(format,"%%-%ds",cs);
   
   r = r0+1;
   c = c0+1;
   
   for (i = PU_i0; i<PU_i0+rs; i++, r++) {
      
      SLsmg_gotorc(r, c);
      SLsmg_set_color((i == PU_ix)? col_isel : col_item);
      strmcpy(p0,PU_items[i],cs+1);
      SLsmg_printf(format,p0);
      /* SLsmg_write_char('>'); */
   }
   PU_dirty = 0;
   SLfree(p0);
}

static void SLirc_create_popup(char *title, char *items, char *handler)
{
   int ct, maxln = strlen(title);
   char *p,*q;
   
   if (PU_title) return; /* for now */
   PU_title = SLmake_string(title);
   PU_func = SLmake_string(handler);
   
   for (ct = 0, p = items; *p && ct < 250; p = q) {
      int l;
      q = index(p,'\n');
      if (!q) q = p + strlen(p);
      l = q-p;
      PU_items[ct++] = SLmake_nstring(p,l);
      if (maxln < l) maxln = l;
      if (*q) q++;
   }
   PU_maxln = maxln;
   PU_ct = ct;  /* the hard-coded limit of 250 will be eliminated some day :) */
   PU_i0 = 0;
   PU_ix = 0;
   PU_dirty = 1;
   return;
}

static void SLirc_destroy_popup(void)
{
   int i;
   
   if (!PU_title) return; /* for now */
   SLfree(PU_title);
   PU_title = NULL;
   SLfree(PU_func);
   for (i = 0; i < PU_ct; i++) SLfree(PU_items[i]);
   
   PU_dirty = 0;
   DirtyWindow();
   return;
}

static void DrawProgress(int start)
{
   struct _ProgressMeter *pm;
   int rows = start;
   
   for (pm = PMfirst; pm; pm = pm->next) {
      int width;
      int point;
      int percent;
      int i;
      char buf[8];
      
      SLsmg_gotorc(rows, 0);
      
      width = SLtt_Screen_Cols - strlen(pm->title) - 10;
      
      point = (pm->point * width) / pm->max;
      percent = (pm->point * 100) / pm->max;
      sprintf(buf, "%d", percent);
      
      SLsmg_set_color(UseColours? ColTopNorm : 1);
      SLsmg_write_string(pm->title);
      SLsmg_write_string(" : ");
      SLsmg_write_string(buf);
      SLsmg_write_string("% : |");
      for (i = 0; i < width; i++) {
	 if (i < point)
	   SLsmg_write_char('-');
	 else if (i == point)
	   SLsmg_write_char('>');
	 else
	   SLsmg_write_char(' ');
      }
      SLsmg_write_char('|');
      SLsmg_erase_eol();
      rows++;
   }
   PM_dirty = 0;
}

static int SLirc_create_progress_meter(char *title, int *scale)
{
   struct _ProgressMeter *pm = (struct _ProgressMeter *) SLmalloc(sizeof(struct _ProgressMeter));
   
   pm->prev = PMlast;
   pm->next = NULL;
   pm->title = SLmake_string(title);
   pm->max = *scale;
   pm->point = 0;
   
   /*
    * List insertion.
    */
   
   if (PMfirst && PMlast) 
     pm->prev->next = pm;
   else
     PMfirst = pm;
   
   PMlast = pm;
   
   PMnum++;
   
   /*
    * Find a tag.
    */
   
   if (PMfirst == pm)
     pm->number = 1;
   else
     pm->number = PMlast->number + 1;
   
   PM_dirty = 1;
   DirtyWindow();
   
   return (pm->number);
}

static void SLirc_destroy_progress_meter(int *which)
{
   struct _ProgressMeter *pm;
   
   for (pm = PMfirst; pm; pm = pm->next) {
      if (pm->number == *which)
	break;
   }
   
   if (pm) {
      if (pm->next)
	pm->next->prev = pm->prev;
      else
	PMlast = pm->prev;
      if (pm->prev)
	pm->prev->next = pm->next;
      else
	PMfirst = pm->next;
      SLfree(pm->title);
      SLfree((char*)pm);
      PMnum--;
      PM_dirty = 1;
      DirtyWindow();
   }
   
}

static void SLirc_set_progress_meter(int *which, int *point)
{
   struct _ProgressMeter *pm;
   
   for (pm = PMfirst; pm; pm = pm->next)
     if (pm->number == *which)
       break;
   
   if (pm) {
      int percent0,percent1,pt = *point;
      if (pt < 0) pt = 0;
      else if (pt > pm->max) pt = pm->max;
      percent0 = (pm->point * 100) / pm->max;
      percent1 = (pt * 100) / pm->max;
      pm->point = pt;
      if (percent0 != percent1) PM_dirty = 1; /* redisplay only if changed */
   }
   
}

void SLirc_UpdateDisplay()
{

	/* All well behaved applications should block signals that may affect
	 * the display while performing screen update.
	 */

	while (1) {
		SLsig_block_signals();
		if (!GotWinch) break;
		HadWinch();
		/* HadWinch() re-inits SLmsg,etc and marks everything dirty  */
		/* also, the SLsmg_init unblocks sigs, so we need to loop... */
	}

	if (LRU_dirty) DrawLRU(0);
	if (Top_dirty) DrawTop(1);
	if (PM_dirty) DrawProgress(2);
	if (DrawAllWindows(2+PMnum, SLtt_Screen_Rows - (4+PMnum)))
	   if (PU_title) PU_dirty=1;
	if (PU_dirty) DrawPopUp();
	if (Status_dirty) DrawStatus(SLtt_Screen_Rows - 2);
	DrawEntryBox(&CommandBox, SLtt_Screen_Rows - 1); /* positions cursor, too */
	SLsmg_refresh();

	SLsig_unblock_signals();
}

static void DoExit(char *st)
{
	SLsmg_reset_smg();
	SLang_reset_tty();
  if (VF_reset) SLexecute_function(VF_reset);
	SLang_doerror(st);
	puts(st);
	exit(1);
}

void Fatal(char *st)
{
	SLsmg_reset_smg();
	SLang_reset_tty();
  if (VF_reset) SLexecute_function(VF_reset);
	puts(st);
	exit(2);
}

static void ClearSomeErrors(void)
{
	if (SLang_Error && SLang_Error != USER_BREAK) {
		SLang_Error = 0;
		SLsmg_touch_lines(0, SLtt_Screen_Rows);
		SLirc_UpdateDisplay();
		SLang_input_pending(20);
	}
}

static int SLirc_define_keysym(char *st, int *ch)
{
	return SLkp_define_keysym(st, *ch);
}

static unsigned int KeySymNext = 0x1000;

typedef struct _KbdAssocArray {
	struct _KbdAssocArray *prev;
	struct _KbdAssocArray *next;
	unsigned int ksym;
	char *fn;
	unsigned int pass;
} KbdAssocArray;

KbdAssocArray *Ksyms = NULL;
KbdAssocArray *LastKsym = NULL;

static int SLirc_KdbAssoc(char *st, char *fn, int *pass)
{
	int res;
	KbdAssocArray *kbd = (KbdAssocArray *) SLmalloc(sizeof(KbdAssocArray));
	kbd->ksym = KeySymNext;
	kbd->fn = SLmake_string(fn);
	kbd->pass = *pass;
	kbd->prev = LastKsym;
	kbd->next = NULL;

	if (LastKsym)
		LastKsym->next = kbd;
	else
		Ksyms = kbd;

	res = SLkp_define_keysym(st, KeySymNext);
	KeySymNext++;

	LastKsym = kbd;

	return res;
}

static int SLirc_KdbAssocKeysym(int *ksym, char *fn, int *pass)
{
	int res = 1;
	KbdAssocArray *kbd = (KbdAssocArray *) SLmalloc(sizeof(KbdAssocArray));
	kbd->ksym = *ksym;
	kbd->fn = SLmake_string(fn);
	kbd->pass = *pass;
	kbd->prev = LastKsym;
	kbd->next = NULL;

	if (LastKsym)
		LastKsym->next = kbd;
	else
		Ksyms = kbd;

	LastKsym = kbd;

	return res;
}

static int SLirc_RunKeysym(unsigned int *ksym)
{
	int res = 0;
	int myksym = *ksym;
	KbdAssocArray *kbd;

	if (PU_title) {
		if (*ksym == SL_KEY_UP) {
			if (PU_ix > 0) PU_ix--;
			PU_dirty = 1;
		}else if (*ksym == SL_KEY_DOWN) {
			if (PU_ix < PU_ct - 1) PU_ix++;
			PU_dirty = 1;
		}else if (!*PU_func) {
			SLirc_destroy_popup();
			goto passthru;
		}else{ /* execute handler */
			SLang_push_integer(*ksym);
			SLang_push_integer(PU_ix);
			SLang_push_string(PU_items[PU_ix]);
			SLang_execute_function(PU_func);/* change this to use SLexecute? */
			ClearSomeErrors();
		}
		return -1;
	}
passthru:
	for (kbd = Ksyms; kbd; kbd = kbd->next) {
		if (kbd->ksym == myksym) {
			if (SLang_is_defined(kbd->fn)) {
				if (kbd->pass)
					SLang_push_integer(myksym);
				SLang_execute_function(kbd->fn); /* change this to use SLexecute? */
				ClearSomeErrors();
				res = 1;
			} else {
				SLirc_Log("Missing function?");
			}
		}
	}
	return (res);
}

#define I SLANG_INT_TYPE
#define S SLANG_STRING_TYPE
#define V SLANG_VOID_TYPE

static SLang_Intrin_Fun_Type SLirc_Fun_Table[] =
{
	MAKE_INTRINSIC_S("define_for_ifdef", SLdefine_for_ifdef, I),
	MAKE_INTRINSIC_S("irc_set_nickname", SLirc_SetNick, V),
	MAKE_INTRINSIC_S("irc_set_servername", SLirc_SetServerName, V),
	MAKE_INTRINSIC_S("irc_set_target", SLirc_SetTarget, V),
	MAKE_INTRINSIC_I("irc_set_srvtime", SLirc_SetSrvTime, V),
	MAKE_INTRINSIC_S("irc_parse_params", ParseReturn, I),
	MAKE_INTRINSIC_S("irc_set_path", SetLoadPath, V),
	MAKE_INTRINSIC_I("irc_set_showinfo", SLirc_SetShowInfo, V),
	
	MAKE_INTRINSIC_S("irc_set_buffer", SLirc_SetCommandBox, V),
	MAKE_INTRINSIC_S("irc_set_top", SetTop, V),
	MAKE_INTRINSIC_S("irc_set_status", SetStatus, V),
	MAKE_INTRINSIC_0("irc_update_display", SLirc_UpdateDisplay, V),

	MAKE_INTRINSIC_S("irc_log", SLirc_Log, V),
	MAKE_INTRINSIC_SS("irc_info", SLirc_Info, V),
	MAKE_INTRINSIC_SSS("irc_say", SLirc_Say, V),
	MAKE_INTRINSIC_SSS("irc_action", SLirc_Action, V),

	MAKE_INTRINSIC_S("irc_exit", DoExit, V),
	MAKE_INTRINSIC_0("irc_beep", SLtt_beep, V),

	MAKE_INTRINSIC_0("irc_get_input", SLkp_getkey, I),
	MAKE_INTRINSIC_SI("define_keysym", SLirc_define_keysym, I),
	MAKE_INTRINSIC_SSI("irc_set_key", SLirc_KdbAssoc, I),
	MAKE_INTRINSIC_ISI("irc_set_keysym", SLirc_KdbAssocKeysym, I),
	MAKE_INTRINSIC_I("irc_exec_key", SLirc_RunKeysym, I),
	MAKE_INTRINSIC_ISS("irc_set_colour", SLirc_Colour, V),

	MAKE_INTRINSIC_SI("irc_create_pm", SLirc_create_progress_meter, I),
	MAKE_INTRINSIC_II("irc_set_pm", SLirc_set_progress_meter, V),
	MAKE_INTRINSIC_I("irc_destroy_pm", SLirc_destroy_progress_meter, V),
	MAKE_INTRINSIC_SSS("irc_create_popup", SLirc_create_popup, V),
	MAKE_INTRINSIC_0("irc_destroy_popup", SLirc_destroy_popup, V),
	SLANG_END_TABLE
};

static SLang_Intrin_Var_Type SLirc_Var_Table[] =
{
	MAKE_VARIABLE("NullString", &NullStringP, S, 1),
	MAKE_VARIABLE("ProgName", &ProgNameP, S, 1),
	MAKE_VARIABLE("ProgVersion", &ProgVersionP, S, 1),
	MAKE_VARIABLE("slirc_home", &CFG_DIRP, S, 1),
	MAKE_VARIABLE("irc_nickname", &NickNameP, S, 1),
	MAKE_VARIABLE("irc_username", &UserNameP, S, 1),
	MAKE_VARIABLE("irc_clientname", &ClientNameP, S, 1),
	MAKE_VARIABLE("irc_servername", &ServerNameP, S, 1),
	MAKE_VARIABLE("irc_target", &TargetP, S, 1),
	MAKE_VARIABLE("irc_showinfo", &ShowInfo, I, 1),
	MAKE_VARIABLE("R_trailing", &R_trailing, I, 1),
	MAKE_VARIABLE("Rpms_ct", &Rpms_ct, I, 1),
	/* next few are re edit command-buffer */
	MAKE_VARIABLE("irc_buffer", &CommandTextP, S, 1),
	MAKE_VARIABLE("irc_edit_insert_pos", &CommandBox.Insert, I, 0),
	MAKE_VARIABLE("irc_command_mode", &CommandBox.mode, I, 0),
# if 0
	MAKE_VARIABLE("irc_recv_time", &LastRecvTime, SLANG_ULONG_TYPE, 0),
#	endif
	MAKE_VARIABLE("irc_use_colours", &UseColours, I, 0),
	SLANG_END_TABLE
};
#undef I
#undef S
#undef V

static SLang_IConstant_Type SLirc_Constants [] =
{
	MAKE_ICONSTANT("irc_col_default", ColDefault),
	MAKE_ICONSTANT("irc_col_top_norm", ColTopNorm),
	MAKE_ICONSTANT("irc_col_auto_resp", ColAutoResp),
	MAKE_ICONSTANT("irc_col_top_hi", ColTopHi),
	MAKE_ICONSTANT("irc_col_status_say", ColStatusSay),
	MAKE_ICONSTANT("irc_col_status_command", ColStatusCommand),
	MAKE_ICONSTANT("irc_col_log_tag", ColLogTag),
	MAKE_ICONSTANT("irc_col_log_text", ColLogText),
	MAKE_ICONSTANT("irc_col_info_text", ColInfoText),
	MAKE_ICONSTANT("irc_col_say_norm_send", ColSayNormSend),
	MAKE_ICONSTANT("irc_col_say_tgto_send", ColSayTgtoSend),
	MAKE_ICONSTANT("irc_col_say_norm_dec", ColSayNormDec),
	MAKE_ICONSTANT("irc_col_say_norm_text", ColSayNormText),
	MAKE_ICONSTANT("irc_col_say_hi_send", ColSayHiSend),
	MAKE_ICONSTANT("irc_col_say_hi_dec", ColSayHiDec),
	MAKE_ICONSTANT("irc_col_say_hi_text", ColSayHiText),
	MAKE_ICONSTANT("irc_col_say_me_send", ColSayMeSend),
	MAKE_ICONSTANT("irc_col_say_me_dec", ColSayMeDec),
	MAKE_ICONSTANT("irc_col_say_me_text", ColSayMeText),
	MAKE_ICONSTANT("irc_col_msg_norm_send", ColMsgNormSend),
	MAKE_ICONSTANT("irc_col_msg_norm_dec", ColMsgNormDec),
	MAKE_ICONSTANT("irc_col_msg_norm_text", ColMsgNormText),
	MAKE_ICONSTANT("irc_col_msg_me_send", ColMsgMeSend),
	MAKE_ICONSTANT("irc_col_msg_me_dec", ColMsgMeDec),
	MAKE_ICONSTANT("irc_col_msg_me_text", ColMsgMeText),
	MAKE_ICONSTANT("irc_col_buffer_prompt_say_norm", ColBufferPromptSayNorm),
	MAKE_ICONSTANT("irc_col_buffer_prompt_say_over", ColBufferPromptSayOver),
	MAKE_ICONSTANT("irc_col_buffer_prompt_cmd_norm", ColBufferPromptCommandNorm),
	MAKE_ICONSTANT("irc_col_buffer_prompt_cmd_over", ColBufferPromptCommandOver),
  MAKE_ICONSTANT("irc_col_buffer_text_say", ColBufferTextSay),
	MAKE_ICONSTANT("irc_col_buffer_text_command", ColBufferTextCommand),
	MAKE_ICONSTANT("irc_col_buffer_text_bold", ColBufferTextBold),
	MAKE_ICONSTANT("irc_col_popup_border", ColPopBord),
	MAKE_ICONSTANT("irc_col_popup_item", ColPopItem),
	MAKE_ICONSTANT("irc_col_popup_select", ColPopISel),
	SLANG_END_TABLE
};

static int init_SLirc(void)
{
   if(SLdefine_for_ifdef("SLIRC")) return 0;
   return (!SLadd_intrin_fun_table(SLirc_Fun_Table, "_SLirc") &&
	   !SLadd_intrin_var_table(SLirc_Var_Table, NULL) &&
	   !SLadd_iconstant_table (SLirc_Constants, NULL));
}

static int do_act_secs = 3;

int main(int argc, char **argv)
{
	unsigned int expiry;
	SLang_Name_Type *VF_do_actions;
	SLang_Name_Type *check_timed = NULL;

	SLirc_InitVarsDefault();
	SLtt_get_terminfo();					/* Get the terminal info. */
	SLtt_Use_Ansi_Colors = 1;
	if (-1 == SLkp_init()) {
		SLang_doerror("SLkp_init failed.");
		exit(1);
	}

	/*
	 * Note: cref.txt says param 3 (opost) of SLang_init_tty()
	 *       should be 0 when SLsmg is used.
	 *   (actually, opost's ignored in slang-1.0.3 )
	 */
	if (-1 == SLang_init_tty(abort_char, 0, 0)) {
		SLang_doerror("Unable to initialize the terminal.");
		exit(1);
	}
	SLsmg_init_smg(); /* this does get_screen_size inside. */
	SLang_set_abort_signal(NULL);
	InitMainScreen();
	SLang_VMessage_Hook = Local_vmessage;
	SLang_Load_File_Hook = Local_SLang_load_file;
	SLang_Error_Hook = SLirc_Log_Error;
	SLang_Dump_Routine = SLirc_Log_Error;
	/* VF_do_actions_Hook = SLirc_UpdateDisplay; */

	SLang_Traceback = 1;

	SLirc_Log("SLirc: Copyright 1997,1998 Dave Cridland, all rights reserved.");
	SLirc_Log("SLirc: Copyright 1997,1998 Stan Brooks, all rights reserved.");
	SLirc_Log("SLirc: Redistributable under GPL or PAL, see COPYRIGHT for info.");

	SetStatus("Initializing SLang...");
	if (SLang_init_slang() || SLang_init_slmath() ||
			SLang_init_posix_dir() || SLang_init_import())

		DoExit("Ooops. SLang init failed.");

	SetStatus("Initializing SLang SLirc API...");
	if (!init_SLirc())
		DoExit("Ooops. SLirc init failed.");

	if (!init_LRU())
		DoExit("Ooops. LRU init failed.");

	Create_Rpms_Array();

	/* try to trap SIGSEGV's */
	SLsignal(SIGSEGV, dflt_sig_handler);

	/*
	 * ignore the SIGPIPE's caused on TCP write to closed connection
	 * we just handle the EPIPE after write()
	 */
	SLsignal(SIGPIPE, SIG_IGN);

	SLsignal(SIGWINCH, whoops_the_screen_size_has_changed);

	SetStatus("Loading SLirc init script...");
	SLirc_UpdateDisplay();
	SLang_load_file(argv[1] ? argv[1] : "init.sl");
	if (SLang_Error) {
		SLang_doerror("Oh, shit. Who fucked up that file, then?");
		SLang_Error = 0;
	}
	SLtt_Use_Ansi_Colors = UseColours;
	SLirc_UpdateDisplay();

	VF_do_actions = SLang_get_function("do_actions");
	if (!VF_do_actions) Fatal("do_actions is undefined");
	VF_reset = SLang_get_function("vf_reset");
	if (!VF_reset) Fatal("vf_reset is undefined");
	check_timed = SLang_get_function("check_timed");
	if (!check_timed) Fatal("check_timed is undefined");
	expiry = time(NULL)+5;
	while (SLang_Error != USER_BREAK) {
		int r,stkdep;
		unsigned int tim;
		
		SLang_start_arg_list();
		SLang_push_integer(do_act_secs);
		SLang_end_arg_list();
		r = SLexecute_function(VF_do_actions);
		if (r==-1) Fatal("SLexecute_function(do_actions)?");
		if (-1==SLang_pop_integer(&r)) Fatal("do_actions(): no integer");
#		if 0
		 r = VF_do_actions(&do_act_secs); /* most everything executes from here */
		 /* r is how many SLang functions executed */
#		endif
		if (r>0 || GotWinch) SLirc_UpdateDisplay();
		ClearSomeErrors();
		tim = time(NULL);
		if (tim<expiry) continue;
		expiry += 5;
		stkdep = _SLstack_depth();
		r = SLexecute_function(check_timed);
		if (r==-1) Fatal("SLexecute_function(check_timed)?");
#		if 0
		r = SLang_execute_function("check_timed");
		if (!r) Fatal("check_timed is undefined");
#		endif
		SLirc_UpdateDisplay();
		stkdep = _SLstack_depth() - stkdep; /* % items left on stack. */
		if (stkdep && SLang_Error != USER_BREAK) {
			SLang_verror(SL_APPLICATION_ERROR,
		  	"Eeek... check_timed() left %d units on stack",stkdep);
			if (stkdep>0) SLdo_pop_n(stkdep);
			SLirc_UpdateDisplay();
		}
		ClearSomeErrors();
	}

	SLsmg_reset_smg();
	SLang_reset_tty();
  if (VF_reset) SLexecute_function(VF_reset);
	exit(0);
}


syntax highlighted by Code2HTML, v. 0.9.1