/*	to make most:	gcc -o irc smallirc.c -ltermcap
                        or cc -o smallirc smallirc.c -ltermcap
	under aix:	bsdcc -o smallirc smallirc.c -lcurses
	under hpux:	cc -o smallirc smallirc.c -lcurses

	PLEASE CHANGE THE DEFAULT SERVER TO ONE NEAR YOU

  Smallirc 1.1 - based on tinyirc.c by Nathan Laredo
                                       (nathan@eas.gatech.edu)

  Developed by  Mandar Mirashi (mmmirash@mailhost.ecn.uoknor.edu) 
                13/2/1994
  
  This is a beta release.

  New features / bugs fixed:
  * all commands made compatible with the 2.8 ':' syntax
  * handles multiple channels
  * reverse video status line, made status line more ircII'ish
  * played around with termcap to better format backspacing on input line
  * intelligent handling of excessively long private/public messages
  * New commands added:
      /me        /leave      /away      /whowas     /ping  
      /signoff   /topic      /clear     /server     /query
      /say       /help       /version   
  * /ignore coded in
  * shorthands /w (whois)  /m (msg)  /si (signoff) /n (notice) /j (join)
    added
  * /msg .   and  /msg ,  support added
  * Beautified numeric replies for motd, whois,whowas,list,names
  * Now does current channel substitution for '*' in /names *, etc.
  * Removed that annoying extra blank line
  * Added tabkey support

  To-do list:  /exec, /notify, /dcc, /set log on
 
  
Tested and verified working under:  Ultrix 4.2, OSF/1

ALL 2.8 server commands (*=sent to client also):
ADMIN AWAY CONNECT DIE *ERROR HASH HELP INFO *INVITE ISON *JOIN *KICK *KILL
LINKS LIST LUSERS *MODE MOTD NAMES *NICK NOTE *NOTICE OPER *PART PASS *PING
PONG *PRIVMSG *QUIT REHASH RESTART SERVER SQUIT STATS SUMMON TIME *TOPIC
TRACE USER USERHOST USERS VERSION *WALLOPS WHO WHOIS WHOWAS

*/

#define DEFAULTPORT	6667
#define DEFAULTSERVER	"okc.ok.us.undernet.org"
#define COMMANDCHAR	'/'
#define TINYSIZE 12322
#define NOT_TELNET
/* the following are machines known to be POSIX compliant --
   if you have trouble and it works after adding yours, let me know */

#ifdef	linux
#define _POSIX_VERSION
#endif

#include <stdio.h>
#ifndef _POSIX_VERSION
#include <sgtty.h>
#define	USE_OLD_TTY
#include <sys/ioctl.h>
#undef	USE_OLD_TTY
#ifndef	CBREAK
#define CBREAK RAW
#endif
#if !defined(sun) && !defined(sequent) && !defined(hpux) && \
	!defined(_AIX) && !defined(aix)
#include <strings.h>
#define strchr index
#else
#include <string.h>
#endif
#else
#include <string.h>
#include <termios.h>
#endif
#include <errno.h>
#include <sys/types.h>
#include <pwd.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <signal.h>

int	sockfd,sok=1,tty_des,CO,LI,_tty_ch,dumb=0;
int     iphead=0, imhead=0;
u_short	IRCPORT=DEFAULTPORT;
char hostname[64];
char	*ME,*MR,*CM,*CS,*CE,*CL;
char    bp[1024],*term,*ptr,buf[512],object[256],localhost[64];
char	sockbuf[512],inputbuf[512],IRCNICK[10],*token[256],*fromhost;
char	termcap[1024],curnick[10],gotnick[10],sentnick[10],*query,*ti;
char    *ipublic[20],*imsg[20];  /* ignore lists */
char	*tmsg[10];     /* tabkey */
int 	thead=0,ttop=0;
char *chanlist = NULL;
fd_set	readfs;
struct	timeval timeout;
struct	passwd *userinfo;
#ifdef	_POSIX_VERSION
struct	termios _tty;
tcflag_t _res_iflg, _res_lflg;
#define cbreak() (_tty.c_lflag&=~ICANON, \
	tcsetattr(_tty_ch, TCSANOW, &_tty))
#define noecho() (_tty.c_lflag &= ~(ECHO|ICRNL), \
	tcsetattr(_tty_ch, TCSADRAIN, &_tty))
#define savetty() ((void) tcgetattr(_tty_ch, &_tty), \
	_res_iflg = _tty.c_iflag, _res_lflg = _tty.c_lflag)
#define resetty() (_tty.c_iflag = _res_iflg, _tty.c_lflag = _res_lflg,\
	(void) tcsetattr(_tty_ch, TCSADRAIN, &_tty))
#define erasechar()	(_tty.c_cc[VERASE])
#else
struct	sgttyb _tty;
int	_res_flg;
#define cbreak() (_tty.sg_flags|=CBREAK, ioctl(_tty_ch, TIOCSETP, &_tty))
#define noecho() (_tty.sg_flags &= ~(ECHO|CRMOD), \
	ioctl(_tty_ch, TIOCSETP, &_tty))
#define savetty() ((void) ioctl(_tty_ch, TIOCGETP, &_tty), \
	_res_flg = _tty.sg_flags)
#define resetty() (_tty.sg_flags = _res_flg, \
	(void) ioctl(_tty_ch, TIOCSETP, &_tty))
#define erasechar()	(_tty.sg_erase)
#endif

static	int current=0; /* current position in input buffer */
static	int pos=0; /* position on input line */
static	int poslc=0; /* current "page" of 70 columns */
static	time_t idletimer; /* idle time for user */
int	putchar_x(c) int c; { putchar(c); }
#define	tputs_x(s) (tputs(s,0,putchar_x))

char *sn(st)
char *st;
{ 
if ((st) && (*st))
 if (st[strlen(st)-1] == '\n')
   st[strlen(st)-1] = '\0';
return(st);
}

int call_socket(hostname)
  char *hostname;
{ struct sockaddr_in sa;
  struct hostent     *hp;
  int    a, s;
  if((hp=gethostbyname(hostname))==NULL) {
	errno=ECONNREFUSED; 
	return(-1); }
  bzero(&sa, sizeof(sa));
  bcopy(hp->h_addr, (char *)&sa.sin_addr, hp->h_length);
  sa.sin_family = hp->h_addrtype;
  sa.sin_port = htons((u_short)IRCPORT);
  if((s=socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0) return(-1);
  if(connect(s,(struct sockaddr *) &sa, sizeof(sa)) < 0) {
	close(s); 
	return(-1); }
  return(s);
}

char *left(st, n)
char *st;
int n;
{ char *s;int i;

s = (char *)malloc(n + 1);
for (i=0;i<n; i++)
   s[i] = st[i];
s[n] = '\0';
return(s);
}

/* upper() could cause memory leaks if you leave dangling pointers
   Remember, upper mallocs, so free the "upped" string if you don't
   plan to use it - Mmmm   */

char *upper(st)
char *st;
{ char *s; int i = 0,k;

k= strlen(st);
s = (char *)malloc(k+1);
while (i<k)
 { s[i] = toupper(st[i]); i++;}
s[i] = '\0';
return(s);
}

int readln()
{ int i=0,valid=1; char c;
while(valid) 
 {
   if(read(sockfd,&c,1)<1) return(0);
   if(i<511 && c != '\n' && c != '\r') 
     sockbuf[i++]=c; 
   else 
     valid=0; 
 }
sockbuf[i]='\0'; return(1);
}

updatestatus()
{ time_t now;
  char st[200];
  char *s;
  int i;

if(!dumb) {
  tputs_x(tgoto(CM, 0, LI-2)); tputs_x(MR);
   now=time(NULL);
  *((strchr(ti=ctime(&now),'\n')))='\0';
  strtok(ti," ");
  for (i=0;i<3;i++) s = strtok(NULL," ");
  s[strlen(s)-3] = '\0';
  if (!query)
    { char *s1;
      s1 = left(object,10);
      sprintf(st,"%s  %s on %s : Smallirc 1.1.beta.Mmmm",s,
	curnick,s1);
      free(s1);
    } 
  else
   { char *s1, *s2;
      s1 = left(object,10);
      s2 = left(query,10);
    sprintf(st,"%s  %s [Query: %s] on %s : Smallirc 1.1.beta.Mmmm",s,
	curnick,s2,s1);
     free(s1);free(s2);
   }
  printf("%s",st);
  tputs_x(CE);
  for (i=0;i<(CO-strlen(st));i++) printf(" ");
  tputs_x(ME); }
}

int writeln(outbuf)
char *outbuf;
{ int to=0;
if(write(sockfd, outbuf, strlen(outbuf)) < to )
	return(0);
return(1);
}

finishline(start)
int start; 
{ int i,ii; 
i=start; ii=0;
while (token[i]) 
  { ii += strlen(token[i]);
    if (ii > CO-10) {printf("\r\n");ii = 0;}
    printf(" %s",sn(token[i++])); 
  }
}

fline(o,start)
int o,start; 
{ int i,ii; 
i=start; ii=0;
while (token[i]) 
  { ii += strlen(token[i]);
    if (ii > CO-o) { printf("\r\n"); ii = 0;o = 20;}
    printf(" %s",sn(token[i++])); 
  }
}

sline(o,s)
int o;
char *s;
{ int i=0,ii=0;
while (s[i])
 { ii++;
   if (ii > CO-o) {printf("\r\n");ii = 0; o = 5;}
   printf("%c", s[i]);
   i++;
 }
}


dojoin()
{ char *c; int i,k;
  char *s;

   if(strcmp(token[0],IRCNICK)==0) 
     {
	strcpy(object,&token[2][1]);
        if (chanlist)
         { 
            s = (char *)malloc(strlen(chanlist)+strlen(&token[2][1])+1);
            strcpy(s, chanlist);
            strcat(s,&token[2][1]); 
            free(chanlist);
            chanlist = s;
          }
        else 
         { chanlist = (char *) malloc (strlen(&token[2][1])+1);
           strcpy(chanlist, &token[2][1]);
         }
     printf("\n*** %s (%s) has joined channel %s",token[0],fromhost,
						sn(&token[2][1]));
	updatestatus();
     }
  else printf("\n*** %s (%s) has joined channel %s",token[0],fromhost,
        sn(&token[2][1]));
}

dopart()
{ char *c,*s;
  int i=0;

  if(strcmp(token[0],IRCNICK)==0) 
   { 
    
    s = (char *)malloc(strlen(chanlist)-strlen(token[2])+1);
    c=strtok(chanlist,"#");
    if (strcmp(c,&token[2][1]) !=0)
     { strcpy(s,"#"); strcat(s,c); i=1;}
    while(c=strtok(NULL, "#"))
      { 
        if (strcmp(c,&token[2][1]) !=0)
          { strcat(s,"#"); strcat(s,c); i = 1;}
       }
    free(chanlist); 
    chanlist = s;
    if (i)
      strcpy(object, strrchr(chanlist,'#'));
    else { chanlist = NULL; strcpy(object,"*"); }
    printf("\n*** You have left channel %s",sn(token[2]));
    updatestatus();
   }
  else printf("\n*** %s has left channel %s",token[0],
	sn(token[2]));
}

donick()
{ if(strcmp(token[0],IRCNICK)==0) { strcpy(IRCNICK,&token[2][1]);
	printf("\n*** You are now known as %s", sn(&token[2][1]));
        strcpy(curnick,&token[2][1]);
	updatestatus();
	}
  else printf("\n*** %s is now known as %s",token[0],sn(&token[2][1]));
}


match(s1, s2)
char *s1, *s2;
{
char *i, *j;

if (!strcmp(s2,"*")) return(1);
i = upper(s1); j = s2;
while ((*i) && (*j))
{ if ((*j) == '?')
   { i++; j++;}
  else if ((*j) == '*')
   { char ss[512], *s3, *s4;
 
     j++;
     strcpy(ss, j);
     s3 = strtok(ss, "*?");
     s4 = strstr(i, s3);
     if (!s4) return(0); 
     else i = s4;
   }
  else if (toupper(*i) != toupper(*j))
      return(0);
  else {i++; j++;}
}
if (!(*i) && !(*j)) return(1);
else return(0);
}


isignored(n, h,  a, ix)
char *n, *h;
char *a[];
int ix;
{
int i;
char *s1, *s2, *s3, *s4, *s5;
char ss[512], st[512];

if (!ix) return(0);
strcpy(ss,h);
s1 = strtok(ss,"@");
s2 = strtok(NULL,"@");
for (i=0; i<ix; i++)
 { strcpy(st,a[i]);
   s3 = strtok(st, "!");
   s4 = strtok(NULL,"@");
   s5 = strtok(NULL,"@");
   if (match(n,s3) && match(s1,s4) && match(s2,s5)) 
      return(1);
 }
return(0);
}

doprivmsg()
{ int i=4,noctcp=0;
if(*(++token[3])=='\01') {
     if (!isignored(token[0],fromhost,imsg,imhead))
        {
	if(*(token[3]+1)=='A') {
	printf("\n*** ACTION: %s",sn(token[0])); noctcp=1; }
	else printf("\n*** CTCP %s from %s",++token[3], sn(token[0])); 
	}
     else return(0);
                       }
else {
	if(*token[2] != '#') 
          {if (!isignored(token[0],fromhost,imsg,imhead))
             {
            tputs_x(MR);printf("\n*%s*",token[0]); 
            strcpy(gotnick,token[0]);tputs_x(ME);i=3; noctcp=1; 
            update_tstack(gotnick);
               }
            else return(0);
          }
	else {
        if (!isignored(token[0],fromhost,ipublic,iphead))
          {char *s1, *s2;
           s1 = upper(object); s2 = upper(token[2]);
	if(strcmp(s1,s2)!=0) 
          printf("\n<%s:%s>",token[0],token[2]);
	else 
           printf("\n<%s>",token[0]); 
        free(s1); free(s2);
	i=3; noctcp=1; 
           }
        else return(0);
                }
      }
fline(27,i); if(noctcp) return(1);
/* CTCP requests */
if(*token[3]=='V') {
	sprintf(buf,"NOTICE %s :\01VERSION Smallirc 1.1.beta.Mmmm *IX \01\n",
	token[0]); writeln(buf); }
else if(*token[3]=='P') {
         
	sprintf(buf,"NOTICE %s :\01PING %s\n",token[0],token[4]);
	writeln(buf); 
               }
else if(*token[3]=='F') {
	sprintf(buf,"NOTICE %s :\01FINGER %s (%s@%s) Idle %d seconds\01\n",
	token[0],userinfo->pw_gecos,userinfo->pw_name,localhost,
	time(NULL)-idletimer); writeln(buf); }
else if(*token[3]=='C') { sprintf(buf,"NOTICE %s :\01CLIENTINFO %s\01\n",
	token[0],"VERSION FINGER CLIENTINFO PID ERRMSG"); writeln(buf); }
else if(*token[3]=='E') { sprintf(buf,"NOTICE %s :\01%s %s %s %s\n", token[0],
	token[3],token[4],token[5],token[6]); writeln(buf); }
else if(*token[3]=='D') { sprintf(buf,"NOTICE %s :\01DCC not supported\01\n",
	token[0]); writeln(buf); }
else { sprintf(buf,"NOTICE %s :\01ERRMSG %s\01\n",token[0],
	"I'm sorry dave, I'm afraid I can't do that"); writeln(buf); }
return(1);
}

donotice()
{ int j=3;
if(token[3][1]=='\01') /* ctcp reply */
{ if (strcmp(token[3],":\01PING") == 0)
     {
          long timediff, currenttime;
         currenttime = time(NULL);
         if ((token[4]) && (*(token[4])))
            { *(strchr(token[4],'')) = '\0'; 
              timediff = currenttime-atol(token[4]); 
            }
         else
             timediff = (long) 0;  
         free(token[4]);
         token[4] = (char *)malloc(20);
	 sprintf(token[4],"%d second%s\01\n",timediff,
                         (timediff == 1) ? "" : "s"); 
    }
  printf("\n*** CTCP %s reply from %s:",++token[j++],sn(token[0]));
  fline(40,j);
}
else if (strchr(token[0],'.')==0) 
    { printf("\n-%s- %s",token[0],sn(&token[3][1]));j++;
      finishline(j);
    }
}

spitout()
{ int i,num,newline;
  char *temp,*s,*c;

if(strncmp(sockbuf,"PI",2)==0) { strncpy(sockbuf,"PONG :",5);
           strcat(sockbuf, DEFAULTSERVER);
	return(writeln(strcat(sockbuf,"\n"))); } /* ping - pong */
/* parse lines from server for output to terminal */
if(!dumb) tputs_x(tgoto(CM,0,LI-3));
token[i=0]=strtok(sockbuf," ");
while(token[++i]=strtok(NULL, " ")); token[i]=NULL;
if (token[0])
{
if(*token[0] != ':') { finishline(2); putchar('\n'); return(0); }
	else token[0]++;
if(temp=strchr(token[0],'!')) { *temp='\0'; fromhost=temp+1; }
if(num=atoi(token[1])) /* this is quite extensible, ya know... */
	switch(num) { /* server-numerics */
	case 352: /* rpl_who reply */
	  printf("\n%-15s %-10s %3s %s@%s",token[3],token[7],
		token[8],token[4],token[5]); finishline(9);
	  break;
         case 311:  /* whois reply */
            printf("\n*** %s is %s@%s (%s",token[3], token[4], token[5],
                                                           &token[7][1]);
             fline(60,8);printf(")");
            break;
         case 401:    /* no such nick - do auto whowas */
            printf("\n*** %s: No such nickname", token[3]);
            writeln("WHOWAS ");writeln(token[3]);writeln("\n");
            break; 
         case 314:  /* whowas reply */
            printf("\n*** %s was %s@%s (%s",token[3], token[4], token[5],
                                                           &token[7][1]);
             fline(60,8);printf(")");
            break;
         case 319: printf("\n*** on channels %s", &token[4][1] );
                    fline(60,5);
            break;
         case 312: printf("\n*** on irc via server %s (%s", 
                                        token[4],&token[5][1]);
                   fline(70,6); printf(")");
             break;
         case 313: printf("\n*** %s is an IRC operator", token[3]);
              break;
         case 317: printf("\n*** %s has been idle for %s seconds", token[3],
								token[4]);
             break;
         case 301: printf("\n*** %s is away: %s", token[3], &token[4][1]);
                   fline(40,5);
             break;
         case 332: printf("\n*** Topic for %s is %s", token[3], &token[4][1]);
                   fline(40,5);
             break;
         case 353: { char *sss;
                     sss = left(token[4],10);
                    if (strcmp(token[3],"=")==0)
                       printf("\nPub:\t%s", sn(sss));
                   else  
                       printf("\nPrv:\t%s", sn(sss));
                    free(sss);
                   if (strlen(token[4]) > 10) printf(">");
                   else  
                     for (i=0;i< 10-strlen(token[4]);i++) printf(" ");
                   printf("\t%s",&token[5][1]);
                   fline(40,6);
                   }
                   break;
         case 321: printf("\nChannel \t Users \t Topic");
             break;
         case 322: {char *sss;
                   sss = left(token[3],10); 
                   printf("\n%s", sss);free(sss);
                   if (strlen(token[3]) > 10) printf(">");
                   else  
                     for (i=0;i< 10-strlen(token[3]);i++) printf(" ");
                   printf("\t %s \t %s", token[4], &token[5][1]);
                   fline(40,6);
                  }
             break;
         case 372: printf("\n*** %s", sn(&token[3][1]));   /* motd reply */
                   finishline(4);
              break;
         case 315:   /* end of who */
         case 318:   /* end of whois */
         case 365:   /*end of links */
         case 366:   /*end of names */
         case 368:   /*end of bans */
         case 369:   /* end of whowas */
         case 323:   /* end of list */
         case 376:   /* end of motd */
         case 219:   /* end of stats */
             break; 
                   
	default:
	  /* printf("%s",token[1]); finishline(3);  */
          printf("\n*** ");
          if (*token[3] == ':') 
            printf("%s", sn(&token[3][1]));
          else printf("%s", sn(token[3]));
          fline(35,4); 
	  break;
	}
else if(*token[1]=='P') 
       if(*(token[1]+1)=='R') 
             newline = doprivmsg(); 
       else dopart();
else if(*token[1]=='N') if(*(token[1]+1)=='O') donotice(); else donick();
else if(*token[1]=='J') dojoin();
else if(*token[1]=='Q') { printf("\n*** Signoff: %s (%s",token[0],&token[2][1]); 
                          fline(40,3); 
                          printf(")");}
else if(*token[1]=='T') {
	printf("\n*** %s has changed the topic on channel %s to %s",token[0],
                                                   token[2], sn(&token[3][1]));
	fline(70,4); }
else if (*token[1]=='W') 
    {printf("\n!%s! %s", token[0], sn(&token[2][1])); fline(10,3);}
else if(*token[1]=='I') printf("\n*** %s invites you to channel %s",token[0],
                                                            sn(&token[3][1]));
else if(*token[1]=='M') {
        if (strcmp(token[2],token[0]) != 0)
        {
        if (token[4])
           { int k=5;
             printf("\n*** Mode change \"%s %s", token[3], token[4]);
             while (token[k]) printf(" %s",token[k++]);
             printf("\" on channel %s by %s", token[2], sn(token[0]));
           }
        else 
	printf("\n*** Mode change \"%s\" on channel %s by %s",token[3],
                                                     token[2], sn(token[0]));
         }
        else
	printf("\n*** Mode change \"%s\" for user %s by %s",&token[3][1],
                                                     token[2], sn(token[0]));
         
	 }
else if(!strcmp(token[1],"KICK")) {
        if (strcmp(IRCNICK,token[3]) != 0)
	  printf("\n*** %s has been kicked off channel %s by %s (%s",token[3],
                                        token[2],token[0],sn(&token[4][1]));
        else
	  {printf("\n*** You have been kicked off channel %s by %s (%s",
                                       token[2],token[0],sn(&token[4][1]));
           i = 0; 
           s = (char *)malloc(strlen(chanlist)-strlen(token[2])+1);
           c=strtok(chanlist,"#");
           if (strcmp(c,&token[2][1]) !=0)
               { strcpy(s,"#"); strcat(s,c); i=1;}
           while(c=strtok(NULL, "#"))
             { 
              if (strcmp(c,&token[2][1]) !=0)
                { strcat(s,"#"); strcat(s,c); i = 1;}
             }
           free(chanlist); 
           chanlist = s;
           if (i)
             strcpy(object, strrchr(chanlist,'#'));
           else {chanlist = NULL; strcpy(object, "*"); }
           fline(60,5); printf(")"); updatestatus();
           }
      }
else if(*token[1]=='E') { printf("\n*** ERROR:"); finishline(2); }
else { printf("\n*** odd server stuff:"); finishline(0); }
/*if (newline) putchar('\n'); */
if(!dumb) fflush(stdout);
}
}


connectclient(new)
int new;
{ char *s, *t;
printf("\n*** trying port %d of %s\n\n\n",IRCPORT,hostname);
if ((sockfd=call_socket(hostname))==-1) {
	fprintf(stderr, "\n*** connection refused, aborting\n", hostname);
	exit(0); }
if (!new)
{ sprintf(buf,"NICK %s\n",IRCNICK); strcpy(curnick,left(IRCNICK,9));}
else 
sprintf(buf, "NICK %s\n",curnick);
writeln(buf); gethostname(localhost, 64);
if (!getenv("IRCNAME")) sprintf(buf, "USER %s %s %s :%s\n", userinfo->pw_name,
	localhost,hostname,userinfo->pw_gecos);
else sprintf(buf, "USER %s %s %s :%s\n", userinfo->pw_name,localhost,
	hostname,getenv("IRCNAME"));
if (getenv("IRCNAME")) 
   puts("yes!!!!!!!!!");
writeln(buf); 
if (!new)
strcpy(object,"*"); 
else
{ 
  if (chanlist)
    { s = (char *)malloc(strlen(chanlist) + 1);
      strcpy(s,chanlist);free(chanlist);
      t = strtok(s,"#");
      writeln("JOIN #");writeln(t);writeln("\n");
      while (t=strtok(NULL,"#")) 
         { writeln("JOIN #");writeln(t);writeln("\n"); }
    }
}
idletimer=time(NULL);
}

ignoreadd(s, a, ix)
char *s;
char *a[];
int *ix;
{ int ii;
if (*ix ==19)
   {printf("\n*** Ignore list full! Discarding %s",a[0]);
    ii=0;free(a[ii]);
    }
else {ii=*ix; (*ix)++;}
a[ii] = (char *) malloc (strlen(s) + 1);
strcpy(a[ii],upper(s));
}
      

ignoredel(s, a, ix)
char *s;
char *a[];
int *ix;
{ int ii, k = 0;
char *s1;

if (*ix == 0)
   {printf("\n*** Ignore list empty!");
    }
else {
s1 = upper(s);
for (ii = 0; ii < *ix; ii++)
{ if (!strcmp(a[ii],s1))
    { int j;
      k = 1;
      for (j=ii; j< (*ix)-1; j++)
       { free(a[j]);
         a[j] = (char *) malloc(strlen(a[j+1])+1);
         strcpy(a[j], a[j+1]); 
       }
      free(a[(*ix)-1]);
      a[(*ix)-1] = NULL;
      break;
    }
}
if (k) --(*ix);
else printf("\n%s not in ignorance list!",s);
free(s1);
}
}

update_tstack(s)
char *s;
{
int tt=-1, i;
char *s1, *s2;

for (i=0; i<ttop; i++)
   { s1 = upper(s); s2 = upper(tmsg[i]); 
     if (!strcmp(s1, s2))
       { tt = i; break; }
     free(s1); free(s2);
   }
if (tt == -1)   /* message from/to a new person */
 { if (ttop < 10) 
    { tmsg[ttop] = (char *) malloc (strlen(s)+1);
      strcpy(tmsg[ttop], s);
      ttop++; 
    }
   else     /* stack full - discard least recently used */
    { free(tmsg[0]);
      for (i=0; i<9;i++)
      tmsg[i] = tmsg[i+1];
      tmsg[9] = (char *) malloc (strlen(s)+1);
      strcpy(tmsg[9], s);
    }
 }
else   /* message found within stack */
  { char *tmp;
    tmp = tmsg[tt];
    for (i=tt; i<ttop-1; i++)
       tmsg[i] = tmsg[i+1];
    tmsg[ttop-1] = tmp;
  }
thead = ttop - 1;
}

int parseinput()
{ int ii=1, i, k,kk,t;
  char st[2], *qq, *s, *c, *s1, *y, *z;

if(!dumb) 
{
 tputs_x(tgoto(CM,0,LI-1)); tputs_x(CE); tputs_x(tgoto(CM,0,LI-3));
 inputbuf[current]='\0'; current=0; 
}
if ((inputbuf[0] == '\n') || (inputbuf[0] == '\r') 
       ||(inputbuf[0] == '\0')) 
         strcpy(inputbuf, inputbuf+1);
if ((query)&& (inputbuf[0] != COMMANDCHAR))
  { qq = (char *)malloc(strlen(inputbuf) + strlen(query) + 7);
    sprintf(qq,"/MSG %s %s",query,inputbuf);
    strcpy(inputbuf,qq);
    free(qq);
  }
if(inputbuf[0]==COMMANDCHAR) 
 {
y = strchr(inputbuf, '*');
z = strchr(inputbuf, ' ');
if (y == z+1)
  { s1 = (char *) malloc(strlen(inputbuf) + strlen(object));
    *y = '\0';
    strcpy(s1, inputbuf);
    strcat(s1, object);
    if (*(y+1)) strcat(s1, y+1);
    strcpy(inputbuf, s1);
    free(s1);
  }
}
s1 = upper(inputbuf);
if (strncmp(s1,"/SAY ",5) ==0)
 { qq = &inputbuf[5];
   strcpy(inputbuf,qq);
 }
if(inputbuf[0]==COMMANDCHAR) 
 {
  switch(toupper(inputbuf[1])) 
     {
	case 'Q': if (strncmp(s1,"/QUERY", 6) == 0)
                    { if ((inputbuf[6] != ' ')&&(query))
                        { printf("\n*** Ending conversation with %s",
						sn(query));
                          free(query); query = (char *)NULL; 
                          updatestatus();
                         }
                      else 
                        { query = (char *)malloc(strlen(&inputbuf[7])+1);
                          strcpy(query,&inputbuf[7]);
                          *(strchr(query,'\n')) = '\0'; 
                          printf("\n*** Starting conversation with %s",
                                                                sn(query));
                          updatestatus();
                        } 
                      ii=0;
                    }
                  else
                   { if (strncmp(s1,"/QUIT", 5) == 0)
                      if(strlen(inputbuf) < 7) {
		        strcat(inputbuf,":Smallirc 1.1.beta.Mmmm Wuz Hea\n");
		         *(strchr(inputbuf,'\n'))=' '; }
                      else 
                        { writeln("QUIT :");
                          writeln(&inputbuf[6]); ii=0; 
                        }
                   }
		break;
        case 'S': if (toupper(inputbuf[2]) == 'I') 
                    writeln("QUIT :Smallirc 1.1.beta.Mmmm Wuz Hea\n");
#ifdef NOT_TELNET
                  else if (strncmp(s1,"/SERVER ",8) ==0)
                    { writeln("QUIT :changing servers");      
                      strcpy(hostname, &inputbuf[8]); 
                      *(strchr(hostname,'\n')) = '\0';
                      close(sockfd);
                      connectclient(1);ii=0;
                    }
                      
#endif
                break;
        case 'L': if (strncmp(s1,"/LEAVE ",7) == 0)
                    { writeln("PART ");writeln(&inputbuf[7]); ii = 0;}
                  break;
        case 'A': if (strncmp(s1,"/AWAY ",6) == 0)
                     { writeln("AWAY :");writeln(&inputbuf[6]); ii=0;}
                  break;
        case 'V': if (strncmp(s1,"/VERSION",8) == 0)
                  { printf("\n*** Client version: Smallirc 1.1.beta.Mmmm\r\n");
                    printf("*** Server version:");
                   }
                  break;
        case 'H': if (strncmp(s1,"/HELP",5) == 0)
                    {
                printf("\n*** SmallIRC -> Not all commands are supported\r\n");
                printf("*** Sending help request to service ircIIhelp...");
                      writeln("PRIVMSG ircIIhelp :");
                      if (strchr(inputbuf, ' '))
                        writeln(strchr(inputbuf,' ')+1);
                      else
                        writeln(&inputbuf[1]);
                      ii=0;
                   }
                  break;
        case 'I': if (strncmp(s1,"/IGNORE",7) == 0)
                    { ii = 0;
                      if (strlen(inputbuf) < 10)
                        { printf("\n*** MSGS ignorance list:");
                          if (imhead) 
                            for (i=0;i<imhead;i++) printf("\r\n%s",imsg[i]);
                          else 
                printf("\r\nEmpty! Not ignoring public replies from anyone");
                          printf("\r\n*** PUBLIC ignorance list:");
                          if (imhead) 
                          for (i=0;i<iphead;i++) printf("\r\n%s",ipublic[i]);
               else printf("\r\nEmpty! Not ignoring messages from anyone");
                        }
                    else { char s[512],ss[512];
                           strcpy(ss,&inputbuf[8]);
                           if (ss[strlen(ss)-1] == '\n') 
                              ss[strlen(ss)-1] = '\0';
                           if (inputbuf[8] != '-')
                            { strcpy(s,strtok(ss," \n"));
                              if (!strchr(s,'!')&& (!strchr(s,'@'))) 
                                  strcat(s,"!*@*");
                              else if (!strchr(s,'!')&& (strchr(s,'@'))) 
                                 { strcpy(ss,"*!");
                                   strcat(ss,s);strcpy(s,ss);
                                 }
                             if (strstr(s1," MSG"))
                              { printf("\n*** Now ignoring messages from %s",s);
                                ignoreadd(s,imsg,&imhead);
                              }
                            else if (strstr(s1," PUBLIC"))
                              { ignoreadd(s,ipublic,&iphead); 
                                printf("\n*** Now ignoring publics from %s",s);
                              }
                            else /* if (strstr(s1," ALL")) */
                           { printf("\n*** Now ignoring ALL messages from %s",s);
                             ignoreadd(s,imsg,&imhead);
                             ignoreadd(s,ipublic,&iphead); 
                            }
                           }
                           else  /* removing */
                            { strcpy(s,strtok(ss," \n"));
                              if (!strchr(s,'!')&& (!strchr(s,'@'))) 
                                  strcat(s,"!*@*");
                              else if (!strchr(s,'!')&& (strchr(s,'@'))) 
                                 { strcpy(ss,"*!");
                                   strcat(ss,&s[1]);strcpy(s,ss);
                                 }
                             if (strstr(s1," MSG"))
                          { printf("\n*** Not ignoring messages from %s",&s[1]);
                                ignoredel(&s[1],imsg,&imhead);
                              }
                            else if (strstr(s1," PUBLIC"))
                              { ignoredel(&s[1],ipublic,&iphead); 
                             printf("\n*** Not ignoring publics from %s",&s[1]);
                              }
                            else /* if (strstr(s1," ALL")) */
                     { printf("\n*** Not ignoring ALL messages from %s",&s[1]);
                              ignoredel(&s[1],imsg,&imhead);
                              ignoredel(&s[1],ipublic,&iphead); 
                            }
                           }
                        }         

                   }
                       break;   

        case 'T': if (strncmp(s1,"/TOPIC ",7) == 0)
                  { writeln("TOPIC "); ii=0; t=7;
                  while ((inputbuf[t] != ' ') && (inputbuf[t] != '\0'))
                  { st[0] = inputbuf[t++]; 
                    st[1] = '\0';
                    writeln(st);
                  }
                  if (inputbuf[t])
                  {
                    writeln(" :");t++;
		    writeln(&inputbuf[t]); 
                  }
                  }
                  break;
	case 'N': if(inputbuf[2]==' ') 
                    t = 3;
                  else if (strncmp(s1,"/NOTICE ",8) == 0)
                     t = 8;
                  else break; 
                  writeln("NOTICE ");
                  printf("\n-> -");
                  while ((inputbuf[t] != ' ') && (inputbuf[t] != '\0'))
                  { st[0] = inputbuf[t++]; 
                    st[1] = '\0';
                    writeln(st);printf("%s",st);
                  }
                  writeln(" :");t++;
		  writeln(&inputbuf[t]);
                  printf("- %s",sn(&inputbuf[t])); 
                  ii=0; 
             break;
        case 'W': if (inputbuf[2] == ' ') 
                    {writeln("WHOIS ");writeln(&inputbuf[3]);ii = 0; }
             break;
	case 'J': if(inputbuf[2]==' ') 
                    { kk = 3;ii = 0;}
                  else if (strncmp(s1,"/JOIN ",6) == 0)
                     { kk = 6; ii=0;}
                   else break;
                   writeln("JOIN ");writeln(&inputbuf[kk]);
                   *(strchr(&inputbuf[kk],'\n')) = '\0';
                   if ((chanlist) && (strstr(chanlist,&inputbuf[kk])))
                      { 
                        i = 0; 
                        s = (char *)malloc(strlen(chanlist)+1);
                        c=strtok(chanlist,"#");
                        if (strcmp(c,&inputbuf[kk+1]) !=0)
                        { strcpy(s,"#"); strcat(s,c); i=1;}
                        while(c=strtok(NULL, "#"))
                            { if (strcmp(c,&inputbuf[kk+1]) !=0)
                                { strcat(s,"#"); strcat(s,c); i = 1;}
                             }
                         free(chanlist); 
                         chanlist = s;
                         strcat(chanlist,&inputbuf[kk]);
                         printf ("\n*** You are now talking to channel %s",
                                                         sn(&inputbuf[kk]));
                         strcpy(object, strrchr(chanlist,'#'));
                         updatestatus();
                      }
                  break;
	case 'M': if(inputbuf[2]==' ') 
                     t = 3;
                  else if (strncmp(s1,"/MSG ",5) == 0)
                     t = 5;
                  else if (strncmp(s1,"/ME ",4) == 0)
                    { writeln("PRIVMSG ");writeln(object);
                      writeln(" :ACTION ");
                      printf("\n*** Action: %s ",IRCNICK);
		      *(strchr(inputbuf,'\n'))= '';
                       strcat(inputbuf,"\n"); 
                      writeln(&inputbuf[4]);
                      sline(20,sn(&inputbuf[4]));
                      ii=0;
                      break;
                    }
                  else
                     break; 
                  writeln("PRIVMSG ");
                  printf("\n-> *");
                  if ((inputbuf[t] != '.')&&(inputbuf[t] != ','))
                  { int doit = 1, tt;
                   if (inputbuf[t] != '#') strcpy(sentnick,"");
                   else doit = 0;
                   while ((inputbuf[t] != ' ') && (inputbuf[t] != '\0'))
                    { st[0] = inputbuf[t++];
                      st[1] = '\0';
                      if (doit) strcat(sentnick,st);
                      writeln(st);printf("%s",st);
                    }
                   update_tstack(sentnick);
                  }
                  else
                  { if ((inputbuf[t] == '.'))
                        {writeln(sentnick);printf("%s",sentnick);t++;
                         update_tstack(sentnick);}
                    else  
                        {writeln(gotnick);printf("%s",gotnick);t++;
                         update_tstack(gotnick);}
                  }
                  writeln(" :");t++;
		  writeln(&inputbuf[t]);
                  printf("* "); sline(15,sn(&inputbuf[t]));
                  ii=0; 
		break;
        case 'C': if (strncmp(s1,"/CTCP ",6) == 0)
                   {  char *s2;
                      writeln("PRIVMSG ");
                      t=6;
                      while ((inputbuf[t] != ' ') && (inputbuf[t] != '\0'))
                       { st[0] = inputbuf[t++]; 
                         st[1] = '\0';
                         writeln(st);
                       }
                      writeln(" :");
                      while (strchr(inputbuf,'\n'))
		          *(strchr(inputbuf,'\n'))= '\0';
                      s2 = upper(&inputbuf[t+1]);strcpy(inputbuf, s2);
                      if (strcmp(s2,"PING")==0)
                        sprintf(inputbuf, "%s %d", inputbuf, time(NULL));
                      strcat(inputbuf,"\01");
                      strcat(inputbuf,"\n");
                      writeln(inputbuf);free(s2);
                      ii=0;
                   }
                   else if (strncmp(s1,"/CLEAR",6) == 0)
                       { tputs_x(CL);updatestatus(); ii = 0; }
                  break; 
        case 'P' : if (strncmp(s1, "/PING ",6) == 0)
                     { char s[80];
		       *(strchr(inputbuf,'\n'))= '\0';
                       writeln("PRIVMSG "); writeln(&inputbuf[6]);
                       writeln(" :PING ");
                       sprintf(s, "%d\01\n", time(NULL));
                       writeln(s); 
                       ii=0;
                     }
                  break;
	case '?': *(strchr(inputbuf,'\n'))='\0';
		printf("\n*** Default object set to %s",
		strcpy(object,&inputbuf[2])); updatestatus(); i=0;
		break;
	default: break; /* this is where you'd match commands */ 
     }
  /* pass command unfiltered to the server */
  if(ii) 
     { 
       writeln(&inputbuf[1]); 
       /* printf("= %s",inputbuf); */
     } 
  }
else { 
      if ((inputbuf[0] != '\n') && (inputbuf[0] != '\r') 
          &&(inputbuf[0] != '\0') && (strcmp(object,"*") != 0))
       {
	sprintf(buf,"PRIVMSG %s :%s",object,inputbuf);
	writeln(buf); 
        if (strlen(inputbuf)%(CO-5) == 0) ii = strlen(inputbuf)/(CO-5);
              else ii = strlen(inputbuf)/(CO-5) + 1;
        for (i=0; i < ii; i++)
          { char *sss; 
            sss = left(&inputbuf[i*(CO-5)],CO-5); 
              if (i == 0)
                { if (sss[strlen(sss)-1] == '\n') 
                     sss[strlen(sss)-1] = '\0';
                  printf("\r\n> %s",sss);
                }
             else
                printf("\r\n+%s",sss);
            free(sss);
          }
            
       }
      else 
        {
         if ((inputbuf[0] != '\n') && (inputbuf[0] != '\r') 
             &&(inputbuf[0] != '\0') )
          { strcpy(inputbuf,"");
            if (strcmp(object,"*") == 0) 
              printf ("\n*** You haven't joined any channel!"); 
           }
        }
     }
idletimer=time(NULL); 
if (!dumb) fflush(stdout);
fflush(stdin);
}

takeinchar()
{ char ch; int kpos;

kpos = pos;
if(dumb) 
  {
    fgets(inputbuf,511,stdin); 
    parseinput(); 
   }
else {
  ch=getchar();
  if(current<500) 
      inputbuf[current++]=ch;
  if(ch=='\10' || ch=='\177' || ch==erasechar()) 
     {
	if (pos) { inputbuf[current-=2]='\0'; --pos; --kpos;}
        else {current--;kpos = -1;}
	if (kpos>=0) printf("%c %c",ch,ch); 
        if (ch == erasechar())
          { tputs_x(tgoto(CM, pos, LI-1)); 
            tputs_x(CE);
            if (!pos) {strcpy(inputbuf,"");}
          } 
	if(!pos && poslc) 
          { char *sss;
            tputs_x(tgoto(CM, pos, LI-1)); 
            tputs_x(CE);
            sss = left(&inputbuf[(--poslc)*(pos=71)],71);
            printf("%s",sss);
            free(sss);
            tputs_x(tgoto(CM, pos=71, LI-1)); 
            tputs_x(CE);
          }
      }
  else if (ch == '\t')
      { tputs_x(tgoto(CM, pos=0, LI-1)); 
        tputs_x(CE); pos=poslc=0; 
        if ((ttop)&&(tmsg[thead]))
            { printf("/msg %s ", tmsg[thead]);
              pos = strlen(tmsg[thead]) + 6;
              sprintf(inputbuf,"/msg %s ", tmsg[thead]); 
              current = strlen(tmsg[thead]) + 6;
              if (thead < 1) thead = ttop-1;
              else thead--;
            }
        else
           { printf("/msg ");pos = 5; 
             strcpy(inputbuf,"/msg "); current=5; 
           }
       }
  else if (ch != '\r' && ch != '\n') 
     {
	putchar(ch);kpos++;
	if((++pos)>71) 
          { tputs_x(tgoto(CM, pos=0, LI-1)); 
            tputs_x(CE); putchar(ch); pos=1; poslc++; 
          }
      }
  else 
    { pos=0; poslc=0; kpos = 0;
      inputbuf[current-1]='\n'; 
      if (current>1)
	parseinput(); 
     }
  fflush(stdin);
  } /* not dumb mode */
}

void cleanup()
{ tputs_x(tgoto(CS,-1,-1)); tputs_x(tgoto(CM,0,LI-1)); resetty(); exit(128); }


main(argc, argv, envp)
	int argc;
	char **argv;
	char **envp;
{ 
  int i, errflag;

for (i=0;i<20;i++) ipublic[i]=imsg[i]=NULL;
imhead = iphead = 0;
userinfo = getpwuid(getuid()); strcpy(hostname,DEFAULTSERVER);
if (!getenv("IRCNICK")) strncpy(IRCNICK,userinfo->pw_name,sizeof(IRCNICK));
	else strncpy(IRCNICK,(char *) getenv("IRCNICK"), sizeof(IRCNICK));
if(argc>1)
for (i=1; i<argc; i++) if(argv[i][0]=='-') { if(argv[i][1]=='d') dumb=1;
	 	else { fprintf(stderr,"usage: %s [nick] [server] [port] [-d]\n",
			argv[0]); exit(1); } }
	else if(strchr(argv[i],'.')) strcpy(hostname,argv[i]);
		else if(atoi(argv[i])) IRCPORT=atoi(argv[i]);
			else strcpy(IRCNICK,argv[i]);
connectclient(0);
if(!dumb) {
ptr=termcap;
if((term=(char *)getenv("TERM"))==NULL) {
	fprintf(stderr, "Smallirc: TERM not set\n");
	exit(1);
	}
if(tgetent(bp, term) < 1) {
	fprintf(stderr, "Smallirc: no termcap entry for %s\n",term);
	exit(1);
	}
if((CO=tgetnum("co")) == -1) CO=80; if((LI=tgetnum("li")) == -1) LI=24;
if((CM=(char *)tgetstr("cm", &ptr))==NULL)
	CM=(char *)tgetstr("cl", &ptr);
if((MR=(char *)tgetstr("mr", &ptr))==NULL)
	MR=(char *)tgetstr("md", &ptr);
ME=(char *)tgetstr("me", &ptr);
CL=(char *)tgetstr("cl", &ptr);
if(!CM || !(CS=(char *)tgetstr("cs", &ptr)) ||
	!(CE=(char *)tgetstr("ce", &ptr))) {
	printf("Smallirc: sorry, no termcap cm/cl,cs,ce: dumb mode set\n");
	dumb=1; }
if(!dumb) {
if ((_tty_ch = open("/dev/tty", O_RDWR, 0)) == -1) _tty_ch = 0;
signal(SIGINT,cleanup); signal(SIGHUP,cleanup); signal(SIGKILL,cleanup);
savetty(); cbreak(); noecho(); tputs_x(tgoto(CS,LI-3,0)); updatestatus(); }
}
while(sok) 
{
	FD_ZERO(&readfs); FD_SET(sockfd,&readfs);
	FD_SET(fileno(stdin),&readfs);
  	if(!dumb) 
   	{ tputs_x(tgoto(CM,pos,LI-1)); fflush(stdout);
	  timeout.tv_sec=15; timeout.tv_usec=0; 
	}
	if(select(FD_SETSIZE, &readfs, NULL, NULL,(dumb ? NULL : &timeout))) 
	{
	 if(FD_ISSET(fileno(stdin),&readfs)) takeinchar();
	 if(FD_ISSET(sockfd,&readfs)) 
  		{  sok = readln(); 
	 	   if (sok) spitout(); 
 		}
	} 
	else  updatestatus(); 
}
if(!dumb) { tputs_x(tgoto(CS,-1,-1)); tputs_x(tgoto(CM,0,LI-1)); resetty(); }
}
/* EOF */
