|
Crossfire Client, Trunk
R18666
|
00001 /* 00002 Crossfire client, a client program for the crossfire program. 00003 00004 Copyright (C) 2005-2010 Mark Wedel & Crossfire Development Team 00005 00006 This program is free software; you can redistribute it and/or modify 00007 it under the terms of the GNU General Public License as published by 00008 the Free Software Foundation; either version 2 of the License, or 00009 (at your option) any later version. 00010 00011 This program is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 GNU General Public License for more details. 00015 00016 You should have received a copy of the GNU General Public License 00017 along with this program; if not, write to the Free Software 00018 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00019 00020 The author can be reached via e-mail to crossfire-devel@real-time.com 00021 */ 00022 00028 #ifndef CPROTO 00029 /* use declarations from p_cmd.h instead of doing make proto on this file */ 00030 00031 #include <client.h> 00032 #include <external.h> 00033 #include <script.h> 00034 #include <p_cmd.h> 00035 00041 /* TODO This should really be under /help commands or something... */ 00042 00043 /* This dynamically generates a list from the ConsoleCommand list. */ 00044 #undef CLIENTHELP_LONG_LIST 00045 00046 /* 00047 long-list: 00048 category 00049 name - description 00050 name - description 00051 ... 00052 00053 not long list: 00054 category 00055 name name name ... 00056 */ 00057 00058 #undef HELP_USE_COLOR 00059 #ifdef HELP_USE_COLOR 00060 #error Oops, need to put them back. 00061 #else 00062 #define H1(a) draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, a) 00063 #define H2(a) draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, a) 00064 #define LINE(a) draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, a) 00065 #endif 00066 00067 #define assumed_wrap get_info_width() 00068 00069 /* TODO Help topics other than commands? Refer to other documents? */ 00070 00074 static void do_clienthelp_list(void) { 00075 ConsoleCommand ** commands_array; 00076 ConsoleCommand * commands_copy; 00077 int i; 00078 CommCat current_cat = COMM_CAT_MISC; 00079 #ifndef CLIENTHELP_LONG_LIST 00080 char line_buf[MAX_BUF]; 00081 size_t line_len = 0; 00082 00083 line_buf[0] = '\0'; 00084 #endif 00085 00086 commands_array = get_cat_sorted_commands(); 00087 00088 /* Now we have a nice sorted list. */ 00089 00090 H1(" === Client Side Commands === "); 00091 00092 for (i = 0; i < get_num_commands(); i++) { 00093 commands_copy = commands_array[i]; 00094 00095 /* Should be LOG_SPAM but I'm too lazy to tweak it. */ 00096 /* LOG(LOG_INFO, "p_cmd::do_clienthelp_list", "%s Command %s", get_category_name(commands_copy->cat), commands_copy->name); */ 00097 00098 if (commands_copy->cat != current_cat) { 00099 char buf[MAX_BUF]; 00100 00101 #ifndef CLIENTHELP_LONG_LIST 00102 if (line_len > 0) { 00103 LINE(line_buf); 00104 line_buf[0] = '\0'; 00105 line_len = 0; 00106 } 00107 #endif 00108 00109 #ifdef HELP_USE_COLOR 00110 snprintf(buf, MAX_BUF - 1, "%s Commands:", get_category_name(commands_copy->cat)); 00111 #else 00112 snprintf(buf, MAX_BUF - 1, " --- %s Commands --- ", get_category_name(commands_copy->cat)); 00113 #endif 00114 00115 H2(buf); 00116 current_cat = commands_copy->cat; 00117 } 00118 00119 #ifdef CLIENTHELP_LONG_LIST 00120 if (commands_copy->desc != NULL) { 00121 char buf[MAX_BUF]; 00122 snprintf(buf, MAX_BUF - 1, "%s - %s", commands_copy->name, commands_copy->desc); 00123 LINE(buf); 00124 } else { 00125 LINE(commands_copy->name); 00126 } 00127 #else 00128 { 00129 size_t name_len; 00130 00131 name_len = strlen(commands_copy->name); 00132 00133 if (strlen(commands_copy->name) > MAX_BUF) { 00134 LINE(commands_copy->name); 00135 } else if (name_len > assumed_wrap) { 00136 LINE(line_buf); 00137 LINE(commands_copy->name); 00138 line_len = 0; 00139 } else if (line_len + name_len > assumed_wrap) { 00140 LINE(line_buf); 00141 strncpy(line_buf, commands_copy->name, name_len + 1); 00142 line_len = name_len; 00143 } else { 00144 if (line_len > 0) { 00145 strncat(line_buf, " ", 2); 00146 line_len += 1; 00147 } 00148 strncat(line_buf, commands_copy->name, name_len + 1); 00149 line_len += name_len; 00150 } 00151 } 00152 #endif 00153 } 00154 00155 #ifndef CLIENTHELP_LONG_LIST 00156 if (line_len) { 00157 LINE(line_buf); 00158 } 00159 #endif 00160 } 00161 00166 static void show_help(const ConsoleCommand * cc) { 00167 { 00168 char buf[MAX_BUF]; 00169 snprintf(buf, MAX_BUF - 1, "%s Command help:", get_category_name(cc->cat)); 00170 H1(buf); 00171 } 00172 00173 if (cc->desc != NULL) { 00174 char buf[MAX_BUF]; 00175 snprintf(buf, MAX_BUF - 1, "%s - %s", cc->name, cc->desc); 00176 H2(buf); 00177 } else { 00178 H2(cc->name); 00179 } 00180 00181 if (cc->helpfunc != NULL) { 00182 const char * long_help = NULL; 00183 00184 long_help = cc->helpfunc(); 00185 00186 if (long_help != NULL) { 00187 /* For a test, let's watch draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, 00188 * MSG_TYPE_CLIENT_NOTICE, ) choke on newlines. */ 00189 /* TODO C line wrapping (get_info_width()), argh. Or move it to UI? */ 00190 LINE(long_help); 00191 } else { 00192 LINE("This command's documentation is bugged!"); 00193 } 00194 } else { 00195 LINE("This command has no extended documentation. :("); 00196 } 00197 } 00198 00203 static void do_clienthelp(const char * arg) { 00204 const ConsoleCommand * cc; 00205 00206 if (!arg || !strlen(arg)) { 00207 do_clienthelp_list(); 00208 return; 00209 } 00210 00211 cc = find_command(arg); 00212 00213 if (cc == NULL) { 00214 char buf[MAX_BUF]; 00215 snprintf(buf, MAX_BUF - 1, "clienthelp: Unknown command %s.", arg); 00216 draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, buf); 00217 return; 00218 } 00219 00220 show_help(cc); 00221 00222 } 00223 00227 static const char * help_clienthelp(void) { 00228 return 00229 "Syntax:\n" 00230 "\n" 00231 " clienthelp\n" 00232 " clienthelp <command>\n" 00233 "\n" 00234 "Without any arguments, displays a list of client-side " 00235 "commands.\n" 00236 "\n" 00237 "With arguments, displays the help for the client-side " 00238 "command <command>.\n" 00239 "\n" 00240 "See also: serverhelp, help."; 00241 } 00242 00247 static void do_serverhelp(const char * arg) { 00248 00249 if (arg) { 00250 char buf[MAX_BUF]; 00251 snprintf(buf, sizeof(buf), "help %s", arg); 00252 /* maybe not a must send, but we probably don't want to drop it */ 00253 send_command(buf, -1, 1); 00254 } else { 00255 send_command("help", -1, 1); /* TODO make install in server branch doesn't install def_help. */ 00256 } 00257 } 00258 00262 static const char * help_serverhelp(void) { 00263 return 00264 "Syntax:\n" 00265 "\n" 00266 " serverhelp\n" 00267 " serverhelp <command>\n" 00268 "\n" 00269 "Fetches help from the server.\n" 00270 "\n" 00271 "Note that currently nothing can be done (without a recompile) if a " 00272 "client command masks a server command.\n" 00273 "\n" 00274 "See also: clienthelp, help."; 00275 } 00276 00281 static void command_help(const char *cpnext) { 00282 if (cpnext) { 00283 const ConsoleCommand * cc; 00284 char buf[MAX_BUF]; 00285 00286 cc = find_command(cpnext); 00287 if (cc != NULL) { 00288 show_help(cc); 00289 } else { 00290 snprintf(buf, sizeof(buf), "help %s", cpnext); 00291 /* maybe not a must send, but we probably don't want to drop it */ 00292 send_command(buf, -1, 1); 00293 } 00294 } else { 00295 do_clienthelp_list(); 00296 /* Now fetch (in theory) command list from the server. 00297 TODO Protocol command - feed it to the tab completer. 00298 00299 Nope! It effectivey fetches '/help commands for commands'. 00300 */ 00301 send_command("help", -1, 1); /* TODO make install in server branch doesn't install def_help. */ 00302 } 00303 } 00304 00308 static const char * help_help(void) { 00309 return 00310 "Syntax:\n" 00311 "\n" 00312 " help\n" 00313 " help <topic>\n" 00314 "\n" 00315 "Without any arguments, displays a list of client-side " 00316 "commands, and fetches the without-arguments help from " 00317 "the server.\n" 00318 "\n" 00319 "With arguments, first checks if there's a client command " 00320 "named <topic>. If there is, display it's help. If there " 00321 "isn't, send the topic to the server.\n" 00322 "\n" 00323 "See also: clienthelp, serverhelp."; 00324 } 00325 /* EndOf PCmdHelpCommands 00328 */ 00329 00330 /* 00331 * Other commands. 00332 */ 00333 00338 static void set_command_window(const char *cpnext) 00339 { 00340 if (!cpnext) { 00341 draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, 00342 "cwindow command requires a number parameter"); 00343 } else { 00344 want_config[CONFIG_CWINDOW] = atoi(cpnext); 00345 if (want_config[CONFIG_CWINDOW]<1 || want_config[CONFIG_CWINDOW]>127) 00346 want_config[CONFIG_CWINDOW]=COMMAND_WINDOW; 00347 else 00348 use_config[CONFIG_CWINDOW] = want_config[CONFIG_CWINDOW]; 00349 } 00350 } 00351 00356 static void command_foodbeep(const char *cpnext) 00357 { 00358 (void)cpnext; /* __UNUSED__ */ 00359 if (want_config[CONFIG_FOODBEEP]) { 00360 want_config[CONFIG_FOODBEEP]=0; 00361 draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, 00362 "Warning bell when low on food disabled"); 00363 } else { 00364 want_config[CONFIG_FOODBEEP]=1; 00365 draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, 00366 "Warning bell when low on food enabled"); 00367 } 00368 use_config[CONFIG_FOODBEEP] = want_config[CONFIG_FOODBEEP]; 00369 } 00370 00375 const char * get_category_name(CommCat cat) { 00376 const char * cat_name; 00377 00378 /* HACK Need to keep this in sync. with player.h */ 00379 switch(cat) { 00380 case COMM_CAT_MISC: cat_name = "Miscellaneous"; break; 00381 case COMM_CAT_HELP: cat_name = "Help"; break; 00382 case COMM_CAT_INFO: cat_name = "Informational"; break; 00383 case COMM_CAT_SETUP: cat_name = "Configuration"; break; 00384 case COMM_CAT_SCRIPT: cat_name = "Scripting"; break; 00385 case COMM_CAT_DEBUG: cat_name = "Debugging"; break; 00386 default: cat_name = "PROGRAMMER ERROR"; break; 00387 } 00388 00389 return cat_name; 00390 } 00391 00392 /* 00393 * Command table. 00394 * 00395 * Implementation basically stolen verbatim from the server. 00396 */ 00397 00398 /* "Typecasters" (and some forwards) */ 00399 00404 static void do_script_list(const char * ignored) { script_list(); } 00405 00410 static void do_clearinfo(const char * ignored) { menu_clear(); } 00411 00416 static void do_disconnect(const char * ignored) { 00417 close_server_connection(); 00418 00419 /* the gtk clients need to do some cleanup logic - otherwise, 00420 * they start hogging CPU. 00421 */ 00422 cleanup_connection(); 00423 return; 00424 } 00425 00426 #ifdef HAVE_DMALLOC_H 00427 #ifndef DMALLOC_VERIFY_NOERROR 00428 #define DMALLOC_VERIFY_NOERROR 1 00429 #endif 00430 00434 static void do_dmalloc(const char * ignored) { 00435 if (dmalloc_verify(NULL)==DMALLOC_VERIFY_NOERROR) 00436 draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, 00437 "Heap checks out OK"); 00438 else 00439 draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_ERROR, 00440 "Heap corruption detected"); 00441 } 00442 #endif 00443 00448 static void do_inv(const char * ignored) { print_inventory (cpl.ob); } 00449 00450 static void do_magicmap(const char * ignored) { 00451 cpl.showmagic=1; 00452 draw_magic_map(); 00453 } 00454 00459 static void do_metaserver(const char * ignored) { 00460 if (!metaserver_get_info(meta_server, meta_port)) 00461 metaserver_show(FALSE); 00462 else 00463 draw_ext_info( 00464 NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_METASERVER, 00465 "Unable to get metaserver information."); 00466 } 00467 00472 static void do_savedefaults(const char * ignored) { save_defaults(); } 00473 00478 static void do_savewinpos(const char * ignored) { save_winpos(); } 00479 00484 static void do_take(const char * used) { command_take("take", used); /* I dunno why they want it. */ } 00485 00490 static void do_num_free_items(const char * ignored) { 00491 LOG(LOG_INFO,"common::extended_command","num_free_items=%d", num_free_items()); 00492 } 00493 00494 static void do_clienthelp(const char * arg); /* Forward. */ 00495 00496 /* Help "typecasters". */ 00497 #include "../help/chelp.h" 00498 00502 static const char * help_bind(void) { return HELP_BIND_LONG; } 00503 00507 static const char * help_unbind(void) { return HELP_UNBIND_LONG; } 00508 00512 static const char * help_magicmap(void) { return HELP_MAGICMAP_LONG; } 00513 00517 static const char * help_inv(void) { return HELP_INV_LONG; } 00518 00522 static const char * help_cwindow(void) { 00523 return 00524 "Syntax:\n" 00525 "\n" 00526 " cwindow <val>\n" 00527 "\n" 00528 "set size of command" 00529 "window (if val is exceeded" 00530 "client won't send new" 00531 "commands to server\n\n" 00532 "(What does this mean, 'put a lid on it'?) TODO"; 00533 } 00534 00538 static const char * help_script(void) { 00539 return 00540 "Syntax:\n" 00541 "\n" 00542 " script <pathname>\n" 00543 "\n" 00544 "Run the program at path <name>" 00545 "as a Crossfire client script." 00546 "See doc/Script.html"; 00547 } 00548 00552 static const char * help_scripttell(void) { 00553 return 00554 "Syntax:\n" 00555 "\n" 00556 " scripttell <yourname> <data>\n" 00557 "\n" 00558 "?"; 00559 } 00560 00561 /* Toolkit-dependent. */ 00562 00566 static const char * help_savewinpos(void) { 00567 return 00568 "Syntax:\n" 00569 "\n" 00570 " savewinpos\n" 00571 "\n" 00572 "save window positions - split windows mode only."; 00573 } 00574 00578 static const char * help_metaserver(void) { 00579 /* TODO Add command_escape() where appropriate. On the other 00580 hand, that can lead to a meaningless syntax-display API.*/ 00581 00582 return 00583 "Syntax:\n" 00584 "\n" 00585 " metaserver\n" 00586 "\n" 00587 "Get updated list of servers " 00588 "from the metaserver and show it." 00589 "This is the same information that the client " 00590 "uses to show a list of servers when it starts.\n" 00591 "\n" 00592 "Warning: This command may freeze the client until it gets the list."; 00593 } 00594 00598 static const char * help_scriptkill(void) { 00599 return 00600 "Syntax:\n" 00601 "\n" 00602 " scriptkill <name>\n" 00603 "\n" 00604 "Stop scripts named <name>.\n" 00605 "(Not guaranteed to work?)"; 00606 } 00607 00611 static const char * help_showweight(void) { 00612 return 00613 "Syntax:\n" 00614 "\n" 00615 " showweight\n" 00616 " showweight inventory\n" 00617 " showweight look\n" 00618 "\n" 00619 "(Or any prefix of the arguments.)" 00620 "Toggles if you see the weight of" 00621 "items in your inventory (also if" 00622 "no argument given) or your" 00623 "look-window."; 00624 } 00625 00626 /* 00627 * draw_ext_info(NDI_NAVY, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, 00628 "Information Commands");* 00629 draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, 00630 " inv - *recursively* print your"); 00631 draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, 00632 " inventory - includes containers."); 00633 draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, 00634 " mapredraw, showinfo, take"); 00635 draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, 00636 " help - show this message"); 00637 draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, 00638 " help <command> - get more information on a"); 00639 draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, 00640 " command (Server command only?)"); 00641 draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, 00642 " showicon - draw status icons in"); 00643 draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, 00644 " inventory window"); 00645 draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, 00646 " showweight - show weight in inventory"); 00647 draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, 00648 " look windows"); 00649 draw_ext_info(NDI_NAVY, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, 00650 "Scripting Commands"); 00651 draw_ext_info(NDI_NAVY, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, 00652 "Client Side Debugging Commands"); 00653 #ifdef HAVE_DMALLOC_H 00654 draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, 00655 " dmalloc - Check heap?"); 00656 #endif 00657 */ 00658 00659 /* TODO Wrap these? Um. */ 00660 static ConsoleCommand CommonCommands[] = { 00661 /* From player.h: 00662 name, cat, 00663 func, helpfunc, 00664 long_desc 00665 */ 00666 00667 { 00668 "autorepeat", COMM_CAT_MISC, 00669 set_autorepeat, NULL, 00670 "toggle autorepeat" /* XXX Eh? */ 00671 }, 00672 00673 { 00674 "bind", COMM_CAT_SETUP, 00675 bind_key, help_bind, 00676 HELP_BIND_SHORT 00677 }, 00678 00679 { 00680 "script", COMM_CAT_SCRIPT, 00681 script_init, help_script, 00682 NULL 00683 }, 00684 #ifdef HAVE_LUA 00685 { "lua_load", COMM_CAT_SCRIPT, 00686 script_lua_load, NULL, NULL 00687 }, 00688 00689 { "lua_list", COMM_CAT_SCRIPT, 00690 script_lua_list, NULL, NULL 00691 }, 00692 00693 { "lua_kill", COMM_CAT_SCRIPT, 00694 script_lua_kill, NULL, NULL 00695 }, 00696 #endif 00697 { 00698 "scripts", COMM_CAT_SCRIPT, 00699 do_script_list, NULL, 00700 "List the running scripts(?)" 00701 }, 00702 00703 { 00704 "scriptkill", COMM_CAT_SCRIPT, 00705 script_kill, help_scriptkill, 00706 NULL 00707 }, 00708 00709 { 00710 "scripttell", COMM_CAT_SCRIPT, 00711 script_tell, help_scripttell, 00712 NULL 00713 }, 00714 00715 { 00716 "clearinfo", COMM_CAT_MISC, 00717 do_clearinfo, NULL, 00718 "clear the info window" 00719 }, 00720 00721 { 00722 "cwindow", COMM_CAT_SETUP, 00723 set_command_window, help_cwindow, 00724 NULL 00725 }, 00726 00727 { 00728 "disconnect", COMM_CAT_MISC, 00729 do_disconnect, NULL, 00730 "close connection to server" 00731 }, 00732 00733 00734 #ifdef HAVE_DMALLOC_H 00735 { 00736 "dmalloc", COMM_CAT_DEBUG, 00737 do_dmalloc, NULL, 00738 NULL 00739 }, 00740 #endif 00741 00742 { 00743 "foodbeep", COMM_CAT_SETUP, 00744 command_foodbeep, NULL, 00745 "toggle audible low on food warning" 00746 00747 }, 00748 00749 { 00750 "help", COMM_CAT_HELP, 00751 command_help, help_help, 00752 NULL 00753 }, 00754 00755 { 00756 "clienthelp", COMM_CAT_HELP, 00757 do_clienthelp, help_clienthelp, 00758 "Client-side command information" 00759 }, 00760 00761 { 00762 "serverhelp", COMM_CAT_HELP, 00763 do_serverhelp, help_serverhelp, 00764 "Server-side command information" 00765 }, 00766 00767 { 00768 "inv", COMM_CAT_DEBUG, 00769 do_inv, help_inv, 00770 HELP_INV_SHORT 00771 }, 00772 00773 { 00774 "magicmap", COMM_CAT_MISC, 00775 do_magicmap, help_magicmap, 00776 HELP_MAGICMAP_SHORT 00777 }, 00778 00779 { 00780 "metaserver", COMM_CAT_INFO, 00781 do_metaserver, help_metaserver, 00782 "Print 'metaserver information'. Warning - your client will pause." 00783 }, 00784 00785 { 00786 "savedefaults", COMM_CAT_SETUP, 00787 do_savedefaults, NULL, 00788 HELP_SAVEDEFAULTS_SHORT /* How do we make sure showicons stays on? */ 00789 }, 00790 00791 { 00792 "savewinpos", COMM_CAT_SETUP, 00793 do_savewinpos, help_savewinpos, 00794 "Saves the position and sizes of windows." /* Panes? */ 00795 }, 00796 00797 { 00798 "scroll", COMM_CAT_SETUP, 00799 set_scroll, NULL, 00800 "toggle scroll/wrap mode in info window" 00801 }, 00802 00803 { 00804 "showicon", COMM_CAT_SETUP, 00805 set_show_icon, NULL, 00806 "Toggles if you see the worn, locked, cursed etc state in the inventory pane." 00807 }, 00808 00809 { 00810 "showweight", COMM_CAT_SETUP, 00811 set_show_weight, help_showweight, 00812 "Toggles if you see item weights in inventory look windows." 00813 }, 00814 00815 { 00816 "take", COMM_CAT_MISC, 00817 do_take, NULL, 00818 NULL 00819 }, 00820 00821 { 00822 "unbind", COMM_CAT_SETUP, 00823 unbind_key, help_unbind, 00824 NULL 00825 }, 00826 00827 { 00828 "num_free_items", COMM_CAT_DEBUG, 00829 do_num_free_items, NULL, 00830 "log the number of free items?" 00831 }, 00832 { 00833 "show", COMM_CAT_SETUP, 00834 command_show, NULL, 00835 "Change what items to show in inventory" 00836 }, 00837 00838 }; 00839 00840 const int CommonCommandsSize = sizeof(CommonCommands) / sizeof(ConsoleCommand); 00841 00842 #ifdef TOOLKIT_COMMANDS 00843 extern ConsoleCommand ToolkitCommands[]; 00844 00845 extern const int ToolkitCommandsSize; 00846 #endif 00847 00848 /* ------------------------------------------------------------------ */ 00849 00850 int num_commands; 00851 00855 int get_num_commands(void) { return num_commands; } 00856 00857 static ConsoleCommand ** name_sorted_commands; 00858 00864 static int sort_by_name(const void * a_, const void * b_) 00865 { 00866 ConsoleCommand * a = *((ConsoleCommand **)a_); 00867 ConsoleCommand * b = *((ConsoleCommand **)b_); 00868 00869 return strcmp(a->name, b->name); 00870 } 00871 00872 static ConsoleCommand ** cat_sorted_commands; 00873 00874 /* Sort by category, then by name. */ 00875 00881 static int sort_by_category(const void *a_, const void *b_) 00882 { 00883 /* Typecasts, so it goes. */ 00884 ConsoleCommand * a = *((ConsoleCommand **)a_); 00885 ConsoleCommand * b = *((ConsoleCommand **)b_); 00886 00887 if (a->cat == b->cat) { 00888 return strcmp(a->name, b->name); 00889 } 00890 00891 return a->cat - b->cat; 00892 } 00893 00897 void init_commands(void) { 00898 int i; 00899 00900 #ifdef TOOLKIT_COMMANDS 00901 init_toolkit_commands(); 00902 00903 /* TODO I dunno ... go through the list and print commands without helps? */ 00904 num_commands = CommonCommandsSize + ToolkitCommandsSize; 00905 #else 00906 num_commands = CommonCommandsSize; 00907 #endif 00908 00909 /* Make a list of (pointers to statically allocated!) all the commands. 00910 We have a list; the toolkit has a 00911 ToolkitCommands and ToolkitCommandsSize, initialized before calling this. 00912 */ 00913 00914 /* XXX Leak! */ 00915 name_sorted_commands = malloc(sizeof(ConsoleCommand *) * num_commands); 00916 00917 for (i = 0; i < CommonCommandsSize; i++) { 00918 name_sorted_commands[i] = &CommonCommands[i]; 00919 } 00920 00921 #ifdef TOOLKIT_COMMANDS 00922 for(i = 0; i < ToolkitCommandsSize; i++) { 00923 name_sorted_commands[CommonCommandsSize + i] = &ToolkitCommands[i]; 00924 } 00925 #endif 00926 00927 /* Sort them. */ 00928 qsort(name_sorted_commands, num_commands, sizeof(ConsoleCommand *), sort_by_name); 00929 00930 /* Copy the list, then sort it by category. */ 00931 cat_sorted_commands = malloc(sizeof(ConsoleCommand *) * num_commands); 00932 00933 memcpy(cat_sorted_commands, name_sorted_commands, sizeof(ConsoleCommand *) * num_commands); 00934 00935 qsort(cat_sorted_commands, num_commands, sizeof(ConsoleCommand *), sort_by_category); 00936 00937 /* TODO Add to the list of tab-completion items. */ 00938 } 00939 00940 #ifndef tolower 00941 #define tolower(C) (((C) >= 'A' && (C) <= 'Z')? (C) - 'A' + 'a': (C)) 00942 #endif 00943 00948 const ConsoleCommand * find_command(const char * cmd) { 00949 ConsoleCommand ** asp_p = NULL, dummy; 00950 ConsoleCommand * dummy_p; 00951 ConsoleCommand * asp; 00952 char *cp, *cmd_cpy; 00953 cmd_cpy = strdup(cmd); 00954 00955 for (cp=cmd_cpy; *cp; cp++) { 00956 *cp =tolower(*cp); 00957 } 00958 00959 dummy.name = cmd_cpy; 00960 dummy_p = &dummy; 00961 asp_p = bsearch( 00962 (void *)&dummy_p, 00963 (void *)name_sorted_commands, 00964 num_commands, 00965 sizeof(ConsoleCommand *), 00966 sort_by_name); 00967 00968 if (asp_p == NULL) 00969 { 00970 free(cmd_cpy); 00971 return NULL; 00972 } 00973 00974 asp = *asp_p; 00975 00976 /* TODO The server's find_command() searches first the commands, 00977 then the emotes. We might have to do something similar someday, too. */ 00978 /* if (asp == NULL) search something else? */ 00979 00980 free(cmd_cpy); 00981 00982 return asp; 00983 } 00984 00989 ConsoleCommand ** get_cat_sorted_commands(void) { 00990 return cat_sorted_commands; 00991 } 00992 01002 int handle_local_command(const char* cp, const char * cpnext) { 01003 const ConsoleCommand * cc = NULL; 01004 01005 cc = find_command(cp); 01006 01007 if (cc == NULL) { 01008 return FALSE; 01009 } 01010 01011 if (cc->dofunc == NULL) { 01012 char buf[MAX_BUF]; 01013 01014 snprintf(buf, MAX_BUF - 1, "Client command %s has no implementation!", cc->name); 01015 draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, buf); 01016 01017 return FALSE; 01018 } 01019 01020 cc->dofunc(cpnext); 01021 01022 return TRUE; 01023 } 01024 01036 void extended_command(const char *ocommand) { 01037 const char *cp = ocommand; 01038 char *cpnext, command[MAX_BUF]; 01039 01040 if ((cpnext = strchr(cp, ' '))!=NULL) { 01041 int len = cpnext - ocommand; 01042 if (len > (MAX_BUF -1 )) len = MAX_BUF-1; 01043 01044 strncpy(command, ocommand, len); 01045 command[len] = '\0'; 01046 cp = command; 01047 while (*cpnext == ' ') 01048 cpnext++; 01049 if (*cpnext == 0) 01050 cpnext = NULL; 01051 } 01052 01053 /* cp now contains the command (everything before first space), 01054 * and cpnext contains everything after that first space. cpnext 01055 * could be NULL. 01056 */ 01057 #ifdef HAVE_LUA 01058 if ( script_lua_command(cp, cpnext) ) 01059 return; 01060 #endif 01061 01062 /* If this isn't a client-side command, send it to the server. */ 01063 if (!handle_local_command(cp, cpnext)) { 01064 /* just send the command(s) (if `ocommand' is a compound command */ 01065 /* then split it and send each part seperately */ 01066 /* TODO Remove this from the server; end of commands.c. */ 01067 strncpy(command, ocommand, MAX_BUF-1); 01068 command[MAX_BUF-1]=0; 01069 cp = strtok(command, ";"); 01070 while ( cp ) { 01071 while( *cp == ' ' ) cp++; /* throw out leading spaces; server 01072 does not like them */ 01073 send_command(cp, cpl.count, 0); 01074 cp = strtok(NULL, ";"); 01075 } 01076 } 01077 } 01078 01079 /* ------------------------------------------------------------------ */ 01080 01081 /* This list is used for the 'tab' completion, and nothing else. 01082 * Therefore, if it is out of date, it isn't that terrible, but 01083 * ideally it should stay somewhat up to date with regards to 01084 * the commands the server supports. 01085 */ 01086 01087 /* TODO Dynamically generate. */ 01088 01089 static const char *const commands[] = { 01090 "accuse", "afk", "apply", "applymode", "archs", "beg", "bleed", "blush", 01091 "body", "bounce", "bow", "bowmode", "brace", "build", "burp", "cackle", "cast", 01092 "chat", "chuckle", "clap", "cointoss", "cough", "cringe", "cry", "dance", 01093 "disarm", "dm", "dmhide", "drop", "dropall", "east", "examine", "explore", 01094 "fire", "fire_stop", "fix_me", "flip", "frown", "gasp", "get", "giggle", 01095 "glare", "grin", "groan", "growl", "gsay", "help", "hiccup", "hiscore", "hug", 01096 "inventory", "invoke", "killpets", "kiss", "laugh", "lick", "listen", "logs", 01097 "mapinfo", "maps", "mark", "me", "motd", "nod", "north", "northeast", 01098 "northwest", "orcknuckle", "output-count", "output-sync", "party", "peaceful", 01099 "petmode", "pickup", "players", "poke", "pout", "prepare", "printlos", "puke", 01100 "quests", "quit", "ready_skill", "rename", "reply", "resistances", 01101 "rotateshoottype", "run", "run_stop", "save", "say", "scream", "search", 01102 "search-items", "shake", "shiver", "shout", "showpets", "shrug", "shutdown", 01103 "sigh", "skills", "slap", "smile", "smirk", "snap", "sneeze", "snicker", 01104 "sniff", "snore", "sound", "south", "southeast", "southwest", "spit", 01105 "statistics", "stay", "strings", "strut", "sulk", "take", "tell", "thank", 01106 "think", "throw", "time", "title", "twiddle", "use_skill", "usekeys", 01107 "version", "wave", "weather", "west", "whereabouts", "whereami", "whistle", 01108 "who", "wimpy", "wink", "yawn", 01109 }; 01110 #define NUM_COMMANDS ((int)(sizeof(commands) / sizeof(char*))) 01111 01119 const char * complete_command(const char *command) 01120 { 01121 int i, len, display; 01122 const char *match; 01123 static char result[64]; 01124 char list[500]; 01125 01126 len = strlen(command); 01127 01128 if (len == 0) 01129 return NULL; 01130 01131 display = 0; 01132 strcpy(list, "Matching commands:"); 01133 01134 /* TODO Partial match, e.g.: 01135 If the completion list was: 01136 wear 01137 wet #? 01138 01139 If we type 'w' then hit tab, put in the e. 01140 01141 Basically part of bash (readline?)'s behaviour. 01142 */ 01143 01144 match = NULL; 01145 01146 /* check server side commands */ 01147 for (i=0; i<NUM_COMMANDS; i++) { 01148 if (!strncmp(command, commands[i], len)) { 01149 if (display) { 01150 snprintf(list + strlen(list), 499 - strlen(list), " %s", commands[i]); 01151 } else if (match != NULL) { 01152 display = 1; 01153 snprintf(list + strlen(list), 499 - strlen(list), " %s %s", match, commands[i]); 01154 match = NULL; 01155 } else 01156 match = commands[i]; 01157 } 01158 } 01159 01160 /* check client side commands */ 01161 for (i=0; i<CommonCommandsSize; i++) { 01162 if (!strncmp(command, CommonCommands[i].name, len)) { 01163 if (display) { 01164 snprintf(list + strlen(list), 499 - strlen(list), " %s", CommonCommands[i].name); 01165 } else if (match != NULL) { 01166 display = 1; 01167 snprintf(list + strlen(list), 499 - strlen(list), " %s %s", match, CommonCommands[i].name); 01168 match = NULL; 01169 } else 01170 match = CommonCommands[i].name; 01171 } 01172 } 01173 01174 if (match == NULL) { 01175 if (display) { 01176 strncat(list, "\n", 499 - strlen(list)); 01177 draw_ext_info( 01178 NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, list); 01179 } 01180 else 01181 draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, 01182 "No matching command.\n"); 01183 /* No match. */ 01184 return NULL; 01185 } 01186 01187 /* 01188 * Append a space to allow typing arguments. For commands without arguments 01189 * the excess space should be stripped off automatically. 01190 */ 01191 snprintf(result, sizeof(result), "%s ", match); 01192 01193 return result; 01194 } 01195 01196 #endif /* CPROTO */
1.7.6.1