Logo Search packages:      
Sourcecode: yics version File versions  Download package

command.c

/*
 * YICS: Connect a FICS interface to the Yahoo! Chess server.
 * Copyright (C) 2004  Chris Howie
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include "types.h"
#include "command.h"
#include "console.h"
#include "network.h"
#include "globals.h"
#include "util.h"
#include "vars.h"
#include "version.h"
#include "ropcodes.h"
#include "topcodes.h"
#include "movecheck.h"
#include "formula.h"
#include "sockets.h"
#include "lists.h"

#define IsLetter(x) ((((x) >= 'A') && ((x) <= 'Z')) \
              || (((x) >= 'a') && ((x) <= 'z')))

#define IsNumber(x) (((x) >= '0') && ((x) <= '9'))

#define IsFile(x) (((x) >= 'a') && ((x) <= 'h'))
#define IsRank(x) (((x) >= '1') && ((x) <= '8'))

#define GetFile(x) ((x) - 'a')
#define GetRank(x) ((x) - '1')

#define PARAM_MAX 10

static void com_abort(String *[], int);
static void com_accept(String *[], int);
static void com_addlist(String *[], int);
static void com_adjourn(String *[], int);
static void com_alias(String *[], int);
static void com_allobservers(String *[], int);
static void com_boot(String *[], int);
static void com_cls(String *[], int);
static void com_create(String *[], int);
void com_decline(String *[], int);  /* We use this in vars.c */
static void com_draw(String *[], int);
static void com_finger(String *[], int);
static void com_flag(String *[], int);
static void com_games(String *[], int);
static void com_help(String *[], int);
static void com_invite(String *[], int);
static void com_kibitz(String *[], int);
static void com_moves(String *[], int);
static void com_observe(String *[], int);
static void com_pending(String *[], int);
static void com_ping(String *[], int);
static void com_play(String *[], int);
static void com_primary(String *[], int);
static void com_quit(String *[], int);
static void com_refresh(String *[], int);
static void com_resign(String *[], int);
static void com_resume(String *[], int);
static void com_seek(String *[], int);
static void com_set(String *[], int);
static void com_shout(String *[], int);
static void com_showlist(String *[], int);
static void com_sit(String *[], int);
static void com_sought(String *[], int);
static void com_stand(String *[], int);
static void com_start(String *[], int);
static void com_sublist(String *[], int);
static void com_tell(String *[], int);
static void com_time(String *[], int);
static void com_tset(String *[], int);
static void com_unalias(String *[], int);
static void com_unobserve(String *[], int);
static void com_variables(String *[], int);
static void com_who(String *[], int);
static void com_xkibitz(String *[], int);
static void com_xtell(String *[], int);
static void com_znotify(String *[], int);

static Command commands[] = {
      /* name           handler           params */
      {"!",       com_shout,        1     },
      {"*",       com_kibitz,       1     },
      {"+",       com_addlist,            2     },
      {"-",       com_sublist,            2     },
      {"=",       com_showlist,           1     },
      {"bye",           com_quit,         0     },
      {"exit",    com_quit,         0     },
      {"iset",    com_set,          2     },
      {"logout",  com_quit,         0     },
      {"qtell",   com_tell,         2     },    /* for bots */

      {"abort",   com_abort,        0     },
      {"accept",  com_accept,       1     },
      {"addlist", com_addlist,            2     },
      {"adjourn", com_adjourn,            0     },
      {"alias",   com_alias,        2     },
      {"allobservers",com_allobservers,   1     },
      {"boot",    com_boot,         1     },
      {"cls",           com_cls,          0     },
      {"create",  com_create,       0     },
      {"decline", com_decline,            2     },
      {"draw",    com_draw,         0     },
      {"finger",  com_finger,       1     },
      {"flag",    com_flag,         0     },
      {"games",   com_games,        1     },
      {"help",    com_help,         0     },
      {"invite",  com_invite,       1     },
      {"kibitz",  com_kibitz,       1     },
      {"moves",   com_moves,        1     },
      {"observe", com_observe,            1     },
      {"pending", com_pending,            0     },
      {"ping",    com_ping,         1     },
      {"play",    com_play,         1     },
      {"primary", com_primary,            1     },
      {"quit",    com_quit,         0     },
      {"refresh", com_refresh,            1     },
      {"resign",  com_resign,       0     },
      {"resume",  com_resume,       0     },
      {"seek",    com_seek,         4     },
      {"set",           com_set,          2     },
      {"shout",   com_shout,        1     },
      {"showlist",      com_showlist,           1     },
      {"sit",           com_sit,          1     },
      {"sought",  com_sought,       1     },
      {"stand",   com_stand,        0     },
      {"start",   com_start,        0     },
      {"sublist", com_sublist,            2     },
      {"tell",    com_tell,         2     },
      {"time",    com_time,         0     },
      {"tset",    com_tset,         2     },
      {"unalias", com_unalias,            1     },
      {"unobserve",     com_unobserve,          1     },
      {"variables",     com_variables,          0     },
      {"who",           com_who,          1     },
      {"xkibitz", com_xkibitz,            2     },
      {"xtell",   com_xtell,        2     },
      {"znotify", com_znotify,            0     },
      {NULL,            NULL,             0     },
};

/*
 * These are less efficient than simple command handlers, but they do allow
 * parameters to be specified.
 *
 * Internal aliases do not take part in command completion, which means that
 * they don't block commands from working.  I.e. a handler alias of "vars"
 * would cause "var" to be ambiguous; here, it does not.
 */
static Alias internalAliases[] = {
      {".",       "tell . $@"},
      {"`",       "tell . $@"},
      {"ame",           "allobservers $m"},
      {"f",       "finger $@"},
      {"f.",            "finger $."},
      {"fop",           "finger $o"},
      {"oping",   "ping $o"},
      {"p",       "who a$@"},
      {"pl",            "who a$@"},
      {"player",  "who a$@"},
      {"players", "who a$@"},
      {"rping",   "ping $@"},
      {"sping",   "ping $@"},
      {"style",   "set style $@"},
      {"t",       "tell $@"},
      {"tping",   "ping $."},
      {"vars",    "variables"},
      {"znotl",   "znotify"},
      {NULL,            NULL},
};

static char *noalias[] = {"alias", "quit", "unalias", NULL};

static Alias *aliases[ALIAS_MAX + 1] = {NULL};

void alias_param(String *out, String *param, int from, int to) {
      /* from and to do not point at the same spot!  In the string
       * "foobar sells widgets":
       *
       * from: 1      2     3
       *        foobar sells widgets
       * to:          1     2       3
       *
       * So 3-3 gets the whole third parameter.
       */
      char *buffer = malloc(param->length + 1);
      char *bufferp = buffer;
      char *paramp = param->string;
      bool onws = false;
      int word = 1;

      if (buffer == NULL) {
            StringSet(out, NULL, 0);
            return;
      }

      while (*paramp == ' ')
            paramp++;

      if (to < 0)
            to = param->length;

      while ((word <= to) && (*paramp != '\0')) {
            if (*paramp == ' ') {
                  if (!onws) {
                        word++;
                        onws = true;
                  }

                  if ((word > from) && (word <= to))
                        *(bufferp++) = ' ';
            } else {
                  if (onws) {
                        onws = false;
                  }

                  if (word >= from)
                        *(bufferp++) = *paramp;
            }

            paramp++;
      }

      *bufferp = '\0';
      StringSet(out, buffer, -1);
      free(buffer);
}

static void alias_substitute(Alias *alias, String *command, String *param) {
      char *aptr = alias->mapping;
      int from, to;
      int starpos;
      String *stmp;
      bool gottoken = false;

      StringSet(command, NULL, 0);
      starpos = 1;

      while (*aptr) {
            if (*aptr == '$') {
                  gottoken = true;

                  aptr++;
                  if (!*aptr)
                        break;

                  if (IsNumber(*aptr) || (*aptr == '-')) {
                        if (*aptr == '-') {
                              from = 1;
                        } else {
                              from = atoi(aptr);
                              while (IsNumber(*(++aptr)));
                        }

                        if (*aptr == '-') {
                              aptr++;
                              if (IsNumber(*aptr)) {
                                    to = atoi(aptr);
                                    if (to < from)
                                          to = from;
                                    else
                                          while (IsNumber(*(++aptr)));
                              } else {
                                    to = -1;
                              }
                        } else {
                              to = from;
                        }

                        stmp = StringNull();
                        alias_param(stmp, param, from, to);
                        StringCat(command, stmp->string, stmp->length);
                        StringFree(stmp);
                        continue;
                  }

                  switch (*aptr) {
                  case '@':
                        StringCat(command, param->string, param->length);
                        break;

                  case 'm':
                        StringCat(command, pme->handle, -1);
                        break;

                  case 'o':
                        if (lastopp != NULL)
                              StringCat(command, lastopp->handle, -1);
                        break;

                  case '.':
                        if (lasttell != NULL)
                              StringCat(command, lasttell->handle, -1);
                        break;

                  case '*':
                        stmp = StringNull();

                        alias_param(stmp, param, starpos, starpos);
                        StringCat(command, stmp->string, stmp->length);
                        StringFree(stmp);

                        starpos++;
                        break;

                  /* This handles "case '$':" too. */
                  default:
                        StringCat(command, aptr, 1);
                  }
            } else {
                  StringCat(command, aptr, 1);
            }

            aptr++;
      }

      /* Try regular @s */
      if (!gottoken) {
            StringSet(command, NULL, 0);
            for (aptr = alias->mapping; *aptr != '\0'; aptr++) {
                  if (*aptr == '@')
                        StringCat(command, param->string, param->length);
                  else
                        StringCat(command, aptr, 1);
            }
      }
}

static bool is_move(const char *command) {
      int len;
      char *mcpy, mv[4];
      String *mdup;
      uchar x1 = 0, y1 = 0, x2 = 0, y2 = 0;
      Table *table;
      Color turn;
      char promote = 0;
      bool castle = false;

      mdup = StringNew(command, -1);
      mcpy = mdup->string;
      lowercase(mcpy);
      len = strlen(mcpy);

      if ((len > 3) && (mcpy[len - 2] == '=')) {
            switch (mcpy[len - 1]) {
            case 'n':
                  promote = 4;
                  break;

            case 'b':
                  promote = 6;
                  break;

            case 'r':
                  promote = 8;
                  break;

            case 'q':
                  promote = 10;
                  break;

            default:
                  StringFree(mdup);
                  return false;
            }

            len -= 2;
      }

      if (len == 5) {
            if ((mcpy[2] == '-') && IsFile(mcpy[0]) && IsRank(mcpy[1]) &&
                              IsFile(mcpy[3]) && IsRank(mcpy[4])) {
                  x1 = (uchar)GetFile(mcpy[0]);
                  y1 = (uchar)GetRank(mcpy[1]);
                  x2 = (uchar)GetFile(mcpy[3]);
                  y2 = (uchar)GetRank(mcpy[4]);
            } else if (!strcmp("o-o-o", mcpy)) {
                  x1 = 4;
                  x2 = 2;
                  castle = true;
            } else {
                  StringFree(mdup);
                  return false;
            }
      } else if (len == 4) {
            if (IsFile(mcpy[0]) && IsRank(mcpy[1]) &&
                IsFile(mcpy[2]) && IsRank(mcpy[3])) {
                  x1 = (uchar)GetFile(mcpy[0]);
                  y1 = (uchar)GetRank(mcpy[1]);
                  x2 = (uchar)GetFile(mcpy[2]);
                  y2 = (uchar)GetRank(mcpy[3]);
            } else {
                  StringFree(mdup);
                  return false;
            }
      } else if (len == 3) {
            if (!strcmp("o-o", mcpy)) {
                  x1 = 4;
                  x2 = 6;
                  castle = true;
            } else {
                  StringFree(mdup);
                  return false;
            }
      } else {
            StringFree(mdup);
            return false;
      }

      StringFree(mdup);
      mdup = NULL;
      mcpy = NULL;

      if (primary == -1) {
            iprint("You are not playing or examining a game.\n");
            prompt();
            return true;
      }

      table = tables[primary];

      turn = BLACK;
      if (table->players[0] == pme) {
            turn = WHITE;
      } else if (table->players[1] != pme) {
            iprint("You are not playing or examining a game.\n");
            prompt();
            return true;
      }

      if (table->game->turn != turn) {
            iprint("It is not your turn.\n");
            prompt();
            return true;
      }

      if (castle)
            y1 = y2 = (uchar)((turn == WHITE) ? 0 : 7);

      if (!legal_andcheck_move(table->game, x1, y1, x2, y2)) {
            iprintf("Illegal move (%s).\n", command);
            refresh(table);
            return true;
      }

      if (((y2 == 0) || (y2 == 7)) &&
                  (piecetype(table->game->board[x1][y1]) == PAWN)) {
            /*
             * If a pawn is being moved to the last rank and no promotion
             * piece was specified, default to a queen.
             */
            if (promote == 0)
                  promote = 10;
      /*
       * But if a pawn wasn't moved to the edge and a promotion piece was
       * specified, it's illegal.
       */
      } else if (promote != 0) {
            iprintf("Illegal move (%s).\n", command);
            refresh(table);
            return true;
      }

      /* YOG is upside-down */
      y1 = (uchar)(7 - y1);
      y2 = (uchar)(7 - y2);

      mv[0] = x1;
      mv[1] = y1;
      mv[2] = x2;
      mv[3] = y2;

      nprinttop(table->number, TOP_MOVE, mv, 4);

      if (promote != 0)
            nprinttop(table->number, TOP_PROMOTE, &promote, 1);

      return true;
}

void do_command(const char *cm, bool noalias) {
      String *command = StringNew(cm, -1);
      String *param = StringNull();
      String *paramlist[PARAM_MAX];
      int i, len, start = 0, pcount = 0;
      bool youlose = false, onspace = true;
      Command *srch, *match = NULL;
      Alias *al;

      if ((command == NULL) || (param == NULL)) {
            iprint("Out of memory!");
            prompt();
            if (command != NULL)
                  StringFree(command);
            if (param != NULL)
                  StringFree(param);
            return;
      }

      trimString(command);

      if (command->string[0] == '\0') {
            StringFree(command);
            StringFree(param);
            prompt();
            return;
      }

      if (!noalias && (command->string[0] == '$')) {
            do_command(&command->string[1], true);
            StringFree(command);
            StringFree(param);
            return;
      }

      if (is_move(command->string)) {
            StringFree(command);
            StringFree(param);
            return;
      }

      if (!IsLetter(command->string[0]) && !IsNumber(command->string[0])) {
            StringSet(param, &command->string[1], command->length - 1);
            ltrimString(param);
            StringSet(command, &command->string[0], 1);
      } else {
            for (i = 0; i < command->length; i++) {
                  if (command->string[i] == ' ') {
                        StringSet(param, &command->string[i + 1],
                              command->length - i - 1);
                        StringSet(command, command->string, i);
                        break;
                  }
            }
      }

      ltrimString(param);

      if (!noalias) {
            for (i = 0; (i < ALIAS_MAX) && (aliases[i] != NULL); i++) {
                  al = aliases[i];
                  if (!strcmp(al->alias, command->string)) {
                        alias_substitute(al, command, param);
                        do_command(command->string, true);
                        StringFree(command);
                        StringFree(param);
                        return;
                  }
            }
      }

      al = internalAliases;
      while (al->alias != NULL) {
            if (!strcmp(al->alias, command->string)) {
                  alias_substitute(al, command, param);
                  do_command(command->string, noalias);
                  StringFree(command);
                  StringFree(param);
                  return;
            }

            al++;
      }

      lowercase(command->string);
      len = command->length;
      for (srch = commands; srch->command != NULL; srch++) {
            if (!strcmp(command->string, srch->command)) {
                  match = srch;
                  break;
            } else if ((len <= (int)strlen(srch->command)) &&
            !memcmp(srch->command, command->string, len)) {
                  if (match == NULL) {
                        match = srch;
                  } else if (youlose) {
                        iprint(" ");
                        iprint(srch->command);
                  } else {
                        youlose = true;
                        iprint("Ambiguous command.  Matches: ");
                        iprint(match->command);
                        iprint(" ");
                        iprint(srch->command);
                  }
            }
      }

      if (youlose) {
            iprint("\n");
            prompt();
            StringFree(command);
            StringFree(param);
            return;
      }
      if (match == NULL) {
            iprint(command->string);
            iprint(": Command not found.\n");
            prompt();
            StringFree(command);
            StringFree(param);
            return;
      }

      memset(paramlist, 0, sizeof(paramlist));

      if (match->maxparams != 0) {
            if (match->maxparams > PARAM_MAX)
                  /* this resets the global array too */
                  match->maxparams = PARAM_MAX;

            for (i = 0; i < param->length; i++) {
                  if (onspace && (param->string[i] != ' ')) {
                        start = i;
                        onspace = false;
                  } else if (!onspace && (param->string[i] == ' ') &&
                  (pcount != match->maxparams - 1)) {
LASTONE:                paramlist[pcount++] = StringNew(
                              &param->string[start], i - start);
                        onspace = true;
                  }
            }
            if (!onspace)
                  goto LASTONE;     /* dirty hack */
      }

      StringFree(command);
      StringFree(param);

      match->handler(paramlist, pcount);

      for (i = 0; i < PARAM_MAX; i++) {
            if (paramlist[i] != NULL)
                  StringFree(paramlist[i]);
      }
}

static int sort_alias(const void *sa, const void *sb) {
      return strcmp((*(Alias **)sa)->alias, (*(Alias **)sb)->alias);
}

static void abort_adjourn(char adjourn) {
      Table *table;

      if (primary == -1) {
            iprint("You are not playing or examining a game.\n");
            prompt();
            return;
      }

      table = tables[primary];
      if ((table->players[0] != pme) && (table->players[1] != pme)) {
            iprintf("You are not seated at table %d.\n", primary);
            prompt();
            return;
      }

      nprinttop(table->number, TOP_CANCELSAVE, &adjourn, 1);
}

static void com_abort(String *param[], int pcount) {
      abort_adjourn(0);
}

static void com_accept(String *param[], int pcount) {
      Invite *current = invitations;
      Player *p;
      int i;
      /* char tmp[16]; */

      if (pcount == 0) {
ACCEPT_USAGE:
            iprint("Usage: accept <number|player>\n");
            prompt();
            return;
      } else if (current == NULL) {
            iprint("You have no offers to accept.\n");
            prompt();
            return;
      }

      if (isnumeric(param[0]->string)) {
            i = atoi(param[0]->string);
            if (i < 1)
                  goto ACCEPT_USAGE;

            while ((current != NULL) && --i)
                  current = current->next;

            if ((i != 0) || (current == NULL)) {
                  iprint("Out of range. Use \"pending\" to see the "
                        "list of offers.\n");
                  prompt();
                  return;
            }

/*          snprintf(tmp, sizeof(tmp), "%d", current->number);
            StringSet(param[0], tmp, -1);
            com_observe(param, 1);*/
      } else if ((p = completeHandle(param[0])) != NULL) {
            while ((current != NULL) && (current->who != p))
                  current = current->next;

            if (current == NULL) {
                  iprint("There are no pending offers from ");
                  iprint(p->handle);
                  iprint(".\n");
                  prompt();
                  return;
            }

/*          snprintf(tmp, sizeof(tmp), "%d", current->number);
            StringSet(param[0], tmp, -1);
            com_observe(param, 1);*/
      }

      delInvite(NULL, current->number);

      nprintrop(ROP_OBSERVE, (char *)&current->number, 1);
}

static void com_addsublist(String *param[], int pcount, bool remove) {
      List *list;
      Player *p;

      if (pcount != 2) {
            iprintf("Usage: %slist <list> <item>\n", remove ? "sub" : "add");
            prompt();
            return;
      }

      if ((list = findList(param[0]->string)) == NULL)
            return;

      if (param[1]->string[param[1]->length - 1] == '!') {
            param[1]->string[param[1]->length - 1] = '\0';
            lowercase(param[1]->string);
      } else {
            if ((p = completeHandle(param[1])) == NULL)
                  return;

            StringSet(param[1], p->lhandle, -1);
      }

      if (inList(list, param[1]->string) ^ remove) {
            iprintf("[%s] is %s your %s list.\n", param[1]->string,
                        remove ? "not in" : "already on", list->name);
            prompt();
            return;
      }

      /* Function references are so magical! */
      if (!(remove ? listSub : listAdd)(list, param[1]->string)) {
            iprintf("Unable to %s %s %s your %s list.\n",
                        remove ? "remove" : "add",
                        param[1]->string,
                        remove ? "from" : "to",
                        list->name);
      } else {
            iprintf("[%s] %s your %s list.\n",
                        param[1]->string,
                        remove ? "removed from" : "added to",
                        list->name);

            if (remove && (list->on_sub != NULL))
                  list->on_sub(param[1]->string);
            else if (!remove && (list->on_add != NULL))
                  list->on_add(param[1]->string);
      }

      prompt();
}

static void com_addlist(String *param[], int pcount) {
      com_addsublist(param, pcount, false);
}

static void com_adjourn(String *param[], int pcount) {
      abort_adjourn(1);
}

static void collapse_aliases() {
      int i, j = 0;

      for (i = 0; i < ALIAS_MAX; i++)
            if (aliases[i] != NULL)
                  aliases[j++] = aliases[i];

      aliases[j] = NULL;
}

static void com_alias(String *param[], int pcount) {
      int i, count;
      Alias *al = NULL;
      char **noal;
      bool found;

      if (pcount == 0) {
            count = 0;

            for (i = 0; (i < ALIAS_MAX) && (aliases[i] != NULL); i++)
                  count++;

            if (count == 0) {
                  iprint("You have no aliases.\n");
            } else {
                  iprint("Aliases:\n\n");

                  qsort(aliases, count, sizeof(Alias *), sort_alias);

                  for (i = 0; i < count; i++) {
                        iprint(aliases[i]->alias);
                        iprint(" -> ");
                        iprint(aliases[i]->mapping);
                        iprint("\n");
                  }
            }
      } else {
            lowercase(param[0]->string);

            for (noal = noalias; *noal != NULL; noal++)
                  if (!strcmp(*noal, param[0]->string)) {
                        iprintf("You cannot alias this command.\n");
                        prompt();
                        return;
                  }

            found = false;

            for (i = 0; (i < ALIAS_MAX) && (aliases[i] != NULL); i++) {
                  al = aliases[i];
                  if (!strcmp(param[0]->string, al->alias)) {
                        found = true;
                        break;
                  }
            }

            if (pcount == 1) {
                  if (found) {
                        iprint(al->alias);
                        iprint(" -> ");
                        iprint(al->mapping);
                        iprint("\n");
                  } else {
                        iprint("You have no alias ");
                        iprint(param[0]->string);
                        iprint(".\n");
                  }
            } else {
                  if (found) {
                        free(al->mapping);
                        al->mapping = malloc(param[1]->length + 1);

                        if (al->mapping == NULL) {
                              free(al->alias);
                              free(al);
                              aliases[i] = NULL;

                              collapse_aliases();

                              iprint("Out of memory.\n");
                              prompt();
                              return;
                        }

                        mstrncpy(al->mapping, param[1]->string, param[1]->length + 1);
                  } else {
                        for (i = 0; (i < ALIAS_MAX) &&
                        (aliases[i] != NULL); i++);

                        if (i == ALIAS_MAX) {
                              iprint("You have too many aliases.");
                              prompt();
                              return;
                        } else {
                              al = malloc(sizeof(Alias));
                              if (al == NULL) {
                                    iprint("Out of memory.\n");
                                    prompt();
                                    return;
                              }

                              al->alias = malloc(param[0]->length + 1);
                              al->mapping = malloc(param[1]->length + 1);
                              if ((al->alias == NULL) || (al->mapping == NULL)) {
                                    free(al);
                                    if (al->alias != NULL)
                                          free(al->alias);
                                    if (al->mapping != NULL)
                                          free(al->mapping);

                                    iprint("Out of memory.\n");
                                    prompt();
                                    return;
                              }

                              mstrncpy(al->alias, param[0]->string,
                                    param[0]->length + 1);

                              mstrncpy(al->mapping, param[1]->string,
                                    param[1]->length + 1);

                              aliases[i] = al;
                        }
                  }

                  iprint("Alias ");
                  iprint(param[0]->string);
                  iprint(found ? " replaced.\n" : " set.\n");
            }
      }

      prompt();
}

static int print_observers(Table *table) {
      int i;
      int count = 0;
      Player *p;

      for (i = 0; i < PLAYER_MAX; i++) {
            if (((p = table->observers[i]) != NULL) &&
                        (p != table->players[0]) &&
                        (p != table->players[1])) {
                  if (count++ == 0)
                        iprintf("Observing %d: [%s vs %s]",
                              table->number,
                              phandle(table->players[0]),
                              phandle(table->players[1]));

                  iprintf(" %s", p->handle);
            }
      }

      if (count > 0)
            iprintf(" (%d user%s)\n", count, ((count == 1) ? "" : "s"));

      return count;
}

static void com_allobservers(String *param[], int pcount) {
      int i, obs = 0, total = 0;
      Table *table;
      String *spec;

      if (pcount == 0) {
            for (i = 0; i < TABLE_MAX; i++) {
                  if ((table = tables[i]) != NULL) {
                        total++;

                        if (print_observers(table) > 0)
                              obs++;
                  }
            }
      } else {
            if ((table = findTable(param[0], (spec = StringNull()))) != NULL) {
                  if (print_observers(table) > 0) {
                        obs++;

                        for (i = 0; i < TABLE_MAX; i++)
                              if (tables[i] != NULL)
                                    total++;
                  } else {
                        iprintf("No one is observing %s.\n", spec->string);
                        prompt();
                  }

                  StringFree(spec);
            }
      }

      if (obs != 0) {
            iprintf("\n  %d game%s displayed (of %d in progress).\n",
                  obs, ((obs == 1) ? "" : "s"), total);
            prompt();
      }
}

static void com_boot(String *param[], int pcount) {
      int i;
      Table *table;
      Player *p;
      String *packet;

      if (pcount != 1) {
            iprint("Usage: boot [handle]\n");
            prompt();
            return;
      }

      if (primary == -1) {
            iprint("You are not playing or observing a game.\n");
            prompt();
            return;
      }

      table = tables[primary];

      if (table->host != pme) {
            iprintf("You are not the host of table %d.\n", primary);
            prompt();
            return;
      }

      if ((p = completeHandle(param[0])) == NULL)
            return;

      for (i = 0; (i < PLAYER_MAX) && (table->observers[i] != p); i++);

      if (i == PLAYER_MAX) {
            iprintf("%s is not observing game %d.\n", p->handle, primary);
            prompt();
            return;
      }

      packet = StringNew(p->lhandle, -1);
      packutfString(packet, packet);

      nprinttop((uchar)primary, (char)TOP_BOOT, packet->string, packet->length);

      StringFree(packet);
}

static void com_cls(String *param[], int pcount) {
      iprint("\x1b[H\x1b[2J\n");
      prompt();
}

static void com_create(String *param[], int pcount) {
      param = param;
      pcount = pcount;

      nprintrop(ROP_CREATETABLE, "\0\0", 2);
}

void com_decline(String *param[], int pcount) {
      Player *p;
      String *reason, *packet;

      if (pcount == 0) {
            iprint("Usage: decline <who> [reason]\n");
            prompt();
            return;
      } else if (pcount == 2) {
            reason = StringDup(param[1]);
      } else {
            reason = StringNew("None", -1);
      }

      if ((p = completeHandle(param[0])) == NULL)
            return;

      if (!delInvite(p, 0)) {
            iprint("There are no pending offers from ");
            iprint(p->handle);
            iprint(".\n");
            prompt();
            return;
      }

      packet = StringNew(p->lhandle, -1);
      packutfString(packet, packet);
      packutfStringP(packet, reason);
      nprintropString(ROP_DECLINE, packet);

      iprint("You decline the match offer from ");
      iprint(p->handle);
      iprint(".\nReason given: ");
      iprint(reason->string);
      iprint("\n");
      prompt();

      StringFree(packet);
      StringFree(reason);
}

static void com_draw(String *param[], int pcount) {
      Table *table;
      Color color;

      if (primary == -1) {
            iprint("You are not playing or examining a game.\n");
            prompt();
            return;
      }

      table = tables[primary];

      if (table->players[0] == pme) {
            color = WHITE;
      } else if (table->players[1] == pme) {
            color = BLACK;
      } else {
            iprintf("You are not seated at table %d.\n", primary);
            prompt();
            return;
      }

      /* This is SO not in line with FIDE rules... */
      if (color == table->game->turn) {
            iprint("You may not offer a draw during your turn.\n");
            prompt();
            return;
      }

      nprinttop((uchar)primary, (char)TOP_DRAW, NULL, 0);
}

static void com_finger(String *param[], int pcount) {
      String *who = StringNull();
      Player *p;

      if (pcount == 0) {
            StringSet(who, pme->lhandle, -1);
      } else {
            if ((p = completeHandle(param[0])) == NULL) {
                  StringFree(who);
                  return;
            }
            StringSet(who, p->lhandle, -1);
      }

      packutfString(who, who);
      nprintropString(ROP_FINGER, who);
      StringFree(who);
}

static void com_flag(String *param[], int pcount) {
      Table *table = NULL;
      char opp;

      if (primary != -1)
            table = tables[primary];

      if ((primary == -1) || ((table->players[0] != pme) &&
      (table->players[1] != pme)) || table->finished) {
            iprint("You are not playing a game.\n");
      } else if (gameType(table) == 'u') {
            iprint("You can't flag untimed games.\n");
      } else if (timeleft(table, (table->players[0] == pme) ? 1 : 0) > 1000) {
            iprint("Opponent is not out of time.\n");
      } else {
            iprint("Flagging...\n");
            opp = (char)((table->players[0] == pme) ? 1 : 0);
            nprinttop(table->number, TOP_FLAG, &opp, 1);
      }

      prompt();
}

static int sort_table(const void *sa, const void *sb) {
      Table *a = *(Table **)sa;
      Table *b = *(Table **)sb;
      int ra = 0, rb = 0;

      if ((a->players[0] != NULL) && (a->players[0]->rating != PROVISIONAL))
            ra += a->players[0]->rating;
      if ((a->players[1] != NULL) && (a->players[1]->rating != PROVISIONAL))
            ra += a->players[1]->rating;

      if ((b->players[0] != NULL) && (b->players[0]->rating != PROVISIONAL))
            rb += b->players[0]->rating;
      if ((b->players[1] != NULL) && (b->players[1]->rating != PROVISIONAL))
            rb += b->players[1]->rating;

      return      (ra < rb) ? -1 :
            (ra > rb) ?  1 : 0;
}

static void print_games(Table **games) {
      Player *white, *black;
      Player nullpl = {"", "", PROVISIONAL};
      Table *table;
      char wh[12], bh[12], wr[16], br[16], rated;
      char *tenmin;
      int i;
      const char protections[3] = {' ', 'p', 'P'};

      /* Format examples:

  5 1700 AlphaWolf   1605 myaa       [ br  3   0]   2:39 -  2:20 (38-38) W: 10
 96 1685 PSerge      1755 ChrisHowie [ xu999 999] 10:00:00 -10:00:00 (39-39) W:  1

      */

      while (*games != NULL) {
            table = *games;

            white = table->players[0];
            black = table->players[1];

            if (white == NULL)
                  white = &nullpl;
            if (black == NULL)
                  black = &nullpl;

            prating(wr, white->rating, 4);
            prating(br, black->rating, 4);

            i = 0;
            while ((i < 11) && (white->handle[i] != '\0')) {
                  wh[i] = white->handle[i];
                  i++;
            }
            wh[i] = '\0';

            i = 0;
            while ((i < 11) && (black->handle[i] != '\0')) {
                  bh[i] = black->handle[i];
                  i++;
            }
            bh[i] = '\0';

            rated = (char)(tablerated(table) ? 'r' : 'u');
            tenmin = (findOption(table->options, "pl") != NULL)
                  ? "" : "   10 min/move";

            iprintf("%3d %4s %-11s %4s %-11s [%c%c%c%3d %3d]%s\n",
                  table->number, wr, wh, br, bh,
                  protections[table->protection], gameType(table),
                  rated, tabletime(table), tableinc(table), tenmin);

            games++;
      }
}

static void com_games(String *param[], int pcount) {
      Table *open[TABLE_MAX], *closed[TABLE_MAX];
      Table *c;
      int i, openc = 0, closedc = 0, total = 0;

      if (pcount == 0) {
            for (i = 0; i < TABLE_MAX; i++) {
                  if (tables[i] != NULL) {
                        total++;
                        c = tables[i];
                        if ((c->players[0] == NULL) || (c->players[1] == NULL))
                              open[openc++] = c;
                        else
                              closed[closedc++] = c;
                  }
            }
      } else {
            if ((c = findTable(param[0], NULL)) == NULL)
                  return;

            /* Since there's only one, it doesn't matter where it goes. */
            open[openc++] = c;

            for (i = 0; i < TABLE_MAX; i++)
                  if (tables[i] != NULL)
                        total++;
      }

      open[openc] = NULL;
      closed[closedc] = NULL;

      qsort(open, openc, sizeof(Table *), sort_table);
      qsort(closed, closedc, sizeof(Table *), sort_table);

      print_games(open);
      print_games(closed);

      openc += closedc;
      if (openc != 0) {
            iprintf("\n  %d games displayed", openc);
      } else {
            iprint("No matching games were found");
      }

      iprintf(" (of %d in progress).\n", total);
      prompt();
}

static void com_help(String *param[], int pcount) {
      param = param;
      pcount = pcount;

      sysiprint(  "\n"
                  "--------------------------------------------------"
                  "---------------------------\n"
                  "This is YICS version ");
      sysiprint(VERSION);
      sysiprint(  ".\n\n"

      "For help using YICS, please see http://www.yics.org/wiki\n\n"

      "YICS: Connect an ICS interface to the Yahoo! Chess server.\n"
      "Copyright (C) 2004  Chris Howie\n"
      "Portions Copyright (C) 1993  Richard V. Nash\n\n"

      "This program is free software; you can redistribute it and/or "
      "modify it under the terms of the GNU General Public License as ");
      sysiprint("published by the Free Software Foundation; either version "
      "2 of the License, or (at your option) any later version.\n\n"

      "This program is distributed in the hope that it will be useful, but "
      "WITHOUT ANY WARRANTY; without even the implied warranty of "
      "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU "
      "General Public License (contained in the LICENSE file) for more "
      "details.\n"
      "--------------------------------------------------"
      "---------------------------\n\n");
      prompt();
}

static void com_invite(String *param[], int pcount) {
      Table *table;
      Player *p;
      String *packet;

      if (pcount != 1) {
            iprint("Usage: invite [handle]\n");
            prompt();
            return;
      }

      if (primary == -1) {
            iprint("You are not playing or observing a game.\n");
            prompt();
            return;
      }

      table = tables[primary];

      if (table->host != pme) {
            iprintf("You are not the host of table %d.\n", primary);
            prompt();
            return;
      }

      if ((p = completeHandle(param[0])) == NULL)
            return;

      packet = StringNew(p->lhandle, -1);
      packutfString(packet, packet);

      nprinttop((uchar)primary, (char)TOP_INVITE, packet->string, packet->length);
      iprintf("Inviting %s to table %d.\n", p->handle, primary);
      prompt();

      StringFree(packet);
}

static void com_kibitz(String *param[], int pcount) {
      String *message;

      if (pcount == 0) {
            iprint("Usage: kibitz <message>\n");
            prompt();
            return;
      }

      if (primary == -1) {
            iprint("You are not playing or observing a game.\n");
            prompt();
            return;
      }

      message = packutfString(StringNull(), param[0]);
      nprinttopString((uchar)primary, (char)TOP_KIBITZ, message);
      StringFree(message);
}

static void com_moves(String *param[], int pcount) {
      Table *t;
      Player nullpl = {"[EMPTY]", "[EMPTY]", PROVISIONAL};
      static char tmp[32];
      Player *white, *black;
      char wr[18], br[18];
      time_t tm;
      int gtime, ginc;
      Movelist *current;
      int ms = variables[VAR_MS].number;
      char *timeTaken;

      if (primary == -1) {
            iprint("You are not playing or observing a game.\n");
            prompt();
            return;
      }

      t = tables[primary];
      iprintf("\nMovelist for game %d:\n", primary);

      white = (t->players[0] == NULL) ? &nullpl : t->players[0];
      black = (t->players[1] == NULL) ? &nullpl : t->players[1];

      if (white->rating == PROVISIONAL)
            strcpy(wr, "UNR");
      else
            sprintf(wr, "%d", white->rating);

      if (black->rating == PROVISIONAL)
            strcpy(br, "UNR");
      else
            sprintf(br, "%d", black->rating);

      time(&tm);
      mstrncpy(tmp, ctime(&tm), sizeof(tmp));
      tmp[strlen(tmp) - 1] = '\0';

      printf("\n%s (%s) vs. %s (%s) --- %s\n", white->handle, wr,
            black->handle, br, tmp);

      gtime = tabletime(t);
      ginc = tableinc(t);

      printf("%sated %s match, initial time: %d minutes, increment: %d seconds.\n",
            (tablerated(t) ? "R" : "Unr"),
            strGameType(t), gtime, ginc);

      mstrncpy(wr, white->handle, 17);
      wr[17] = '\0';

      mstrncpy(br, black->handle, 17);
      br[17] = '\0';

      if (ms)
            printf("\nMove  %-17s       %-17s\n"
                  "----  ---------------------   ---------------------\n",
                  wr, br);
      else
            printf("\nMove  %-17s  %-17s\n"
                  "----  ----------------   ----------------\n",
                  wr, br);

      ginc = 1;
      gtime = 0;
      current = t->game->movelist;

      ms = ms ? 14 : 9;
      while (current != NULL) {
            timeTaken = fmttime_ms(current->move.timeTaken);

            if (gtime) {
                  printf("%-8s(%s)\n", current->move.alg, timeTaken);
                  gtime = 0;
                  ginc++;
            } else {
                  printf("%3d.  %-8s(%s)%*s", ginc, current->move.alg,
                              timeTaken, ms - strlen(timeTaken), "");
                  gtime = 1;
            }
            current = current->next;
      }

      if (gtime)
            printf("\n");

      printf("      {%s} %s\n\n",
            (t->finished ? "Finished" : "Still in progress"), t->result);
      prompt();
}

static void observe_unobserve(String *param[], int pcount, bool onlyUnobserve) {
      Table *table;
      int i;
      bool unobserve = false;

      if (pcount == 0) {
            iprintf("Usage: %sobserve <table>\n", onlyUnobserve ? "un" : "");
            prompt();
            return;
      }

      if ((table = findTable(param[0], NULL)) == NULL)
            return;

      for (i = 0; i < PLAYER_MAX; i++) {
            if (table->observers[i] == pme) {
                  unobserve = true;
                  break;
            }
      }

      if (onlyUnobserve && !unobserve) {
            iprintf("You are not observing game %d.\n", table->number);
            prompt();
            return;
      }

      /* if (unobserve) {
            if ((table->players[0] == pme) ||
                (table->players[1] == pme)) {
                  iprint("You must stand up first.\n");
                  prompt();
                  return;
            }
      } */

      if (!unobserve)
            delInvite(NULL, table->number);

      nprintrop((char) (unobserve ? ROP_UNOBSERVE : ROP_OBSERVE),
            (char *)&table->number, 1);
}

static void com_observe(String *param[], int pcount) {
      observe_unobserve(param, pcount, false);
}

static void com_pending(String *param[], int pcount) {
      Invite *current = invitations;
      int i = 1;

      iprint("There are no offers pending TO other players.\n\n");

      if (current == NULL) {
            iprint("There are no offers pending FROM other players.\n");
            prompt();
            return;
      }

      while (current != NULL) {
            iprintf(" %d: %s has invited you to table #%d.\n\n", i++,
                  current->who->handle, current->number);
            current = current->next;
      }

      prompt();
}

static void com_ping(String *param[], int pcount) {
      Player *p;
      String *who;

      if (pcount == 0)
            p = pme;
      else if ((p = completeHandle(param[0])) == NULL)
            return;

      if (p->ping != -1) {
            iprintf("There is already a ping in progress for %s.\n", p->handle);
            prompt();
            return;
      }

      who = StringNew(p->lhandle, -1);
      packutfString(who, who);
      nprintropString(ROP_PING, who);

      iprintf("Ping time for %s not available.\n\n(told ROBOadmin)\n", p->handle);
      prompt();
      prompt();

      p->ping = -2;
      StringFree(who);
}

static void com_play(String *param[], int pcount) {
      int i;
      Table *t;
      char seat;

      if ((pcount != 1) || !isnumeric(param[0]->string)) {
            iprint("Usage: play <number>\n");
            prompt();
            return;
      }

      i = atoi(param[0]->string);

      if ((i < 0) || (i > TABLE_MAX) || (tables[i] == NULL)) {
NO_SUCH_SEEK:
            iprint("That seek is not available.\n");
            prompt();
            return;
      }

      t = tables[i];

      if (t->players[0] != NULL) {
            if (t->players[1] != NULL)
                  goto NO_SUCH_SEEK;

            seat = 1;
      } else if (t->players[1] != NULL) {
            seat = 0;
      } else {
            goto NO_SUCH_SEEK;
      }
                  
      for (i = 0; i < PLAYER_MAX; i++)
            if (t->observers[i] == pme)
                  goto NO_SUCH_SEEK;

      delInvite(NULL, t->number);
      nprintrop((char) ROP_OBSERVE, (char *)&t->number, 1);
      nprinttop(t->number, (char) TOP_SIT, &seat, 1);
      nprinttop(t->number, (char) TOP_START, NULL, 0);
}

static void com_primary(String *param[], int pcount) {
      Table *obs[TABLE_MAX], *p[2];
      Table **op = obs;
      int number;

      observing(obs, pme);

      param = param;

      p[0] = NULL;
      p[1] = NULL;

      if (pcount == 0) {
            while (*op != NULL) {
                  if ((*op)->number == primary) {
                        p[0] = *op;

                        /* shift down */
                        while (op[1] != NULL) {
                              op[0] = op[1];
                              op++;
                        }
                        *op = NULL;

                        break;
                  }
                  op++;
            }

            if (p[0] != NULL)
                  print_games(p);

            print_games(obs);
      } else if (isnumeric(param[0]->string)) {
            number = atoi(param[0]->string);
            if (number == primary) {
                  iprintf("Game %d is already your primary game.\n",
                        primary);
                  prompt();
                  return;
            } else if ((number > TABLE_MAX) || (tables[number] == NULL)) {
                  iprint("There is no such game.\n");
                  prompt();
                  return;
            }

            while (*op != NULL) {
                  if ((*op)->number == number) {
                        primary = number;
                        iprintf("Your primary game is now game %d.\n",
                              primary);
                        prompt();
                        return;
                  }
                  op++;
            }

            iprintf("You are not observing game %d.\n", number);
      } else {
            iprint("Usage: primary [table]\n");
      }

      prompt();
}

static void com_quit(String *param[], int pcount) {
      pcount = pcount;
      param = param;

      nprintrop(ROP_EXIT, NULL, 0);

      prompting = false;
      iprint("Sending logout request...\n");

      prompt();   /* Flushes STDOUT */
}

static void com_refresh(String *param[], int pcount) {
      if (primary == -1) {
            iprint("You are not playing or observing a game.\n");
            prompt();
            return;
      }

      refresh(tables[primary]);
}

static void com_resign(String *param[], int pcount) {
      Table *table;
      char color;

      if (primary == -1) {
            iprint("You are not playing or examining a game.\n");
            prompt();
            return;
      }

      table = tables[primary];
      if (table->players[0] == pme) {
            color = 0;
      } else if (table->players[1] == pme) {
            color = 1;
      } else {
            iprintf("You are not seated at table %d.\n", primary);
            prompt();
            return;
      }

      nprinttop((uchar)primary, (char)TOP_RESIGN, &color, 1);
}

static void com_resume(String *param[], int pcount) {
      iprint("This command is not supported, but is provided to prevent "
                  "accidental usage of the resign command.\n");
      prompt();
}

static void com_seek(String *param[], int pcount) {
      int i;
      int area;

      int time;
      int inc;
      bool limit;

      unsigned short optcount;
      String *tpacket;
      String *topt;
      static char tmp[64];

      time = variables[VAR_TIME].number;
      inc = variables[VAR_INC].number;
      limit = false;

      for (i = 0, area = 0; i < pcount;) {
            switch (area) {
            /* [time inc] */
            case 0:
                  if (!isnumeric(param[i]->string)) {
                        /* Try next area */
                        area++;
                        continue;
                  }

                  time = atoi(param[i]->string);

                  if ((++i >= pcount) || !isnumeric(param[i]->string)) {
                        iprint("Invalid increment.\n");
                        prompt();
                        return;
                  }

                  inc = atoi(param[i]->string);

                  if ((inc > 0) && (time == 0)) {
                        iprint("You cannot have an increment on an "
                              "untimed game.\n");
                        prompt();
                        return;
                  }

                  if (time > 999) {
                        iprint("The time is too large.\n");
                        prompt();
                        return;
                  }

                  if (inc > 999) {
                        iprint("The increment is too large.\n");
                        prompt();
                        return;
                  }

                  break;

            /* limit */
            case 1:
                  if (!strcmp(param[i]->string, "limit") ||
                      !strcmp(param[i]->string, "l")) {
                        limit = true;
                  } else {
                        /* Try next area. */
                        area++;
                        continue;
                  }
                  break;

            /* Too many params */
            default:
                  iprint("Seek has a trailing parameter.\n");
                  prompt();
                  return;
            }

            i++;
            area++;
      }

      optcount = 0;
      tpacket = StringNew("\0\0", 2);
      topt = StringNull();

      if (time > 0) {
            StringSet(topt, "it", 2);
            packutfStringP(tpacket, topt);

            snprintf(tmp, sizeof(tmp), "%d", inc * 1000);
            StringSet(topt, tmp, -1);
            packutfStringP(tpacket, topt);

            optcount++;

            StringSet(topt, "tt", 2);
            packutfStringP(tpacket, topt);

            snprintf(tmp, sizeof(tmp), "%d", time * 60000);
            StringSet(topt, tmp, -1);
            packutfStringP(tpacket, topt);

            optcount++;
      }

      if (!limit) {
            StringSet(topt, "pl", 2);
            packutfStringP(tpacket, topt);

            StringSet(topt, NULL, 0);
            packutfStringP(tpacket, topt);

            optcount++;
      }

      optcount = htons(optcount);
      memcpy(tpacket->string, &optcount, 2);

      nprintropString(ROP_CREATETABLE, tpacket);

      StringFree(topt);
      StringFree(tpacket);
}

static bool com_xset(String *param[], int pcount, Variable *type) {
      const char *var, *new = NULL;
      Variable *v;

      if (pcount == 0)
            return false;

      var = param[0]->string;
      if (pcount == 2)
            new = param[1]->string;

      switch (setvar(type, var, new, &v)) {
            case VARSET_BAD:
                  iprintf("Bad setting for variable %s.\n", v->name);
                  break;

            case VARSET_LOCKED:
                  iprint("Cannot alter: Interface setting locked.\n");
                  break;

            case VARSET_NOTFOUND:
                  iprintf("No such variable name %s.\n", var);
                  break;

            case VARSET_AMBIGUOUS:
                  iprintf("Ambiguous variable name %s.\n", var);
                  break;

            default:
                  return true;
      }

      prompt();
      return true;
}

static void com_set(String *param[], int pcount) {
      if (!com_xset(param, pcount, variables)) {
            iprint("Usage: set <variable> [setting]\n");
            prompt();
      }
}

static void com_tset(String *param[], int pcount) {
      Variable *srch;
      Table *table;

      if (primary == -1) {
            iprint("You are not playing or examining a game.\n");
            prompt();
            return;
      }

      /* This hack lets you tset boolean tvars without a setting. */
      table = tables[primary];
      for (srch = tvariables; srch->type != VAR_END; srch++) {
            if (srch->type == VAR_BOOLEAN) {
                  if (!strcmp(srch->name, "rated")) {
                        srch->number = tablerated(table) ? 1 : 0;
                  } else if (!strcmp(srch->name, "tenminlimit")) {
                        srch->number = (findOption(table->options, "pl")
                              != NULL) ? 0 : 1;
                  }
            }
      }

      if (!com_xset(param, pcount, tvariables)) {
            iprint("Usage: tset <tvariable> [setting]\n");
            prompt();
      }
}

static void com_shout(String *param[], int pcount) {
      String *message;

      if (pcount == 0) {
            iprint("Usage: shout <message>\n");
            prompt();
            return;
      }

      message = packutfString(StringNull(), param[0]);
      nprintropString(ROP_SHOUT, message);

      StringFree(message);
}

static void com_showlist(String *param[], int pcount) {
      List *lp;

      if (pcount == 0) {
            iprint("Lists:\n\n");
            for (lp = lists; lp->name != NULL; lp++)
                  iprintf("%-20s is PERSONAL\n", lp->name);
      } else if ((lp = findList(param[0]->string)) != NULL) {
            iprintf("-- %s list: %d names --\n", lp->name, lp->size);

            if (lp->contents != NULL)
                  columns(lp->contents);
      }

      prompt();
}

static void com_sit(String *param[], int pcount) {
      char which = '\0';
      Table *table;

      if (pcount == 1)
            which = param[0]->string[0];

      if ((pcount == 0) || ((which != 'w') && (which != 'b'))) {
            iprint("Usage: sit w|b\n");
            prompt();
            return;
      }

      if (primary == -1) {
            iprint("You are not observing any games.\n");
            prompt();
            return;
      }

      table = tables[primary];

      if ((table->players[0] == pme) || (table->players[1] == pme)) {
            iprint("You are already seated.\n");
            prompt();
            return;
      }

      which = (char)((which == 'w') ? 0 : 1);
      if (table->players[(int)which] != NULL) {
            iprint("That seat is occupied.\n");
            prompt();
            return;
      }

      nprinttop((uchar)primary, (char)TOP_SIT, &which, 1);
}

static void com_sought(String *param[], int pcount) {
      Table *table;
      int i, j, count = 0;
      char srating[16], shandle[18];
      bool white;
      bool all = false;
      Player *p;
      String *formula;

      if (pcount == 1) {
            if (strcmp(param[0]->string, "a") &&
                        strcmp(param[0]->string, "all")) {
                  iprint("Usage: sought [a|all]\n");
                  prompt();
                  return;
            }

            all = true;
      } else {
            /* Short-circuit formula tests. */
            formula = variables[VAR_FORMULA].string;
            if ((formula == NULL) || (formula->length == 0))
                  all = true;
      }

      for (i = 0; i < TABLE_MAX; i++)
            if ((table = tables[i]) != NULL) {
                  if (table->protection == 2)
                        continue;

                  p = NULL;

                  if (table->players[0] != NULL) {
                        if (table->players[1] != NULL)
                              continue;

                        p = table->players[0];
                        white = true;
                  } else if (table->players[1] != NULL) {
                        p = table->players[1];
                        white = false;
                  } else {
                        continue;
                  }

                  if (!all) {
                        /* Test against formula. */
                        table->players[white ? 1 : 0] = pme;
                        j = checkFormula(table);
                        table->players[white ? 1 : 0] = NULL;
                        if (!j)
                              continue;
                  }

                  prating(srating, p->rating, 4);

                  for (j = 0; (j < 17) && (p->handle[j] != '\0'); j++)
                        shandle[j] = p->handle[j];
                  shandle[j] = '\0';

                  iprintf("%3d %s %-17s %3d %3d %-7s %-9s [%s]    0-9999 m\n",
                              i, srating, shandle, tabletime(table),
                              tableinc(table),
                              tablerated(table) ? "rated" : "unrated",
                              strGameType(table),
                              white ? "white" : "black");

                  count++;
            }

      iprintf("%d ad%s displayed.\n", count, (count == 1) ? "" : "s");
      prompt();
}

static void com_stand(String *param[], int pcount) {
      Table *table;

      if (primary == -1) {
            iprint("You are not observing any games.\n");
            prompt();
            return;
      }

      table = tables[primary];

      if ((table->players[0] != pme) && (table->players[1] != pme)) {
            iprintf("You are not seated at table %d.\n", primary);
            prompt();
            return;
      }

      nprinttop((uchar)primary, (char)TOP_STAND, NULL, 0);
}

static void com_start(String *param[], int pcount) {
      Table *table;

      if (primary == -1) {
            iprint("You are not observing any games.\n");
            prompt();
            return;
      }

      table = tables[primary];
      if ((table->players[0] != pme) && (table->players[1] != pme)) {
            iprintf("You are not seated at table %d.\n", primary);
            prompt();
            return;
      }

      nprinttop((uchar)primary, (char)TOP_START, NULL, 0);
}

static void com_sublist(String *param[], int pcount) {
      com_addsublist(param, pcount, true);
}

static void xtell(String *param[], int pcount, bool xtell) {
      String *who;
      Player *p;

      if (pcount != 2) {
            iprintf("Usage: %stell <who> <message>\n", xtell ? "x" : "");
            prompt();
            return;
      }

      if (!strcmp(param[0]->string, ".")) {
            if (lasttell == NULL) {
                  iprint("I don't know who to tell that to.\n");
                  prompt();
                  return;
            } else {
                  p = lasttell;
            }
      } else if ((p = completeHandle(param[0])) == NULL) {
            return;
      }

      who = StringNew(p->lhandle, -1);

      if (!xtell)
            lasttell = p;

      packutfString(who, who);
      packutfStringP(who, param[1]);
      nprintropString(ROP_TELL, who);

      iprint("(told ");
      iprint(p->handle);
      iprint(")\n");
      prompt();

      StringFree(who);
}

static void com_tell(String *param[], int pcount) {
      xtell(param, pcount, false);
}

static void com_time(String *param[], int pcount) {
      Table *table;
      char wr[16], br[16];

      if (primary == -1) {
            iprint("You are not playing or observing a game.\n");
            prompt();
            return;
      }

      table = tables[primary];

      prating(wr, table->players[0]->rating, 4);
      prating(br, table->players[1]->rating, 4);

      iprintf("Game %d: %s (%s) %s (%s) %srated %s %d %d\n\n",
                  table->number,
                  phandle(table->players[0]),
                  wr,
                  phandle(table->players[1]),
                  br,
                  tablerated(table) ? "" : "un",
                  strGameType(table),
                  tabletime(table),
                  tableinc(table));

      iprintf("White Clock : %s\n", fmttime_ms(timeleft(table, 0)));
      iprintf("Black Clock : %s\n", fmttime_ms(timeleft(table, 1)));

      prompt();
}

static void com_unalias(String *param[], int pcount) {
      int i;

      if (pcount == 0) {
            iprint("Usage: unalias <alias>\n");
            prompt();
            return;
      }

      lowercase(param[0]->string);
      for (i = 0; (i < ALIAS_MAX) && (aliases[i] != NULL); i++) {
            if (!strcmp(aliases[i]->alias, param[0]->string)) {
                  free(aliases[i]->alias);
                  free(aliases[i]->mapping);
                  free(aliases[i]);
                  aliases[i] = NULL;

                  collapse_aliases();

                  iprint("Alias ");
                  iprint(param[0]->string);
                  iprint(" removed.\n");
                  prompt();
                  return;
            }
      }

      iprint("You have no alias ");
      iprint(param[0]->string);
      iprint(".\n");
      prompt();
}

static void com_unobserve(String *param[], int pcount) {
      char number;

      if (pcount == 0) {
            if (primary == -1) {
                  iprintf("You are not observing any games.\n");
                  prompt();
                  return;
            }

            number = primary;
            nprintrop((char) ROP_UNOBSERVE, &number, 1);
      } else {
            observe_unobserve(param, pcount, true);
      }
}

static void com_variables(String *param[], int pcount) {
      Variable *var = variables;
      char *vstr[64];
      char *vstr_s[64];
      char **vp = vstr;
      char **vsp = vstr_s;

      iprint("Variable settings of ");
      iprint(pme->handle);
      iprint(":\n");

      while (var->type != VAR_END) {
            *vp = NULL;
            *vsp = NULL;

            switch ((int)var->type) {
                  case VAR_NUMERIC:
                  case VAR_BOOLEAN:
                        *vp = malloc(256);
                        if (*vp != NULL)
                              snprintf(*vp, 256, "%s=%d", var->name,
                                    var->number);
                        break;

                  case VAR_STRING:
                        if ((var->string != NULL) && (var->string->string[0] != '\0')) {
                              *vsp = malloc(strlen(var->name) +
                                    var->string->length + 3);
                              if (*vsp != NULL)
                                    sprintf(*vsp, "%s: %s",
                                          var->name,
                                          var->string->string);
                        }
                        break;
            }

            if (*vp != NULL)
                  vp++;
            if (*vsp != NULL)
                  vsp++;

            var++;
      }

      *vp = NULL;
      *vsp = NULL;

      columns(vstr);
      iprint("\n");

      for (vsp = vstr_s; *vsp != NULL; vsp++) {
            iprintf("%s\n", *vsp);
            free(*vsp);
      }

      prompt();

      for (vp = vstr; *vp != NULL; vp++)
            free(*vp);
}

static int sort_rating(const void *sa, const void *sb) {
      Player *a = *(Player **)sa;
      Player *b = *(Player **)sb;

      return      (a->rating < b->rating) ?  1 :
            (a->rating > b->rating) ? -1 :
            strcmp(a->lhandle, b->lhandle);
}

static int sort_handle(const void *sa, const void *sb) {
      Player *a = *(Player **)sa;
      Player *b = *(Player **)sb;
      int cmp = strcmp(a->lhandle, b->lhandle);

      return      (cmp != 0) ? cmp :
            (a->rating < b->rating) ?  1 :
            (a->rating > b->rating) ? -1 : 0;
}

static void com_who(String *param[], int pcount) {
      Player *who[PLAYER_MAX];
      Player *pl;
      int i, count, lcount;
      short rating;
      char srating[64];
      char **items, **ip;
      char status;
      Table *obs[TABLE_MAX];
      Table **op;

      char *flags;
      char order = 'b';
      char filter = '\0';
      bool noratings = false;

      if (pcount == 1) {
            /*
             * X: can't do
             * ^n: alias for n
             * 
             * USERS TO DISPLAY
             * X    o: Only open players.
             * X    r: Only players for rated matches.
             *      f: Only free players (not playing a game).
             * ^f   a: Only available players (open & free).
             * X    R: Only registered players.
             * X    U: Only unregistered players.
             *   #i#j: Only a portion of the who list; #j divides the list into j segments;
             *         #i will display the i-th segment; #i cannot be higher than #j; (there
             *         are three shortcuts: "who 1" works the same as "who 13", "who 2" works
             *         the same as "who 23" and "who 3" works the same as "who 33").  [See
             *         the special note below.]
             */
            flags = param[0]->string;
            for (i = 0; i < param[0]->length; i++) {
                  switch (flags[i]) {
                        case 'f':
                        case 'a':
                              filter = 'f';
                              break;

                        case 's':
                        case 'b':
                        case 'L':
                              order = 'b';
                              break;

                        case 'A':
                              order = 'A';
                              break;

                        case 'l':
                              noratings = true;
                              break;

                        default:
                              iprint("Usage: who [f][a][s][b][L][A][l]\n");
                              prompt();
                              return;
                  }
            }
      }

      param = param;
      pcount = pcount;

      items = malloc(sizeof(char *) * (PLAYER_MAX + 1));
      if (items == NULL) {
            iprint("Out of memory!\n");
            prompt();
            return;
      }
      ip = items;

      count = 0;
      for (i = 0; i < PLAYER_MAX; i++) {
            if (players[i] != NULL)
                  who[count++] = players[i];
      }
      who[count] = NULL;

      qsort(who, count, sizeof(Player *), (order == 'b') ? sort_rating : sort_handle);

      for (i = 0; i < count; i++) {
            pl = who[i];

            /*
             * O     we do this
             * X     not possible on Yahoo!
             * ?     we could do this at great expense
             * 
             * Status codes:
             * O ^   involved in a game
             * X ~   running a simul match
             * ? :   not open for a match
             * O #   examining a game
             * ? .   inactive for 5 minutes or longer, or if "busy" is set
             * O     not busy
             * X &   involved in a tournament
             *
             * We add one:
             *   ,   Observing a table
             */
            status = ' ';

            /* Check in-game and examining */
            observing(obs, pl);
            for (op = obs; *op != NULL; op++) {
                  status = ',';
                  if ((((*op)->players[0] == pl) && ((*op)->players[1] != NULL)) ||
                      (((*op)->players[1] == pl) && ((*op)->players[0] != NULL))) {
                        status = '^';
                        break;
                  }
            }

            if ((filter == 'f') && (status == '^'))
                  continue;

            *ip = malloc(256);
            if (*ip == NULL)
                  break;

            rating = pl->rating;
            prating(srating, rating, 4);

            if (noratings)
                  mstrncpy(*ip, pl->handle, 256);
            else
                  snprintf(*ip, 256, "%s%c%s", srating, status, pl->handle);

            ip++;
      }

      *ip = NULL;
      lcount = columns(items);

      if (snprintf(srating, sizeof(srating),
      "\n %d players displayed (of %d).\n", lcount, count) != -1) {
            iprint(srating);
            prompt();
      }

      ip = items;
      while (*ip != NULL) {
            free(*ip);
            ip++;
      }
}

static void com_xkibitz(String *param[], int pcount) {
      String *message;
      Table *obs[TABLE_MAX], **optr = obs;
      int number;
      char temp[64];

      if ((pcount < 2) || !isnumeric(param[0]->string)) {
            iprint("Usage: xkibitz <table> <message>\n");
            prompt();
            return;
      }

      number = atoi(param[0]->string);

      if ((number < 1) || (number > TABLE_MAX) || (tables[number] == NULL)) {
            iprint("There is no such game.\n");
            prompt();
            return;
      }

      observing(obs, pme);
      while (*optr != NULL) {
            if ((*optr)->number == number) {
                  message = packutfString(StringNull(), param[1]);
                  nprinttopString((uchar)number, (char)TOP_KIBITZ, message);
                  StringFree(message);
                  return;
            }
            optr++;
      }

      snprintf(temp, sizeof(temp), "You are not observing game %d.\n", number);
      iprint(temp);
      prompt();
}

static void com_xtell(String *param[], int pcount) {
      xtell(param, pcount, true);
}

static void com_znotify(String *param[], int pcount) {
      int i;
      bool gotone = false;

      for (i = 0; i < PLAYER_MAX; i++) {
            if ((players[i] != NULL) && inList(&lists[LIST_NOTIFY],
                        players[i]->lhandle)) {
                  if (!gotone) {
                        iprint("Present company on your notify list:\n  ");
                        gotone = true;
                  }

                  /* printf because these don't wrap on FICS. */
                  printf(" %s", players[i]->handle);
            }
      }

      if (!gotone)
            iprint("No one from your notify list is logged on.\n");
      else
            iprint("\n");

      iprint("No one logged in has you on their notify list.\n");
      prompt();
}

Generated by  Doxygen 1.6.0   Back to index