1 /*
2  * commands.c: all the client commands.
3  *
4  * Copyright(c) 1997-2000 - All Rights Reserved
5  *
6  * See the COPYRIGHT file.
7  */
8 
9 #ifndef lint
10 static char rcsid[] = "@(#)$Id: commands.c,v 1.66 2001/03/14 19:50:20 kalt Exp $";
11 #endif
12 
13 #include "os.h"
14 
15 #include "struct.h"
16 #include "server.h"
17 #include "window.h"
18 #include "format.h"
19 #include "option.h"
20 #include "term.h"
21 #include "utils.h"
22 #include "input.h"
23 
24 extern struct server_ *server;
25 
26 extern char tab_add(char *, char *);
27 extern char space_add(char *, char *);
28 extern int cmd_ctcp(char *);
29 extern int cmd_ping(char *);
30 extern int cmd_seen(char *);
31 extern int cmd_lastlog(char *);
32 extern int sic_wreformat();
33 extern char *yow();
34 
35 void parse_command(char *, int);
36 
37 struct cmdlist_
38 {
39   char	*name;
40   int	(*func) (char *);
41 };
42 
43 struct aliaslist_
44 {
45   char	*name;
46   char	*eval;
47   struct aliaslist_ *nexta;
48 };
49 
50 static struct aliaslist_ *alias = NULL;
51 
52 /* return codes:
53  *	0	"success"
54  *	-1	"need more parameters"
55  *	-2	"no recipient"
56  *	-3	"no text to send"
57  *	-4	"bad params"
58  *	-5	"bind window to server first"
59  *	-9	"not implemented"
60  */
61 
62 static char		outp[1024];
63 static u_char		outa[1024];
64 
65 /* cmd_smart: if the first word in the buffer is not a channel, prepend
66  *	      the window default channel name to the buffer.
67  */
68 static char *
cmd_smart(ibuf)69 cmd_smart(ibuf)
70   char *ibuf;
71 {
72   static char newbuf[1024];
73   struct channel_ *ctmp;
74   char *sp;
75 
76   if (*ibuf == '&' || *ibuf == '#' || *ibuf == '+')
77     {
78       char known;
79       if (sp = index(ibuf, ' '))
80 	  *sp = '\0';
81       known = map_c2w(ibuf);
82       if (sp)
83 	  *sp = ' ';
84       if (known)
85 	  return ibuf;
86     }
87   ctmp = get_channel(NULL);
88   if (ctmp == NULL)
89       return "";
90   sprintf(newbuf, "%s%s%s", ctmp->chname, (*ibuf) ? " " : "", ibuf);
91   return newbuf;
92 }
93 
94 /*
95  * IRC commands
96  */
97 static int
cmd_epart(p,forget)98 cmd_epart(p, forget)
99   char *p;
100   char forget;
101 {
102   char *c;
103 
104   p = cmd_smart(p);
105   c = index(p, ' ');
106   if (!*p)
107       return -1;
108   if (c)
109       *c++ = '\0';
110 
111   if (map_c2w(p) == 0)
112     {
113       vsic_slog(LOG_CLIENT, "--- Channel %s is unknown to me", p);
114       return 0;
115     }
116 
117   part_channel(p, (c) ? c : "", forget);
118 
119   return 0;
120 }
121 
122 static int
cmd_cmode(p)123 cmd_cmode(p)
124   char *p;
125 {
126   p = cmd_smart(p);
127   if (!*p)
128       return -1;
129   vsic_write("MODE %s", p);
130   return 0;
131 }
132 
133 static int
cmd_join(p)134 cmd_join(p)
135   char *p;
136 {
137   char *key = index(p, ' ');
138   struct channel_ *ctmp;
139 
140   if (!*p)
141       return -1;
142   if (index(p, ','))
143       return -9; /* better this than a crash */
144   if (server == NULL)
145       return -5;
146   if (key)
147       *key++ = '\0';
148   switch (map_c2w(p))
149     {
150   case 0 :
151       new_channel(p);
152       break;
153   case 1 :
154       break;
155   case 2 :
156       vsic_slog(LOG_CLIENT,
157 		"--- Channel %s was mapped to a different window", p);
158       sic_wchannel(p);
159       break;
160   default :
161       abort(); /* never */
162     }
163   ctmp = get_channel(p);
164   if (key && *key)
165     {
166       if (ctmp->chkey)
167 	free(ctmp->chkey);
168       ctmp->chkey = strdup(key);
169     }
170   join_channel(p, 0);
171   return 0;
172 }
173 
174 static int
cmd_kick(p)175 cmd_kick(p)
176   char *p;
177 {
178   char *comment;
179 
180   p = cmd_smart(p);
181   if (!*p)
182       return -1;
183   if (comment = index(p, ' '))
184       if (comment = index(comment+1, ' '))
185 	  *comment++ = '\0';
186   vsic_write("KICK %s :%s", p, (comment) ? comment : "");
187   return 0;
188 }
189 
190 static int
cmd_leave(p)191 cmd_leave(p)
192 {
193   return cmd_epart(p, 0);
194 }
195 
196 static int
cmd_list(p)197 cmd_list(p)
198   char *p;
199 {
200   vsic_write("LIST %s", (*p) ? p : cmd_smart(p));
201   return 0;
202 }
203 
204 int
cmd_msgnotice(cmd,p)205 cmd_msgnotice(cmd, p)
206 char *cmd, *p;
207 {
208   char *txt = index(p, ' '), fmt;
209   int rendering;
210   unsigned int flags = 0;
211 
212   if (!*p)
213       return -2;
214   if (cmd)
215     {
216       if (!txt || !*(txt+1))
217 	  return -3;
218       *txt++ = '\0';
219       if (rmatch("*@[*]", p))
220 	{
221 	  char *s = index(index(p, '@'), ']');
222 
223 	  *s = '\0';
224 	  s = index(p, '@');
225 	  *s++ = '\0';
226 	  s += 1;
227 	  if (select_server(s) == NULL)
228 	    {
229 	      vsic_slog(LOG_CLIENT, "--- No connected to %s.", s);
230 	      return 0;
231 	    }
232 	}
233       if (*p != '&' && *p != '#' && *p != '+' && *txt != '\001')
234 	  tab_add(p, (*cmd == 'S') ? NULL : "");
235       if (*p == '=')
236 	  if (*cmd == 'N')
237 	      return -4;
238 	  else
239 	    {
240 	      p++;
241 	      cmd = "DCC";
242 	    }
243     }
244   else
245     {
246       cmd = "PRIVMSG";
247       txt = p;
248       if (p = get_query())
249 	{
250 	  if (*p == '=')
251 	    {
252 	      cmd = "DCC";
253 	      p++;
254 	    }
255 	  else if (index(p, '@'))
256 	      cmd = "SQUERY";
257 	}
258       else
259 	{
260 	  if (get_channel(NULL) == NULL)
261 	      return -3;
262 	  p = get_channel(NULL)->chname;
263 	}
264     }
265 
266   if (*p != '=' && server == NULL)
267       return -5;
268   fmt = get_channel(p) ? (*cmd == 'P') ? F_MYPUBM : F_MYPUBN :
269       (*cmd == 'D') ? F_MYDCC : (*cmd == 'P') ? F_MYPRIVM : F_MYPRIVN;
270   rendering = out_sprintf(outp, outa, get_format(fmt, p), p, cmd, txt);
271   select_active(get_channel(p) ? p : NULL, 0);
272   if (*cmd == 'P' && get_channel(p) == NULL)
273     {
274       char tmpq[512];
275 
276       sprintf(tmpq, "%s%s", (*cmd == 'D') ? "=" : "", p);
277       select_query(tmpq);
278     }
279   flags = LOG_CLIENT;
280   flags |= (*cmd == 'D') ? LOG_DCC : (*cmd == 'P') ? LOG_MSG : LOG_NOTICE;
281   flags |= get_channel(p) ? LOG_PUBLIC : LOG_PRIVATE;
282   if (*cmd == 'N' && *txt == '\001'
283       && !get_option(Z_VCTCP, (get_channel(p)) ? p : NULL))
284       flags |= LOG_HIDE;
285   sic_log(flags, 0, NULL, cmd, p, txt, fmt, outp, rendering ? outa : NULL);
286   if (*cmd == 'D')
287       vsic_dwrite(p, "%s", txt);
288   else
289       vsic_write("%s %s :%s", cmd, p, txt);
290   if (get_channel(p) && (p = index(txt, ':')) < index(txt, ' ') && p)
291     {
292       *p = '\0';
293       space_add(txt, "");
294     }
295   return 0;
296 }
297 
298 static int
cmd_msg(p)299 cmd_msg(p)
300   char *p;
301 {
302   return cmd_msgnotice("PRIVMSG", p);
303 }
304 
305 static int
cmd_names(p)306 cmd_names(p)
307   char *p;
308 {
309   vsic_write("NAMES %s", (*p) ? p : cmd_smart(p));
310   return 0;
311 }
312 
313 static int
cmd_notice(p)314 cmd_notice(p)
315   char *p;
316 {
317   return cmd_msgnotice("NOTICE", p);
318 }
319 
320 static int
cmd_part(p)321 cmd_part(p)
322   char *p;
323 {
324   return cmd_epart(p, 1);
325 }
326 
327 static int
cmd_quit(p)328 cmd_quit(p)
329   char *p;
330 {
331   vsic_write("QUIT :%s", p);
332   cmd_server(NULL);
333   return 0;
334 }
335 
336 static int
cmd_squery(p)337 cmd_squery(p)
338   char *p;
339 {
340   char *txt = index(p, ' ');
341   int rendering;
342 
343   if (!*p)
344       return -2;
345   if (server == NULL)
346       return -5;
347   if (!txt || !*(txt+1))
348       return -3;
349   *txt++ = '\0';
350 
351   vsic_write("SQUERY %s :%s", p, txt);
352   rendering = out_sprintf(outp, outa, get_format(F_MYQUERY, NULL),
353 			  p, "SQUERY", txt);
354   select_active(NULL, 0);
355   sic_log(LOG_CLIENT|LOG_PRIVATE|LOG_MSG,
356 	  0, NULL, "SQUERY", p, txt, F_MYQUERY, outp, rendering ? outa : NULL);
357   return 0;
358 }
359 
360 static int
cmd_topic(p)361 cmd_topic(p)
362   char *p;
363 {
364   char *comment;
365 
366   if (!*p)
367       p = cmd_smart(p);
368   if (!*p)
369       return -1;
370   if (comment = index(p, ' '))
371       *comment++ = '\0';
372   vsic_write("TOPIC %s%c%s", p, (comment) ? ':' : ' ',
373 	     (comment) ? comment : "");
374   return 0;
375 }
376 
377 static int
cmd_umode(p)378 cmd_umode(p)
379   char *p;
380 {
381   if (server == NULL)
382       return -5;
383   vsic_write("MODE %s %s", server->nick, p);
384   return 0;
385 }
386 
387 static int
cmd_who(p)388 cmd_who(p)
389   char *p;
390 {
391   vsic_write("WHO %s", (*p) ? p : cmd_smart(p));
392   return 0;
393 }
394 
395 /*
396  * internal commands
397  */
398 int
cmd_alias(p)399 cmd_alias(p)
400   char *p;
401 {
402   char tmp[1024], *wp = tmp, *txt;
403   struct aliaslist_ *atmp = alias;
404 
405   if (txt = index(p, ' '))
406     {
407       *txt++ = '\0';
408 
409       while (atmp)
410 	{
411 	  if (!strcasecmp(p, atmp->name))
412 	      break;
413 	  atmp = atmp->nexta;
414 	}
415       if (atmp)
416 	  free(atmp->eval);
417       else
418 	{
419 	  atmp = (struct aliaslist_ *) malloc(sizeof(struct aliaslist_));
420 	  atmp->name = strdup(p);
421 	  atmp->nexta = alias;
422 	  alias = atmp;
423 	}
424       while (*txt)
425 	{
426 	  if (*txt == '%' && isdigit((int) *(txt+1)))
427 	    {
428 	      *wp++ = *txt++; /* % */
429 	      *wp++ = '{';
430 	      while (isdigit((int) *txt))
431 		  *wp++ = *txt++;
432 	      *wp++ = '}';
433 	    }
434 	  else
435 	      *wp++ = *txt++;
436 	}
437       *wp = '\0';
438       atmp->eval = strdup(tmp);
439     }
440   else
441     {
442       int len = strlen(p);
443 
444       sic_slog(LOG_CLIENT, "--- Aliases:");
445       while (atmp)
446 	{
447 	  if (!strncasecmp(p, atmp->name, len))
448 	      vsic_slog(LOG_CLIENT, "---     %-8s: %s", atmp->name,atmp->eval);
449 	  atmp = atmp->nexta;
450 	}
451     }
452   return 0;
453 }
454 
455 static int
cmd_clear(p)456 cmd_clear(p)
457   char *p;
458 {
459   if (*p && !strcasecmp(p, "-f"))
460       sic_clog(1);
461   else
462       sic_clog(0);
463   sic_scroll2(+1, NULL);
464   sic_redowin(0);
465   return 0;
466 }
467 
468 static int
cmd_exit(p)469 cmd_exit(p)
470   char *p;
471 {
472   sic_slog(LOG_CLIENT, "Exiting...");
473   term_end();
474   exit(0);
475 }
476 
477 static int
cmd_dns(p)478 cmd_dns(p)
479   char *p;
480 {
481   if (*p)
482     {
483       vsic_slog(LOG_CLIENT, "--- Looking up \"%s\" in DNS", p);
484       dns_lookup(p, NULL);
485       return 0;
486     }
487   else
488       return -1;
489 }
490 
491 int
cmd_option(p)492 cmd_option(p)
493   char *p;
494 {
495   static char *channel = NULL;
496   char *c, *v;
497   unsigned int flag, all = 0;
498   int fnum = -1;
499 
500   if (!*p)
501       return -1;
502 
503   if (channel)
504     {
505       free(channel);
506       channel = NULL;
507     }
508   if (v = index(p, ' '))
509       *v++ = '\0';
510 
511   switch (*p)
512     {
513   case 'c': case 'C':
514       flag = O_CLEAR;
515       break;
516   case 'g': case 'G':
517       flag = O_GET;
518       break;
519   case 's': case 'S':
520       flag = O_SET;
521       break;
522   case 'r': case 'R':
523       sic_wreformat();
524       sic_redowin(1);
525       return 0;
526   default:
527       return -4;
528     }
529   if ((c = index(p, '.')) == NULL)
530       return -4;
531   switch (*(++c))
532     {
533   case 'a': case 'A':
534       all = 1;
535       if (flag != O_GET)
536 	  return -4;
537       break;
538   case 'c': case 'C':
539       if ((c = index(p, '=')) == NULL)
540 	  return -4;
541       if ((p = index(c, '.')) == NULL)
542 	  return -4;
543       c +=1 ;
544       *p = '\0';
545       if (c == p)
546 	  if (get_channel(NULL))
547 	      c = get_channel(NULL)->chname;
548 	  else
549 	      c = NULL;
550       if (c == NULL || map_c2w(c) == 0)
551 	{
552 	  vsic_slog(LOG_CLIENT, "%s: No such channel", c);
553 	  return 0;
554 	}
555       channel = strdup(c);
556       *(c = p) = '.';
557       break;
558   case 'd': case 'D':
559       if (flag != O_GET)
560 	  return -4;
561       flag |= O_DEFAULT;
562       break;
563   case 'p': case 'P':
564       flag |= O_PROTO;
565       break;
566   case 's': case 'S':
567       flag |= O_SERVER;
568       break;
569   case 't': case 'T':
570       flag |= O_TOPLVL;
571       break;
572   case 'w': case 'W':
573       flag |= O_WINDOW;
574       break;
575   default:
576       return -4;
577     }
578   if ((p = index(c, '.')) == NULL)
579       return -4;
580   if (c = index(++p, '.'))
581       *(c++) = '\0';
582   if (!strcasecmp(p, "on"))
583       flag |= O_ON;
584   else if (!strcasecmp(p, "off"))
585       flag |= O_OFF;
586   else if (*p == '#' && isdigit((int) *(p+1)))
587       fnum = atoi(p+1) + F_MAX+1;
588   else if (isdigit((int) *p))
589       fnum = atoi(p);
590   else if (*p == 'k' || *p == 'K')
591       flag |= O_KEYWORD;
592   else if (*p == 'l' || *p == 'L')
593       flag |= O_LOGFILE;
594   else if (*p == 's' || *p == 'S')
595       flag |= O_SWITCH;
596   else if (*p == 'r' || *p == 'R')
597       flag |= O_REWRITE;
598   else
599       return -4;
600   if (flag & (O_ON|O_OFF))
601     {
602       if (c == NULL)
603 	  return -4;
604       flag |= O_MASK;
605     }
606   if (flag & (O_GET|O_CLEAR))
607     {
608       unsigned char	i;
609       char		*str = NULL;
610       unsigned int	all_list[] = {O_DEFAULT, O_TOPLVL, O_SERVER, O_WINDOW},
611       			ui = 0;
612 
613       for (i = 0; (i == 0 || all) && i < 4; i++)
614 	{
615 	  if (flag & (O_MASK|O_SWITCH))
616 	      str = c;
617 	  if (flag & O_KEYWORD)
618 	      str = v;
619 	  if (flag & O_REWRITE)
620 	    {
621 	      ui = (c) ? atoi(c) : F_MAX+1;
622 	      str = v;
623 	    }
624 	  customize((all) ? flag|all_list[i] : flag, channel, fnum, &str, &ui);
625 	  if (flag & O_GET)
626 	    {
627 	      if (flag & (O_KEYWORD|O_REWRITE))
628 		{
629 		  int count = fnum; /* -1 */
630 
631 		  do
632 		    {
633 		      if (str)
634 			  if (flag & O_KEYWORD)
635 			      vsic_slog(LOG_CLIENT, "Keyword: %5.5X %s",
636 					ui, str);
637 			  else
638 			      vsic_slog(LOG_CLIENT, "Rewrite: %3u %s",
639 					ui, str);
640 		      str = v; fnum = --count;
641 		      if (flag & O_REWRITE)
642 			  ui = (c) ? atoi(c) : F_MAX+1;
643 		      customize((all) ? flag|all_list[i] : flag, channel,
644 				fnum, &str, &ui);
645 		    }
646 		  while (str != NULL && v == NULL);
647 		  fnum = -1;
648 		}
649 	      else if (flag & O_MASK)
650 		{
651 		  char *str2 = c;
652 		  unsigned int ui2 = 0;
653 
654 		  customize((all) ? (flag|all_list[i])^(O_ON|O_OFF) :
655 			    flag^(O_ON|O_OFF), channel, fnum, &str2, &ui2);
656 
657 		  vsic_slog(LOG_CLIENT, "Set to: \"%8.8X\" - \"%8.8X\"",
658 			    ui, ui2);
659 		}
660 	      else if (flag & O_LOGFILE)
661 		  vsic_slog(LOG_CLIENT, "Log: %s, Filter: %d", str, ui);
662 	      else if (str)
663 		  vsic_slog(LOG_CLIENT, "Set to: \"%c%s\"",
664 			    (*str) ? *str : ':', str+1);
665 	      else
666 		  vsic_slog(LOG_CLIENT, "Set to: %8.8X", ui);
667 	    }
668 	}
669       return 0;
670     }
671   if (v)
672     {
673       unsigned int	ui = 0;
674       char		*str = NULL;
675 
676       if (flag & O_SWITCH)
677 	{
678 	  if (!strcasecmp(v, "on"))
679 	      ui = 1;
680 	  else
681 	      ui = 0;
682 	  str = c;
683 	}
684       else
685 	{
686 	  ui = (unsigned int) strtoul(v, &str, 0);
687 #if !defined(HAVE_REGEXP)
688 	  if (flag & O_REWRITE || ((flag & O_KEYWORD) && (ui & K_REGEXP)))
689 	    {
690 	      sic_slog(LOG_CLIENT,
691 		       "--- Option not available (requires regexp library).");
692 	      return 0;
693 	    }
694 #endif
695 	  if (flag & O_MASK)
696 	      str = c;
697 	  else if (flag & O_REWRITE)
698 	      ui = atoi(c);
699 	  else if (str)
700 	      while (*str && isspace((int) *str))
701 		  str++;
702 	}
703       customize(flag, channel, fnum, &str, &ui);
704       if (flag & (O_LOGFILE|O_REWRITE|O_KEYWORD) && str)
705 	vsic_slog(LOG_CLIENT, "--- Error: %s", str);
706       return 0;
707     }
708   else
709       return -4;
710 }
711 
712 static int
cmd_query(p)713 cmd_query(p)
714   char *p;
715 {
716   char *txt = index(p, ' ');
717 
718   if (txt)
719       *txt = '\0';
720   if (map_c2w(p) == 0)
721     {
722       default_channel(p);
723       if (txt && *(txt+1))
724 	{
725 	  *txt = ' ';
726 	  cmd_msgnotice("PRIVMSG", p);
727 	}
728     }
729   else
730       sic_slog(LOG_CLIENT,"--- Cannot query a channel!");
731   return 0;
732 }
733 
734 static int
cmd_window(p)735 cmd_window(p)
736   char *p;
737 {
738   if (!*p)
739       return -1;
740   if (!strcasecmp(p, "new"))
741       sic_newwin();
742   else if (!strcasecmp(p, "next"))
743       sic_chgwin(1);
744   else if (!strcasecmp(p, "kill"))
745       sic_wkill();
746   else if (!strcasecmp(p, "last"))
747       sic_chgwin(-3);
748   else if (!strcasecmp(p, "list"))
749       sic_wlist();
750   else if (!strcasecmp(p, "previous"))
751       sic_chgwin(-2);
752   else if (!strcasecmp(p, "release"))
753       sic_swin(2);
754   else if (!strncasecmp(p, "number ", 7))
755       if (p = index(p, ' '))
756 	  if (atoi(++p) >= 0 && atoi(p) < 11)
757 	      sic_wch(atoi(p));
758 	  else
759 	      return -4;
760       else
761 	  return -1;
762   else if (!strncasecmp(p, "channel ", 8))
763       if (map_c2w(p+8) == 1)
764 	  default_channel(p+8);
765       else
766 	  sic_slog(LOG_CLIENT,"--- No such channel or channel not in window.");
767   else if (!strncasecmp(p, "dcc", 3))
768       sic_wdcc();
769   else
770       return -4;
771   return 0;
772 }
773 
774 static int
cmd_yow(p)775 cmd_yow(p)
776 char *p;
777 {
778   char *zippy;
779 
780   zippy = yow();
781   if (zippy)
782       vsic_slog(LOG_CLIENT, "--- %s", zippy);
783   return 0;
784 }
785 
786 struct cmdlist_	cmdlist[] = {
787 	{ "ADMIN",	NULL },
788 	{ "ALIAS",	cmd_alias},
789 	{ "AWAY",	NULL },
790 	{ "CLOSE",	NULL },
791 	{ "CONNECT",	NULL },
792 	{ "CLEAR",	cmd_clear},
793 	{ "CMODE",	cmd_cmode},
794 	{ "CTCP",	cmd_ctcp},
795 	{ "DCC",	cmd_dcc},
796 	{ "DIE",	NULL },
797 	{ "DNS",	cmd_dns},
798 	{ "EXIT",	cmd_exit},
799 	{ "INFO",	NULL },
800 	{ "INVITE",	NULL },
801 	{ "ISON",	NULL },
802 	{ "JOIN",	cmd_join },
803 	{ "KICK",	cmd_kick},
804 	/*{ "KILL",	NULL },*/
805 	{ "LASTLOG",	cmd_lastlog},
806 	{ "LEAVE",	cmd_leave},
807 	{ "LINKS",	NULL },
808 	{ "LIST",	cmd_list},
809 	{ "LUSERS",	NULL },
810 	{ "MOTD",	NULL },
811 	{ "MODE",	NULL },
812 	{ "MSG",	cmd_msg},
813 	{ "NAMES",	cmd_names},
814 	{ "NICK",	NULL },
815 	{ "NOTICE",	cmd_notice},
816 	{ "OPER",	NULL },
817 	{ "OPTION",	cmd_option},
818 	{ "PART",	cmd_part},
819 	{ "PING",	cmd_ping},
820 	/* PING/PONG */
821 	{ "QUERY",	cmd_query},
822 	{ "QUIT",	cmd_quit},
823 	{ "REHASH",	NULL },
824 	{ "RESTART",	NULL },
825 	{ "RUN",	cmd_run},
826 	{ "SEEN",	cmd_seen},
827 	{ "SERVER",	cmd_server},
828 	{ "SERVLIST",	NULL },
829 	{ "SQUERY",	cmd_squery},
830 	/*{ "SQUIT",	NULL },*/
831 	{ "STATS",	NULL },
832 	{ "TIME",	NULL },
833 	{ "TOPIC",	cmd_topic },
834 	{ "TRACE",	NULL },
835 	{ "UMODE",	cmd_umode},
836 	{ "USERHOST",	NULL },
837 	{ "VERSION",	NULL },
838 	{ "WINDOW",	cmd_window },
839 	{ "WHO",	cmd_who },
840 	{ "WHOIS",	NULL },
841 	{ "WHOWAS",	NULL },
842 	{ "YOW",	cmd_yow },
843 	{ NULL,		NULL }
844 	};
845 
846 /* parse_alias: parse and execute an alias */
847 void
parse_alias(acmd,str)848 parse_alias(acmd, str)
849   struct aliaslist_ *acmd;
850   char *str;
851 {
852   static unsigned char recursion = 0;
853   char *slice = acmd->eval, *wp = acmd->eval, *rp;
854   char command[1024];
855 
856   if (recursion++ >= 5)
857     {
858       vsic_slog(LOG_CLIENT, "--- Alias %s: maximum recursion exceeded.",
859 		acmd->name);
860       return;
861     }
862   while (wp)
863     {
864       slice = index(slice+1, ';');
865       command[0] = '\0'; rp = command;
866       while(*wp && (!slice || wp < slice))
867 	{
868 	  while(*wp && (!slice || wp < slice) && *wp != '%')
869 	      *rp++ = *wp++;
870 	  if (*wp && (!slice || wp < slice))
871 	    {
872 	      wp++; /* skip % */
873 	      *rp = '\0'; /* for strcat() to work */
874 	      switch (*wp++)
875 		{
876 	      case '%':
877 		  *rp++ = '%'; *rp = '\0';
878 		  break;
879 	      case '*':
880 		  strcat(command, str);
881 		  break;
882 	      case '{':
883 		  strcat(command, sic_split(str, wp-1));
884 		  while (*wp != '}')
885 		      wp++;
886 		  wp++;
887 		  break;
888 		}
889 	      while (*rp != '\0')
890 		  rp++;
891 	    }
892 	}
893       *rp = '\0';
894       if (*command == '/')
895 	  parse_command(command+1, 1);
896       else
897 	  cmd_msgnotice(NULL, command);
898       wp = slice;
899     }
900   recursion--;
901 }
902 
903 /* parse_command: parse an input string that is a command/alias. */
904 void
parse_command(str,aliases)905 parse_command(str, aliases)
906   char *str;
907   int aliases;
908 {
909   int i = -1, len;
910   char *p;
911 
912   select_active(NULL, 2);
913   p = index(str, ' ');
914   if (p)
915     {
916       *p = '\0';
917       while (*p++ == ' ');
918     }
919   else
920       p = "";
921   len = strlen(str);
922 
923   while (cmdlist[++i].name)
924     {
925       if (!strncasecmp(cmdlist[i].name, str, len))
926 	  break;
927     }
928 
929   if (aliases)
930     {
931       struct aliaslist_ *atmp = alias;
932       struct aliaslist_ *acmd = NULL;
933 
934       while (atmp)
935 	{
936 	  if (!strncasecmp(atmp->name, str, len))
937 	    {
938 	      if (len == strlen(atmp->name))
939 		  break;
940 	      if (!acmd)
941 		  acmd = atmp;
942 	    }
943 	  atmp = atmp->nexta;
944 	}
945       if (atmp)
946 	{
947 	  if (len != strlen(atmp->name) && acmd)
948 	    {
949 	      vsic_slog(LOG_CLIENT, "--- %s: Ambiguous command.", str);
950 	      return;
951 	    }
952 	  acmd = atmp;
953 	  if (cmdlist[i].name && len != strlen(acmd->name))
954 	    {
955 	      vsic_slog(LOG_CLIENT, "--- %s: Ambiguous command.", str);
956 	      return;
957 	    }
958 	  parse_alias(acmd, p);
959 	  return;
960 	}
961     }
962 
963   if (cmdlist[i].name)
964     {
965       if (len != strlen(cmdlist[i].name)
966 	  && cmdlist[i+1].name && !strncasecmp(cmdlist[i+1].name, str, len))
967 	{
968 	  vsic_slog(LOG_CLIENT, "--- %s: Ambiguous command.", str);
969 	  return;
970 	}
971 
972       if (cmdlist[i].func)
973 	  switch (cmdlist[i].func(p))
974 	    {
975 	  case 0:
976 	      break;
977 	  case -1:
978 	      vsic_slog(LOG_CLIENT, "%s: Not enough parameters.",
979 			cmdlist[i].name);
980 	      break;
981 	  case -2:
982 	      vsic_slog(LOG_CLIENT, "%s: No recipient specified.",
983 			cmdlist[i].name);
984 	      break;
985 	  case -3:
986 	      vsic_slog(LOG_CLIENT, "%s: No text to send.", cmdlist[i].name);
987 	      break;
988 	  case -4:
989 	      vsic_slog(LOG_CLIENT, "%s: Invalid parameter(s).",
990 			cmdlist[i].name);
991 	      break;
992 	  case -5:
993 	      vsic_slog(LOG_CLIENT, "%s: Bind the window to a server first!",
994 			cmdlist[i].name);
995 	      break;
996 	  case -9:
997 	      vsic_slog(LOG_CLIENT, "%s: functionality not implemented.",
998 			cmdlist[i].name);
999 	      break;
1000 	  default:
1001 	      abort(); /* never */
1002 	    }
1003       else
1004 	  vsic_write("%s %s", cmdlist[i].name, p);
1005     }
1006   else
1007       vsic_slog(LOG_CLIENT, "%s: Unknown command.", str);
1008 }
1009