Crossfire Client, Branches  R11627
p_cmd.c
Go to the documentation of this file.
1 /*
2  Crossfire client, a client program for the crossfire program.
3 
4  Copyright (C) 2005,2006 Mark Wedel & Crossfire Development Team
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program; if not, write to the Free Software
18  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 
20  The author can be reached via e-mail to crossfire-devel@real-time.com
21 */
22 
23 /*
24  * Contains a lot about the commands typed into the client.
25  */
26 
27 #ifndef CPROTO
28 /* use declartions from p_cmd.h instead of doing make proto on this file */
29 
30 #include <client.h>
31 #include <external.h>
32 #include <script.h>
33 #include <p_cmd.h>
34 
35 /*
36  *
37  * Help commands.
38  *
39  */
40 
41 /* TODO This should really be under /help commands or something... */
42 
43 /* This dynamically generates a list from the ConsoleCommand list. */
44 #undef CLIENTHELP_LONG_LIST
45 
46 /*
47 long-list:
48 category
49 name - description
50 name - description
51 ...
52 
53 not long list:
54 category
55 name name name ...
56 */
57 
58 #undef HELP_USE_COLOR
59 #ifdef HELP_USE_COLOR
60 #error Oops, need to put them back.
61 #else
62 #define H1(a) draw_info(a, NDI_BLACK)
63 #define H2(a) draw_info(a, NDI_BLACK)
64 #define LINE(a) draw_info(a, NDI_BLACK)
65 #endif
66 
67 #define assumed_wrap get_info_width()
68 
69 
70 /* TODO Help topics other than commands? Refer to other documents? */
71 static void do_clienthelp_list(void) {
72  ConsoleCommand ** commands_array;
73  ConsoleCommand * commands_copy;
74  int i;
75  CommCat current_cat = COMM_CAT_MISC;
76 #ifndef CLIENTHELP_LONG_LIST
77  char line_buf[MAX_BUF];
78  size_t line_len = 0;
79 
80  line_buf[0] = '\0';
81 #endif
82 
83  commands_array = get_cat_sorted_commands();
84 
85  /* Now we have a nice sorted list. */
86 
87  H1(" === Client Side Commands === ");
88 
89  for (i = 0; i < get_num_commands(); i++) {
90  commands_copy = commands_array[i];
91 
92  /* Should be LOG_SPAM but I'm too lazy to tweak it. */
93  /* LOG(LOG_INFO, "p_cmd::do_clienthelp_list", "%s Command %s", get_category_name(commands_copy->cat), commands_copy->name); */
94 
95  if (commands_copy->cat != current_cat) {
96  char buf[MAX_BUF];
97 
98 #ifndef CLIENTHELP_LONG_LIST
99  if (line_len > 0) {
100  LINE(line_buf);
101  line_buf[0] = '\0';
102  line_len = 0;
103  }
104 #endif
105 
106 #ifdef HELP_USE_COLOR
107  snprintf(buf, MAX_BUF - 1, "%s Commands:", get_category_name(commands_copy->cat));
108 #else
109  snprintf(buf, MAX_BUF - 1, " --- %s Commands --- ", get_category_name(commands_copy->cat));
110 #endif
111 
112  H2(buf);
113  current_cat = commands_copy->cat;
114  }
115 
116 #ifdef CLIENTHELP_LONG_LIST
117  if (commands_copy->desc != NULL) {
118  char buf[MAX_BUF];
119  snprintf(buf, MAX_BUF - 1, "%s - %s", commands_copy->name, commands_copy->desc);
120  LINE(buf);
121  } else {
122  LINE(commands_copy->name);
123  }
124 #else
125  {
126  size_t name_len;
127 
128  name_len = strlen(commands_copy->name);
129 
130  if (strlen(commands_copy->name) > MAX_BUF) {
131  LINE(commands_copy->name);
132  } else if (name_len > assumed_wrap) {
133  LINE(line_buf);
134  LINE(commands_copy->name);
135  line_len = 0;
136  } else if (line_len + name_len > assumed_wrap) {
137  LINE(line_buf);
138  strncpy(line_buf, commands_copy->name, name_len + 1);
139  line_len = name_len;
140  } else {
141  if (line_len > 0) {
142  strncat(line_buf, " ", 2);
143  line_len += 1;
144  }
145  strncat(line_buf, commands_copy->name, name_len + 1);
146  line_len += name_len;
147  }
148  }
149 #endif
150  }
151 
152 #ifndef CLIENTHELP_LONG_LIST
153  if (line_len) {
154  LINE(line_buf);
155  }
156 #endif
157 }
158 
159 
160 static void show_help(const ConsoleCommand * cc) {
161  {
162  char buf[MAX_BUF];
163  snprintf(buf, MAX_BUF - 1, "%s Command help:", get_category_name(cc->cat));
164  H1(buf);
165  }
166 
167  if (cc->desc != NULL) {
168  char buf[MAX_BUF];
169  snprintf(buf, MAX_BUF - 1, "%s - %s", cc->name, cc->desc);
170  H2(buf);
171  } else {
172  H2(cc->name);
173  }
174 
175  if (cc->helpfunc != NULL) {
176  const char * long_help = NULL;
177 
178  long_help = cc->helpfunc();
179 
180  if (long_help != NULL) {
181  /* For a test, let's watch draw_info() choke on newlines. */
182  /* TODO C line wrapping (get_info_width()), argh. Or move it to UI? */
183  LINE(long_help);
184  } else {
185  LINE("This command's documentation is bugged!");
186  }
187  } else {
188  LINE("This command has no extended documentation. :(");
189  }
190 }
191 
192 static void do_clienthelp(const char * arg) {
193  const ConsoleCommand * cc;
194 
195  if (!arg || !strlen(arg)) {
197  return;
198  }
199 
200  cc = find_command(arg);
201 
202  if (cc == NULL) {
203  char buf[MAX_BUF];
204  snprintf(buf, MAX_BUF - 1, "clienthelp: Unknown command %s.", arg);
205  draw_info(buf, NDI_BLACK);
206  return;
207  }
208 
209  show_help(cc);
210 
211 }
212 
213 static const char * help_clienthelp(void) {
214  return
215  "Syntax:\n"
216  "\n"
217  " clienthelp\n"
218  " clienthelp <command>\n"
219  "\n"
220  "Without any arguments, displays a list of client-side "
221  "commands.\n"
222  "\n"
223  "With arguments, displays the help for the client-side "
224  "command <command>.\n"
225  "\n"
226  "See also: serverhelp, help.";
227 }
228 
229 static void do_serverhelp(const char * arg) {
230 
231  if (arg) {
232  char buf[MAX_BUF];
233  snprintf(buf, sizeof(buf), "help %s", arg);
234  /* maybe not a must send, but we probably don't want to drop it */
235  send_command(buf, -1, 1);
236  } else {
237  send_command("help", -1, 1); /* TODO make install in server branch doesn't install def_help. */
238  }
239 }
240 
241 static const char * help_serverhelp(void) {
242  return
243  "Syntax:\n"
244  "\n"
245  " serverhelp\n"
246  " serverhelp <command>\n"
247  "\n"
248  "Fetches help from the server.\n"
249  "\n"
250  "Note that currently nothing can be done (without a recompile) if a "
251  "client command masks a server command.\n"
252  "\n"
253  "See also: clienthelp, help.";
254 }
255 
256 
257 static void command_help(const char *cpnext) {
258  if (cpnext) {
259  const ConsoleCommand * cc;
260  char buf[MAX_BUF];
261 
262  cc = find_command(cpnext);
263  if (cc != NULL) {
264  show_help(cc);
265  } else {
266  snprintf(buf, sizeof(buf), "help %s", cpnext);
267  /* maybe not a must send, but we probably don't want to drop it */
268  send_command(buf, -1, 1);
269  }
270  } else {
272  /* Now fetch (in theory) command list from the server.
273  TODO Protocol command - feed it to the tab completer.
274 
275  Nope! It effectivey fetches '/help commands for commands'.
276  */
277  send_command("help", -1, 1); /* TODO make install in server branch doesn't install def_help. */
278  }
279 }
280 
281 static const char * help_help(void) {
282  return
283  "Syntax:\n"
284  "\n"
285  " help\n"
286  " help <topic>\n"
287  "\n"
288  "Without any arguments, displays a list of client-side "
289  "commands, and fetches the without-arguments help from "
290  "the server.\n"
291  "\n"
292  "With arguments, first checks if there's a client command "
293  "named <topic>. If there is, display it's help. If there "
294  "isn't, send the topic to the server.\n"
295  "\n"
296  "See also: clienthelp, serverhelp.";
297 }
298 
299 
300 /*
301  *
302  * Other commands.
303  *
304  */
305 
306 static void set_command_window(const char *cpnext)
307 {
308  if (!cpnext) {
309  draw_info("cwindow command requires a number parameter", NDI_BLACK);
310  } else {
311  want_config[CONFIG_CWINDOW] = atoi(cpnext);
314  else
316  }
317 }
318 
319 static void command_foodbep(const char *cpnext)
320 {
321  (void)cpnext; /* __UNUSED__ */
324  draw_info("Warning bell when low on food disabled", NDI_BLACK);
325  } else {
327  draw_info("Warning bell when low on food enabled", NDI_BLACK);
328  }
330 }
331 
332 
333 
334 
335 const char * get_category_name(CommCat cat) {
336  const char * cat_name;
337 
338  /* HACK Need to keep this in sync. with player.h */
339  switch(cat) {
340  case COMM_CAT_MISC: cat_name = "Miscellaneous"; break;
341  case COMM_CAT_HELP: cat_name = "Help"; break;
342  case COMM_CAT_INFO: cat_name = "Informational"; break;
343  case COMM_CAT_SETUP: cat_name = "Configuration"; break;
344  case COMM_CAT_SCRIPT: cat_name = "Scripting"; break;
345  case COMM_CAT_DEBUG: cat_name = "Debugging"; break;
346  default: cat_name = "PROGRAMMER ERROR"; break;
347  }
348 
349  return cat_name;
350 }
351 
352 
353 /*
354  * Command table.
355  *
356  * Implementation basically stolen verbatim from the server.
357  */
358 
359 /* "Typecasters" (and some forwards) */
360 static void do_script_list(const char * ignored) { script_list(); }
361 static void do_clearinfo(const char * ignored) { menu_clear(); }
362 
363 static void do_disconnect(const char * ignored) {
364 #ifdef WIN32
365  closesocket(csocket.fd);
366 #else
367  close(csocket.fd);
368 #endif
369  csocket.fd=-1;
370 
371  /* the gtk clients need to do some cleanup logic - otherwise,
372  * they start hogging CPU.
373  */
375  return;
376 }
377 
378 #ifdef HAVE_DMALLOC_H
379 #ifndef DMALLOC_VERIFY_NOERROR
380  #define DMALLOC_VERIFY_NOERROR 1
381 #endif
382 static void do_dmalloc(const char * ignored) {
383  if (dmalloc_verify(NULL)==DMALLOC_VERIFY_NOERROR)
384  draw_info("Heap checks out OK", NDI_BLACK);
385  else
386  draw_info("Heap corruption detected", NDI_RED);
387 }
388 #endif
389 
390 static void do_inv(const char * ignored) { print_inventory (cpl.ob); }
391 
392 static void do_magicmap(const char * ignored) {
393  cpl.showmagic=1;
394  draw_magic_map();
395 }
396 
397 static void do_metaserver(const char * ignored) {
400  else
401  draw_info("Unable to get metaserver information.", NDI_BLACK);
402 }
403 
404 static void do_savedefaults(const char * ignored) { save_defaults(); }
405 
406 static void do_savewinpos(const char * ignored) { save_winpos(); }
407 
408 static void do_take(const char * used) { command_take("take", used); /* I dunno why they want it. */ }
409 
410 static void do_num_free_items(const char * ignored) {
411  LOG(LOG_INFO,"common::extended_command","num_free_items=%d", num_free_items());
412 }
413 
414 static void do_clienthelp(const char * arg); /* Forward. */
415 
416 /* Help "typecasters". */
417 #include "../help/chelp.h"
418 
419 static const char * help_bind(void) { return HELP_BIND_LONG; }
420 
421 static const char * help_unbind(void) { return HELP_UNBIND_LONG; }
422 
423 static const char * help_magicmap(void) { return HELP_MAGICMAP_LONG; }
424 
425 static const char * help_inv(void) { return HELP_INV_LONG; }
426 
427 static const char * help_cwindow(void) {
428  return
429  "Syntax:\n"
430  "\n"
431  " cwindow <val>\n"
432  "\n"
433  "set size of command"
434  "window (if val is exceeded"
435  "client won't send new"
436  "commands to server\n\n"
437  "(What does this mean, 'put a lid on it'?) TODO";
438 }
439 
440 static const char * help_script(void) {
441  return
442  "Syntax:\n"
443  "\n"
444  " script <pathname>\n"
445  "\n"
446  "Run the program at path <name>"
447  "as a Crossfire client script."
448  "See Documentation/Script.html";
449 }
450 
451 static const char * help_scripttell(void) {
452  return
453  "Syntax:\n"
454  "\n"
455  " scripttell <yourname> <data>\n"
456  "\n"
457  "?";
458 }
459 
460 /* Toolkit-dependent. */
461 static const char * help_savewinpos(void) {
462  return
463  "Syntax:\n"
464  "\n"
465  " savewinpos\n"
466  "\n"
467  "save window positions - split windows mode only.";
468 }
469 
470 static const char * help_metaserver(void) {
471  /* TODO Add command_escape() where appropriate. On the other
472  hand, that can lead to a meaningless syntax-display API.*/
473 
474  return
475  "Syntax:\n"
476  "\n"
477  " metaserver\n"
478  "\n"
479  "Get updated list of servers "
480  "from the metaserver and show it."
481  "This is the same information that the client "
482  "uses to show a list of servers when it starts.\n"
483  "\n"
484  "Warning: This command may freeze the client until it gets the list.";
485 }
486 
487 static const char * help_scriptkill(void) {
488  return
489  "Syntax:\n"
490  "\n"
491  " scriptkill <name>\n"
492  "\n"
493  "Stop scripts named <name>.\n"
494  "(Not guaranteed to work?)";
495 }
496 
497 static const char * help_showweight(void) {
498  return
499  "Syntax:\n"
500  "\n"
501  " showweight\n"
502  " showweight inventory\n"
503  " showweight look\n"
504  "\n"
505  "(Or any prefix of the arguments.)"
506  "Toggles if you see the weight of"
507  "items in your inventory (also if"
508  "no argument given) or your"
509  "look-window.";
510 }
511 
512 /*
513 * draw_info("Information Commands", NDI_NAVY);*
514  draw_info(" inv - *recursively* print your", NDI_BLACK);
515  draw_info(" inventory - includes containers.", NDI_BLACK);
516  draw_info(" mapredraw, showinfo, take", NDI_BLACK);
517  draw_info(" help - show this message", NDI_BLACK);
518  draw_info(" help <command> - get more information on a", NDI_BLACK);
519  draw_info(" command (Server command only?)", NDI_BLACK);
520  draw_info(" showicon - draw status icons in", NDI_BLACK);
521  draw_info(" inventory window", NDI_BLACK);
522  draw_info(" showweight - show weight in inventory", NDI_BLACK);
523  draw_info(" look windows", NDI_BLACK);
524  draw_info("Scripting Commands", NDI_NAVY);
525  draw_info("Client Side Debugging Commands", NDI_NAVY);
526 #ifdef HAVE_DMALLOC_H
527  draw_info(" dmalloc - Check heap?", NDI_BLACK);
528 #endif
529 */
530 
531 /* Forward. */
532 static void do_clienthelp(const char * currently_ignored);
533 
534 /* TODO Wrap these? Um. */
536  /* From player.h:
537  name, cat,
538  func, helpfunc,
539  long_desc
540  */
541 
542  {
543  "autorepeat", COMM_CAT_MISC,
544  set_autorepeat, NULL,
545  "toggle autorepeat" /* XXX Eh? */
546  },
547 
548  {
549  "bind", COMM_CAT_SETUP,
552  },
553 
554  {
555  "script", COMM_CAT_SCRIPT,
557  NULL
558  },
559 #ifdef HAVE_LUA
560  { "lua_load", COMM_CAT_SCRIPT,
561  script_lua_load, NULL, NULL
562  },
563 
564  { "lua_list", COMM_CAT_SCRIPT,
565  script_lua_list, NULL, NULL
566  },
567 
568  { "lua_kill", COMM_CAT_SCRIPT,
569  script_lua_kill, NULL, NULL
570  },
571 #endif
572  {
573  "scripts", COMM_CAT_SCRIPT,
574  do_script_list, NULL,
575  "List the running scripts(?)"
576  },
577 
578  {
579  "scriptkill", COMM_CAT_SCRIPT,
581  NULL
582  },
583 
584  {
585  "scripttell", COMM_CAT_SCRIPT,
587  NULL
588  },
589 
590  {
591  "clearinfo", COMM_CAT_MISC,
592  do_clearinfo, NULL,
593  "clear the info window"
594  },
595 
596  {
597  "cwindow", COMM_CAT_SETUP,
599  NULL
600  },
601 
602  {
603  "disconnect", COMM_CAT_MISC,
604  do_disconnect, NULL,
605  "close connection to server"
606  },
607 
608 
609 #ifdef HAVE_DMALLOC_H
610  {
611  "dmalloc", COMM_CAT_DEBUG,
612  do_dmalloc, NULL,
613  NULL
614  },
615 #endif
616 
617  {
618  "foodbeep", COMM_CAT_SETUP,
619  command_foodbep, NULL,
620  "toggle audible low on food warning"
621 
622  },
623 
624  {
625  "help", COMM_CAT_HELP,
627  NULL
628  },
629 
630  {
631  "clienthelp", COMM_CAT_HELP,
633  "Client-side command information"
634  },
635 
636  {
637  "serverhelp", COMM_CAT_HELP,
639  "Server-side command information"
640  },
641 
642  {
643  "inv", COMM_CAT_DEBUG,
644  do_inv, help_inv,
646  },
647 
648  {
649  "magicmap", COMM_CAT_MISC,
652  },
653 
654  {
655  "metaserver", COMM_CAT_INFO,
657  "Print 'metaserver information'. Warning - your client will pause."
658  },
659 
660  {
661  "savedefaults", COMM_CAT_SETUP,
662  do_savedefaults, NULL,
663  HELP_SAVEDEFAULTS_SHORT /* How do we make sure showicons stays on? */
664  },
665 
666  {
667  "savewinpos", COMM_CAT_SETUP,
669  "Saves the position and sizes of windows." /* Panes? */
670  },
671 
672  {
673  "scroll", COMM_CAT_SETUP,
674  set_scroll, NULL,
675  "toggle scroll/wrap mode in info window"
676  },
677 
678  {
679  "showicon", COMM_CAT_SETUP,
680  set_show_icon, NULL,
681  "Toggles if you see the worn, locked, cursed etc state in the inventory pane."
682  },
683 
684  {
685  "showweight", COMM_CAT_SETUP,
687  "Toggles if you see item weights in inventory look windows."
688  },
689 
690  {
691  "take", COMM_CAT_MISC,
692  do_take, NULL,
693  NULL
694  },
695 
696  {
697  "unbind", COMM_CAT_SETUP,
699  NULL
700  },
701 
702  {
703  "num_free_items", COMM_CAT_DEBUG,
704  do_num_free_items, NULL,
705  "log the number of free items?"
706  },
707  {
708  "show", COMM_CAT_SETUP,
709  command_show, NULL,
710  "Change what items to show in inventory"
711  },
712 
713 };
714 
715 const int CommonCommandsSize = sizeof(CommonCommands) / sizeof(ConsoleCommand);
716 
717 #ifdef TOOLKIT_COMMANDS
718 extern ConsoleCommand ToolkitCommands[];
719 
720 extern const int ToolkitCommandsSize;
721 #endif
722 
723 /* ------------------------------------------------------------------ */
724 
726 
727 int get_num_commands(void) { return num_commands; }
728 
730 
731 static int sort_by_name(const void * a_, const void * b_)
732 {
733  ConsoleCommand * a = *((ConsoleCommand **)a_);
734  ConsoleCommand * b = *((ConsoleCommand **)b_);
735 
736  return strcmp(a->name, b->name);
737 }
738 
740 
741 /* Sort by category, then by name. */
742 static int sort_by_category(const void *a_, const void *b_)
743 {
744  /* Typecasts, so it goes. */
745  ConsoleCommand * a = *((ConsoleCommand **)a_);
746  ConsoleCommand * b = *((ConsoleCommand **)b_);
747 
748  if (a->cat == b->cat) {
749  return strcmp(a->name, b->name);
750  }
751 
752  return a->cat - b->cat;
753 }
754 
755 void init_commands(void) {
756  int i;
757 
758 #ifdef TOOLKIT_COMMANDS
759  init_toolkit_commands();
760 
761  /* TODO I dunno ... go through the list and print commands without helps? */
762  num_commands = CommonCommandsSize + ToolkitCommandsSize;
763 #else
764  num_commands = CommonCommandsSize;
765 #endif
766 
767  /* Make a list of (pointers to statically allocated!) all the commands.
768  We have a list; the toolkit has a
769  ToolkitCommands and ToolkitCommandsSize, initialized before calling this.
770  */
771 
772  /* XXX Leak! */
773  name_sorted_commands = malloc(sizeof(ConsoleCommand *) * num_commands);
774 
775  for (i = 0; i < CommonCommandsSize; i++) {
776  name_sorted_commands[i] = &CommonCommands[i];
777  }
778 
779 #ifdef TOOLKIT_COMMANDS
780  for(i = 0; i < ToolkitCommandsSize; i++) {
781  name_sorted_commands[CommonCommandsSize + i] = &ToolkitCommands[i];
782  }
783 #endif
784 
785  /* Sort them. */
786  qsort(name_sorted_commands, num_commands, sizeof(ConsoleCommand *), sort_by_name);
787 
788  /* Copy the list, then sort it by category. */
789  cat_sorted_commands = malloc(sizeof(ConsoleCommand *) * num_commands);
790 
791  memcpy(cat_sorted_commands, name_sorted_commands, sizeof(ConsoleCommand *) * num_commands);
792 
793  qsort(cat_sorted_commands, num_commands, sizeof(ConsoleCommand *), sort_by_category);
794 
795  /* TODO Add to the list of tab-completion items. */
796 }
797 
798 #ifndef tolower
799 #define tolower(C) (((C) >= 'A' && (C) <= 'Z')? (C) - 'A' + 'a': (C))
800 #endif
801 
802 const ConsoleCommand * find_command(const char * cmd) {
803  ConsoleCommand ** asp_p = NULL, dummy;
804  ConsoleCommand * dummy_p;
805  ConsoleCommand * asp;
806  char *cp, *cmd_cpy;
807  cmd_cpy = strdup(cmd);
808 
809  for (cp=cmd_cpy; *cp; cp++) {
810  *cp =tolower(*cp);
811  }
812 
813  dummy.name = cmd_cpy;
814  dummy_p = &dummy;
815  asp_p = bsearch(
816  (void *)&dummy_p,
817  (void *)name_sorted_commands,
818  num_commands,
819  sizeof(ConsoleCommand *),
820  sort_by_name);
821 
822  if (asp_p == NULL)
823  {
824  free(cmd_cpy);
825  return NULL;
826  }
827 
828  asp = *asp_p;
829 
830  /* TODO The server's find_command() searches first the commands,
831  then the emotes. We might have to do something similar someday, too. */
832  /* if (asp == NULL) search something else? */
833 
834  free(cmd_cpy);
835 
836  return asp;
837 }
838 
839 
847  return cat_sorted_commands;
848 }
849 
850 
851 /* Tries to handle command cp (with optional params in cpnext, which may be null)
852  * as a local command. If this was a local command, returns true to indicate
853  * command was handled.
854  * This code was moved from extended_command so scripts ca issue local commands
855  * to handle keybindings or anything else.
856  */
857 
858 int handle_local_command(const char* cp, const char * cpnext) {
859  const ConsoleCommand * cc = NULL;
860 
861  cc = find_command(cp);
862 
863  if (cc == NULL) {
864  return FALSE;
865  }
866 
867  if (cc->dofunc == NULL) {
868  char buf[MAX_BUF];
869 
870  snprintf(buf, MAX_BUF - 1, "Client command %s has no implementation!", cc->name);
871  draw_info(buf, NDI_RED);
872 
873  return FALSE;
874  }
875 
876  cc->dofunc(cpnext);
877 
878  return TRUE;
879 }
880 
881 /* This is an extended command (ie, 'who, 'whatever, etc). In general,
882  * we just send the command to the server, but there are a few that
883  * we care about (bind, unbind)
884  *
885  * The command passed to us can not be modified - if it is a keybinding,
886  * we get passed the string that is that binding - modifying it effectively
887  * changes the binding.
888  */
889 
890 void extended_command(const char *ocommand) {
891  const char *cp = ocommand;
892  char *cpnext, command[MAX_BUF];
893 
894  if ((cpnext = strchr(cp, ' '))!=NULL) {
895  int len = cpnext - ocommand;
896  if (len > (MAX_BUF -1 )) len = MAX_BUF-1;
897 
898  strncpy(command, ocommand, len);
899  command[len] = '\0';
900  cp = command;
901  while (*cpnext == ' ')
902  cpnext++;
903  if (*cpnext == 0)
904  cpnext = NULL;
905  }
906 
907  /* cp now contains the command (everything before first space),
908  * and cpnext contains everything after that first space. cpnext
909  * could be NULL.
910  */
911 #ifdef HAVE_LUA
912  if ( script_lua_command(cp, cpnext) )
913  return;
914 #endif
915 
916  /* If this isn't a client-side command, send it to the server. */
917  if (!handle_local_command(cp, cpnext)) {
918  /* just send the command(s) (if `ocommand' is a compound command */
919  /* then split it and send each part seperately */
920  /* TODO Remove this from the server; end of commands.c. */
921  strncpy(command, ocommand, MAX_BUF-1);
922  command[MAX_BUF-1]=0;
923  cp = strtok(command, ";");
924  while ( cp ) {
925  while( *cp == ' ' ) cp++; /* throw out leading spaces; server
926  does not like them */
927  send_command(cp, cpl.count, 0);
928  cp = strtok(NULL, ";");
929  }
930  }
931 }
932 
933 
934 /* ------------------------------------------------------------------ */
935 
936 /* This list is used for the 'tab' completion, and nothing else.
937  * Therefore, if it is out of date, it isn't that terrible, but
938  * ideally it should stay somewhat up to date with regards to
939  * the commands the server supports.
940  */
941 
942 /* TODO Dynamically generate. */
943 
944 static const char *const commands[] = {
945 "accuse", "afk", "apply", "applymode", "archs", "beg", "bleed", "blush",
946 "body", "bounce", "bow", "bowmode", "brace", "build", "burp", "cackle", "cast",
947 "chat", "chuckle", "clap", "cointoss", "cough", "cringe", "cry", "dance",
948 "disarm", "dm", "dmhide", "drop", "dropall", "east", "examine", "explore",
949 "fire", "fire_stop", "fix_me", "flip", "frown", "gasp", "get", "giggle",
950 "glare", "grin", "groan", "growl", "gsay", "help", "hiccup", "hiscore", "hug",
951 "inventory", "invoke", "killpets", "kiss", "laugh", "lick", "listen", "logs",
952 "mapinfo", "maps", "mark", "me", "motd", "nod", "north", "northeast",
953 "northwest", "orcknuckle", "output-count", "output-sync", "party", "peaceful",
954 "petmode", "pickup", "players", "poke", "pout", "prepare", "printlos", "puke",
955 "quests", "quit", "ready_skill", "rename", "reply", "resistances",
956 "rotateshoottype", "run", "run_stop", "save", "say", "scream", "search",
957 "search-items", "shake", "shiver", "shout", "showpets", "shrug", "shutdown",
958 "sigh", "skills", "slap", "smile", "smirk", "snap", "sneeze", "snicker",
959 "sniff", "snore", "sound", "south", "southeast", "southwest", "spit",
960 "statistics", "stay", "strings", "strut", "sulk", "take", "tell", "thank",
961 "think", "throw", "time", "title", "twiddle", "use_skill", "usekeys",
962 "version", "wave", "weather", "west", "whereabouts", "whereami", "whistle",
963 "who", "wimpy", "wink", "yawn",
964 };
965 #define NUM_COMMANDS ((int)(sizeof(commands) / sizeof(char*)))
966 
967 /* Player has entered 'command' and hit tab to complete it.
968  * See if we can find a completion. Returns matching
969  * command. Returns NULL if no command matches.
970  */
971 
972 const char * complete_command(const char *command)
973 {
974  int i, len, display;
975  const char *match;
976  static char result[64];
977  char list[500];
978 
979  len = strlen(command);
980 
981  if (len == 0)
982  return NULL;
983 
984  display = 0;
985  strcpy(list, "Matching commands:");
986 
987  /* TODO Partial match, e.g.:
988  If the completion list was:
989  wear
990  wet #?
991 
992  If we type 'w' then hit tab, put in the e.
993 
994  Basically part of bash (readline?)'s behaviour.
995  */
996 
997  match = NULL;
998 
999  /* check server side commands */
1000  for (i=0; i<NUM_COMMANDS; i++) {
1001  if (!strncmp(command, commands[i], len)) {
1002  if (display) {
1003  snprintf(list + strlen(list), 499 - strlen(list), " %s", commands[i]);
1004  } else if (match != NULL) {
1005  display = 1;
1006  snprintf(list + strlen(list), 499 - strlen(list), " %s %s", match, commands[i]);
1007  match = NULL;
1008  } else
1009  match = commands[i];
1010  }
1011  }
1012 
1013  /* check client side commands */
1014  for (i=0; i<CommonCommandsSize; i++) {
1015  if (!strncmp(command, CommonCommands[i].name, len)) {
1016  if (display) {
1017  snprintf(list + strlen(list), 499 - strlen(list), " %s", CommonCommands[i].name);
1018  } else if (match != NULL) {
1019  display = 1;
1020  snprintf(list + strlen(list), 499 - strlen(list), " %s %s", match, CommonCommands[i].name);
1021  match = NULL;
1022  } else
1023  match = CommonCommands[i].name;
1024  }
1025  }
1026 
1027  if (match == NULL) {
1028  if (display) {
1029  strncat(list, "\n", 499 - strlen(list));
1030  draw_info(list, NDI_BLACK);
1031  }
1032  else
1033  draw_info("No matching command.\n", NDI_BLACK);
1034  /* No match. */
1035  return NULL;
1036  }
1037 
1038  /*
1039  * Append a space to allow typing arguments. For commands without arguments
1040  * the excess space should be stripped off automatically.
1041  */
1042  snprintf(result, sizeof(result), "%s ", match);
1043 
1044  return result;
1045 }
1046 
1047 #endif /* CPROTO */
#define HELP_BIND_SHORT
Definition: chelp.h:6
static GtkWidget * list
Definition: gx11.c:2982
void set_autorepeat(const char *s)
Definition: gx11.c:4448
int num_free_items(void)
Definition: item.c:444
void script_lua_list(const char *param)
#define COMMAND_WINDOW
Definition: client.h:54
int script_lua_command(const char *command, const char *param)
uint32 count
Definition: client.h:295
#define LINE(a)
Definition: p_cmd.c:64
int handle_local_command(const char *cp, const char *cpnext)
Definition: p_cmd.c:858
static const char * help_unbind(void)
Definition: p_cmd.c:421
void command_take(const char *command, const char *cpnext)
Definition: player.c:264
void cleanup_connection()
Definition: gx11.c:329
void set_show_icon(const char *s)
Definition: inventory.c:1303
void script_lua_kill(const char *param)
#define HELP_MAGICMAP_SHORT
Definition: chelp.h:54
#define HELP_BIND_LONG
Definition: chelp.h:7
CommFunc dofunc
Definition: p_cmd.h:64
#define NUM_COMMANDS
Definition: p_cmd.c:965
#define HELP_MAGICMAP_LONG
Definition: chelp.h:55
static void command_foodbep(const char *cpnext)
Definition: p_cmd.c:319
item * ob
Definition: client.h:272
void metaserver_show(int show_selection)
Definition: metaserver.c:807
const char * get_category_name(CommCat cat)
Definition: p_cmd.c:335
static const char *const commands[]
Definition: p_cmd.c:944
ClientSocket csocket
Definition: client.c:78
CommCat cat
Definition: p_cmd.h:63
static const char * help_showweight(void)
Definition: p_cmd.c:497
void init_commands(void)
Definition: p_cmd.c:755
static void set_command_window(const char *cpnext)
Definition: p_cmd.c:306
static const char * help_savewinpos(void)
Definition: p_cmd.c:461
sint16 want_config[CONFIG_NUMS]
Definition: init.c:50
int meta_port
Definition: client.c:67
static const char * help_clienthelp(void)
Definition: p_cmd.c:213
CommCat
Definition: p_cmd.h:48
#define H1(a)
Definition: p_cmd.c:62
static void do_clearinfo(const char *ignored)
Definition: p_cmd.c:361
#define HELP_UNBIND_LONG
Definition: chelp.h:45
void LOG(LogLevel level, const char *origin, const char *format,...)
Definition: misc.c:178
#define NDI_RED
Definition: newclient.h:204
void script_init(const char *cparams)
Definition: script.c:213
#define TRUE
Definition: client-types.h:71
const char * desc
Definition: p_cmd.h:67
static const char * help_scripttell(void)
Definition: p_cmd.c:451
#define CONFIG_CWINDOW
Definition: client.h:155
static const char * help_metaserver(void)
Definition: p_cmd.c:470
static void do_serverhelp(const char *arg)
Definition: p_cmd.c:229
#define CONFIG_FOODBEEP
Definition: client.h:172
static void show_help(const ConsoleCommand *cc)
Definition: p_cmd.c:160
int send_command(const char *command, int repeat, int must_send)
Definition: player.c:195
static void do_magicmap(const char *ignored)
Definition: p_cmd.c:392
void save_winpos(void)
Definition: gx11.c:4804
sint16 use_config[CONFIG_NUMS]
Definition: init.c:50
#define HELP_SAVEDEFAULTS_SHORT
Definition: chelp.h:59
const char * name
Definition: p_cmd.h:62
static void do_clienthelp(const char *arg)
Definition: p_cmd.c:192
Client_Player cpl
Definition: client.c:77
static void do_metaserver(const char *ignored)
Definition: p_cmd.c:397
char * name
Definition: image.c:61
int fd
Definition: client.h:97
static const char * help_scriptkill(void)
Definition: p_cmd.c:487
Display * display
Definition: x11.c:184
static const char * help_magicmap(void)
Definition: p_cmd.c:423
const int CommonCommandsSize
Definition: p_cmd.c:715
static void do_clienthelp_list(void)
Definition: p_cmd.c:71
void extended_command(const char *ocommand)
Definition: p_cmd.c:890
int num_commands
Definition: p_cmd.c:725
#define H2(a)
Definition: p_cmd.c:63
static void do_take(const char *used)
Definition: p_cmd.c:408
CommHelpFunc helpfunc
Definition: p_cmd.h:66
const char * complete_command(const char *command)
Definition: p_cmd.c:972
#define HELP_INV_SHORT
Definition: chelp.h:66
#define MAX_BUF
Definition: client-types.h:128
void script_list(void)
Definition: script.c:504
static int sort_by_category(const void *a_, const void *b_)
Definition: p_cmd.c:742
int get_num_commands(void)
Definition: p_cmd.c:727
#define HELP_INV_LONG
Definition: chelp.h:67
void save_defaults(void)
Definition: config.c:848
void bind_key(const char *params)
Definition: keys.c:648
static ConsoleCommand ** name_sorted_commands
Definition: p_cmd.c:729
static ConsoleCommand CommonCommands[]
Definition: p_cmd.c:535
static const char * help_help(void)
Definition: p_cmd.c:281
static void do_num_free_items(const char *ignored)
Definition: p_cmd.c:410
void print_inventory(item *op)
Definition: item.c:654
const ConsoleCommand * find_command(const char *cmd)
Definition: p_cmd.c:802
void script_tell(const char *params)
Definition: script.c:890
static const char * help_bind(void)
Definition: p_cmd.c:419
void menu_clear(void)
Definition: gx11.c:3241
uint8 showmagic
Definition: client.h:300
void script_kill(const char *params)
Definition: script.c:528
void set_show_weight(const char *s)
Definition: inventory.c:1258
void command_show(const char *params)
Definition: inventory.c:1153
static void do_disconnect(const char *ignored)
Definition: p_cmd.c:363
static void do_savewinpos(const char *ignored)
Definition: p_cmd.c:406
void draw_info(const char *str, int color)
Definition: gx11.c:1773
void script_lua_load(const char *name)
void draw_magic_map(void)
Definition: gx11.c:4571
int metaserver_get_info(char *metaserver, int meta_port)
Definition: metaserver.c:791
#define assumed_wrap
Definition: p_cmd.c:67
static int sort_by_name(const void *a_, const void *b_)
Definition: p_cmd.c:731
static void do_inv(const char *ignored)
Definition: p_cmd.c:390
static void command_help(const char *cpnext)
Definition: p_cmd.c:257
static const char * help_script(void)
Definition: p_cmd.c:440
void unbind_key(const char *params)
Definition: keys.c:918
char * meta_server
Definition: client.c:61
#define NDI_BLACK
Definition: newclient.h:201
#define FALSE
Definition: client-types.h:68
static ConsoleCommand ** cat_sorted_commands
Definition: p_cmd.c:739
static const char * help_serverhelp(void)
Definition: p_cmd.c:241
#define tolower(C)
Definition: p_cmd.c:799
static const char * help_inv(void)
Definition: p_cmd.c:425
static void do_script_list(const char *ignored)
Definition: p_cmd.c:360
static void do_savedefaults(const char *ignored)
Definition: p_cmd.c:404
static const char * help_cwindow(void)
Definition: p_cmd.c:427
ConsoleCommand ** get_cat_sorted_commands(void)
Definition: p_cmd.c:846
void set_scroll(const char *s)
Definition: gx11.c:4443