Crossfire Client, Trunk  R18666
/home/leaf/crossfire/client/trunk/common/script.c
Go to the documentation of this file.
00001 const char * const rcsid_common_script_c =
00002     "$Id: script.c 17543 2012-03-16 21:24:20Z ryo_saeba $";
00003 /*
00004     Crossfire client, a client program for the crossfire program.
00005 
00006     Copyright (C) 2003 Mark Wedel & Crossfire Development Team
00007     This source file also Copyright (C) 2003 Preston Crow
00008 
00009     This program is free software; you can redistribute it and/or modify
00010     it under the terms of the GNU General Public License as published by
00011     the Free Software Foundation; either version 2 of the License, or
00012     (at your option) any later version.
00013 
00014     This program is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017     GNU General Public License for more details.
00018 
00019     You should have received a copy of the GNU General Public License
00020     along with this program; if not, write to the Free Software
00021     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00022 
00023     The author can be reached via e-mail to crossfire-devel@real-time.com
00024 */
00025 
00026 /*
00027  * This file has its own script.h for prototypes, so don't want to include
00028  * this when doing a 'make proto'.
00029  */
00030 #ifndef CPROTO
00031 
00109 /*
00110  * Include files
00111  */
00112 
00113 /*
00114  * This does not work under Windows for now.  Someday this will be fixed :)
00115  */
00116 
00117 #ifndef WIN32
00118 #include <errno.h>
00119 #include <sys/types.h>
00120 #include <sys/socket.h>
00121 #include <sys/wait.h>
00122 /* for SIGHUP */
00123 #include <signal.h>
00124 #endif
00125 #include <ctype.h>
00126 
00127 #include <client.h>
00128 #include <external.h>
00129 #include <script.h>
00130 #include <p_cmd.h>
00131 #include "mapdata.h"
00132 
00133 /*
00134  * Data structures
00135  */
00136 struct script {
00137    char *name; /* the script name */
00138    char *params; /* the script parameters, if any */
00139 #ifndef WIN32
00140    int out_fd; /* the file descriptor to which the client writes to the script */
00141    int in_fd; /* the file descriptor from which we read commands from the script */
00142 #else
00143    HANDLE out_fd; /* the file descriptor to which the client writes to the script */
00144    HANDLE in_fd; /* the file descriptor from which we read commands from the script */
00145 #endif /* WIN32 */
00146    int monitor; /* true if this script is monitoring commands sent to the server */
00147    int num_watch; /* number of commands we're watching */
00148    char **watch; /* array of commands that we're watching */
00149    int cmd_count; /* bytes already read in */
00150    char cmd[1024]; /* command from the script */
00151 #ifndef WIN32
00152    int pid;
00153 #else
00154    DWORD pid;   /* Handle to Win32 process ID */
00155    HANDLE process; /* Handle of Win32 process */
00156 #endif
00157    int sync_watch;
00158 };
00159 
00160 /*
00161  * Global variables
00162  */
00163 static struct script *scripts = NULL;
00164 
00165 static int num_scripts = 0;
00166 
00167 /*
00168  * Prototypes
00169  */
00170 static int script_by_name(const char *name);
00171 
00172 static void script_dead(int i);
00173 
00174 static void script_process_cmd(int i);
00175 
00176 static void send_map(int i, int x, int y);
00177 
00178 static void script_send_item(int i, const char *head, const item *it);
00179 
00180 
00181 /*
00182  * Functions
00183  */
00184 
00185 #ifdef WIN32
00186 
00187 #define write(x, y, z) emulate_write(x, y, z)
00188 #define read(x, y, z) emulate_read(x, y, z)
00189 
00190 static int emulate_read(HANDLE fd, char *buf, int len) {
00191     DWORD dwBytesRead;
00192     BOOL rc;
00193 
00194     FlushFileBuffers(fd);
00195     rc = ReadFile(fd, buf, len, &dwBytesRead, NULL);
00196     if (rc == FALSE)
00197         return(-1);
00198     buf[dwBytesRead] = '\0';
00199 
00200     return(dwBytesRead);
00201 }
00202 
00203 static int emulate_write(HANDLE fd, const char *buf, int len) {
00204     DWORD dwBytesWritten;
00205     BOOL rc;
00206 
00207     rc = WriteFile(fd, buf, len, &dwBytesWritten, NULL);
00208     FlushFileBuffers(fd);
00209     if (rc == FALSE)
00210         return(-1);
00211 
00212     return(dwBytesWritten);
00213 }
00214 
00215 #endif /* WIN32 */
00216 
00217 void script_init(const char *cparams) {
00218 #ifndef WIN32
00219     int pipe1[2];
00220 #ifdef USE_PIPE
00221     int pipe2[2];
00222 #endif
00223     int pid;
00224     char *name, *args, params[MAX_BUF];
00225 
00226     if (!cparams) {
00227         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00228             "Please specify a script to launch!");
00229         return;
00230     }
00231 
00232     /* cparams as passed in is a const value, so need to copy it
00233      * to data we can write over.
00234      */
00235     strncpy(params, cparams, MAX_BUF-1);
00236     params[MAX_BUF-1] = 0;
00237 
00238 
00239     /* Get name and args */
00240     name = params;
00241     args = name;
00242     while (*args && *args != ' ')
00243         ++args;
00244     while (*args && *args == ' ')
00245         *args++ = '\0';
00246     if (*args == 0) {
00247         args = NULL;
00248     }
00249 
00250 #ifdef USE_PIPE
00251     /* Create two pipes */
00252     if (pipe(pipe1)) {
00253         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00254             "Unable to start script--pipe failed");
00255         return;
00256     }
00257     if (pipe(pipe2)) {
00258         close(pipe1[0]);
00259         close(pipe1[1]);
00260         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00261             "Unable to start script--pipe failed");
00262         return;
00263     }
00264 #else
00265     /* Create a pair of sockets */
00266     if (socketpair(PF_LOCAL, SOCK_STREAM, AF_LOCAL, pipe1)) {
00267         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00268             "Unable to start script--socketpair failed");
00269         return;
00270     }
00271 #endif
00272 
00273     /* Fork */
00274     pid = fork();
00275     if (pid == -1) {
00276         close(pipe1[0]);
00277         close(pipe1[1]);
00278 #ifdef USE_PIPE
00279         close(pipe2[0]);
00280         close(pipe2[1]);
00281 #endif
00282         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00283             "Unable to start script--fork failed");
00284         return;
00285     }
00286 
00287     /* Child--set stdin/stdout to the pipes, then exec */
00288     if (pid == 0) {
00289         int i;
00290         int r;
00291         char *argv[256];
00292 
00293         /* Fill in argv[] */
00294         argv[0] = name;
00295         i = 1;
00296         while (args && *args && i < sizeof(argv)/sizeof(*argv)-1) {
00297             argv[i++] = args;
00298             while (*args && *args != ' ')
00299                 ++args;
00300             while (*args && *args == ' ')
00301                 *args++ = '\0';
00302         }
00303         argv[i] = NULL;
00304 
00305         /* Clean up file descriptor space */
00306         r = dup2(pipe1[0], 0);
00307         if (r != 0) {
00308             fprintf(stderr, "Script Child: Failed to set pipe1 as stdin\n");
00309         }
00310 #ifdef USE_PIPE
00311         r = dup2(pipe2[1], 1);
00312 #else
00313         r = dup2(pipe1[0], 1);
00314 #endif
00315         if (r != 1) {
00316             fprintf(stderr, "Script Child: Failed to set pipe2 as stdout\n");
00317         }
00318         for (i = 3; i < 100; ++i)
00319             close(i);
00320 
00321         /* EXEC */
00322         r = execvp(argv[0], argv);
00323 
00324         /* If we get here, then there's been an failure of some sort.
00325          * In my case, it's often that I don't know what script name to
00326          * give to /script, so exec() can't find the script.
00327          *
00328          * Forward the error back to the client, using the script pipes.
00329          */
00330 
00331         if (r != -1) {
00332             printf("draw %d Script child: no error, but no execvp().\n", NDI_RED);
00333         } else {
00334             printf("draw %d Script child failed to start: %s\n", NDI_RED, strerror(errno));
00335         }
00336 
00337         exit(1);
00338     }
00339 
00340     /* Close the child's pipe ends */
00341     close(pipe1[0]);
00342 #ifdef USE_PIPE
00343     close(pipe2[1]);
00344 #endif
00345 
00346     if (fcntl(pipe1[1], F_SETFL, O_NDELAY) == -1) {
00347         LOG(LOG_WARNING, "common::script_init", "Error on fcntl.");
00348     }
00349 
00350     /* realloc script array to add new entry; fill in the data */
00351     scripts = realloc(scripts, sizeof(scripts[0])*(num_scripts+1));
00352     scripts[num_scripts].name = strdup(name);
00353     scripts[num_scripts].params = args ? strdup(args) : NULL;
00354     scripts[num_scripts].out_fd = pipe1[1];
00355 #ifdef USE_PIPE
00356     scripts[num_scripts].in_fd = pipe2[0];
00357 #else
00358     scripts[num_scripts].in_fd = pipe1[1];
00359 #endif
00360     scripts[num_scripts].monitor = 0;
00361     scripts[num_scripts].num_watch = 0;
00362     scripts[num_scripts].watch = NULL;
00363     scripts[num_scripts].cmd_count = 0;
00364     scripts[num_scripts].pid = pid;
00365     scripts[num_scripts].sync_watch = -1;
00366     ++num_scripts;
00367 #else /* WIN32 */
00368 
00369     char *name, *args;
00370     char params[ MAX_BUF ];
00371     SECURITY_ATTRIBUTES saAttr;
00372     PROCESS_INFORMATION piProcInfo;
00373     STARTUPINFO siStartupInfo;
00374     HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup, hChildStdoutRd;
00375     HANDLE hChildStdoutWr, hChildStdoutRdDup, hSaveStdin, hSaveStdout;
00376 
00377     if (!cparams) {
00378         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00379             "Please specifiy a script to launch!");
00380         return;
00381     }
00382 
00383     strncpy(params, cparams, MAX_BUF-1);
00384     params[MAX_BUF-1] = '\0';
00385 
00386     /* Get name and args */
00387     name = params;
00388     args = name;
00389     while (*args && *args != ' ')
00390         ++args;
00391     while (*args && *args == ' ')
00392         *args++ = '\0';
00393     if (*args == 0) {
00394         args = NULL;
00395     }
00396 
00397     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
00398     saAttr.bInheritHandle = TRUE;
00399     saAttr.lpSecurityDescriptor = NULL;
00400 
00401     hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);
00402     if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
00403         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00404             "Script support: stdout CreatePipe() failed");
00405         return;
00406     }
00407 
00408     if (!SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr)) {
00409         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00410             "Script support: failed to redirect stdout using SetStdHandle()");
00411         return;
00412     }
00413 
00414     if (!DuplicateHandle(GetCurrentProcess(), hChildStdoutRd, GetCurrentProcess(), &hChildStdoutRdDup, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
00415         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00416             "Script support: failed to duplicate stdout using DuplicateHandle()");
00417         return;
00418     }
00419 
00420     CloseHandle(hChildStdoutRd);
00421 
00422     hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);
00423     if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
00424         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00425             "Script support: stdin CreatePipe() failed");
00426         return;
00427     }
00428 
00429     if (!SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd)) {
00430         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00431             "Script support: failed to redirect stdin using SetStdHandle()");
00432         return;
00433     }
00434 
00435     if (!DuplicateHandle(GetCurrentProcess(), hChildStdinWr, GetCurrentProcess(), &hChildStdinWrDup, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
00436         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00437             "Script support: failed to duplicate stdin using DuplicateHandle()");
00438         return;
00439     }
00440 
00441     CloseHandle(hChildStdinWr);
00442 
00443     ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
00444     ZeroMemory(&siStartupInfo, sizeof(STARTUPINFO));
00445     siStartupInfo.cb = sizeof(STARTUPINFO);
00446 
00447     if (args)
00448         args[-1] = ' ';
00449 
00450     if (!CreateProcess(NULL, name, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &siStartupInfo, &piProcInfo)) {
00451         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00452             "Script support: CreateProcess() failed");
00453         return;
00454     }
00455 
00456     CloseHandle(piProcInfo.hThread);
00457 
00458     if (args)
00459         args[-1] = '\0';
00460 
00461     if (!SetStdHandle(STD_INPUT_HANDLE, hSaveStdin)) {
00462         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00463             "Script support: restoring original stdin failed");
00464         return;
00465     }
00466 
00467     if (!SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout)) {
00468         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00469             "Script support: restoring original stdout failed");
00470         return;
00471     }
00472 
00473     /* realloc script array to add new entry; fill in the data */
00474     scripts = realloc(scripts, sizeof(scripts[0])*(num_scripts+1));
00475     scripts[num_scripts].name = strdup(name);
00476     scripts[num_scripts].params = args ? strdup(args) : NULL;
00477     scripts[num_scripts].out_fd = hChildStdinWrDup;
00478     scripts[num_scripts].in_fd = hChildStdoutRdDup;
00479     scripts[num_scripts].monitor = 0;
00480     scripts[num_scripts].num_watch = 0;
00481     scripts[num_scripts].watch = NULL;
00482     scripts[num_scripts].cmd_count = 0;
00483     scripts[num_scripts].pid = piProcInfo.dwProcessId;
00484     scripts[num_scripts].process = piProcInfo.hProcess;
00485     scripts[num_scripts].sync_watch = -1;
00486     ++num_scripts;
00487 #endif /* WIN32 */
00488 }
00489 
00490 void script_sync(int commdiff) {
00491     int i;
00492 
00493     if (commdiff < 0)
00494         commdiff +=256;
00495     for (i = 0; i < num_scripts; ++i) {
00496         if (commdiff <= scripts[i].sync_watch && scripts[i].sync_watch >= 0) {
00497             char buf[1024];
00498 
00499             snprintf(buf, sizeof(buf), "sync %d\n", commdiff);
00500             write(scripts[i].out_fd, buf, strlen(buf));
00501             scripts[i].sync_watch = -1;
00502         }
00503     }
00504 }
00505 
00506 void script_list(void) {
00507     if (num_scripts == 0) {
00508         draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00509             "No scripts are currently running");
00510     } else {
00511         int i;
00512         char buf[1024];
00513 
00514         snprintf(buf, sizeof(buf), "%d scripts currently running:", num_scripts);
00515         draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, buf);
00516         for (i = 0; i < num_scripts; ++i) {
00517             if (scripts[i].params)
00518                 snprintf(buf, sizeof(buf), "%d %s  %s", i+1, scripts[i].name, scripts[i].params);
00519             else
00520                 snprintf(buf, sizeof(buf), "%d %s", i+1, scripts[i].name);
00521             draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, buf);
00522         }
00523     }
00524 }
00525 
00526 void script_kill(const char *params) {
00527     int i;
00528 
00529     /* Verify that the number is a valid array entry */
00530     i = script_by_name(params);
00531     if (i < 0 || i >= num_scripts) {
00532         draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00533             "No such running script");
00534         return;
00535     }
00536 #ifndef WIN32
00537     kill(scripts[i].pid, SIGHUP);
00538 #else
00539     GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, scripts[i].pid);
00540 #endif /* WIN32 */
00541     draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00542         "Killed script.");
00543     script_dead(i);
00544 }
00545 
00546 #ifdef WIN32
00547 void script_killall(void) {
00548     while (num_scripts > 0) {
00549         GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, scripts[0].pid);
00550         script_dead(0);
00551     }
00552 }
00553 #endif /* WIN32 */
00554 
00555 void script_fdset(int *maxfd, fd_set *set) {
00556 #ifndef WIN32
00557     int i;
00558 
00559     for (i = 0; i < num_scripts; ++i) {
00560         FD_SET(scripts[i].in_fd, set);
00561         if (scripts[i].in_fd >= *maxfd)
00562             *maxfd = scripts[i].in_fd+1;
00563     }
00564 #endif /* WIN32 */
00565 }
00566 
00567 void script_process(fd_set *set) {
00568     int i;
00569     int r;
00570 #ifdef WIN32
00571     DWORD nAvailBytes = 0;
00572     char cTmp;
00573     BOOL bRC;
00574     DWORD dwStatus;
00575     BOOL bStatus;
00576 #endif
00577 
00578     /* Determine which script's fd is set */
00579     for (i = 0; i < num_scripts; ++i) {
00580 #ifndef WIN32
00581         if (FD_ISSET(scripts[i].in_fd, set))
00582 #else
00583         bStatus = GetExitCodeProcess(scripts[i].process, &dwStatus);
00584         bRC = PeekNamedPipe(scripts[i].in_fd, &cTmp, 1, NULL, &nAvailBytes, NULL);
00585         if (nAvailBytes)
00586 #endif /* WIN32 */
00587         {
00588             /* Read in script[i].cmd */
00589             r = read(scripts[i].in_fd, scripts[i].cmd+scripts[i].cmd_count, sizeof(scripts[i].cmd)-scripts[i].cmd_count-1);
00590             if (r > 0) {
00591                 scripts[i].cmd_count += r;
00592             }
00593 #ifndef WIN32
00594             else if (r == 0 || errno == EBADF)
00595 #else
00596             else if (r == 0 || GetLastError() == ERROR_BROKEN_PIPE)
00597 #endif
00598             {
00599                 /* Script has exited; delete it */
00600                 script_dead(i);
00601                 return;
00602             }
00603             /* If a newline or full buffer has been reached, process it */
00604             scripts[i].cmd[scripts[i].cmd_count] = 0; /* terminate string */
00605             while (scripts[i].cmd_count == sizeof(scripts[i].cmd)-1
00606 #ifndef WIN32
00607             || strchr(scripts[i].cmd, '\n'))
00608 #else
00609             || strchr(scripts[i].cmd, '\r\n'))
00610 #endif /* WIN32 */
00611         {
00612             script_process_cmd(i);
00613             scripts[i].cmd[scripts[i].cmd_count] = 0; /* terminate string */
00614         }
00615         return; /* Only process one script at a time */
00616     }
00617 #ifdef WIN32
00618     else if (!bRC || (bStatus && (dwStatus != STILL_ACTIVE))) /* Error: assume dead */
00619         script_dead(i);
00620 #endif /* WIN32 */
00621     }
00622 }
00623 
00624 void script_watch(const char *cmd, const uint8 *data_initial, const int data_len, const enum CmdFormat format) {
00625     int i;
00626     int w;
00627     int l, len;
00628     const uint8 *data;
00629 
00630     /* For each script... */
00631     for (i = 0; i < num_scripts; ++i) {
00632         /* For each watch... */
00633         for (w = 0; w < scripts[i].num_watch; ++w)
00634         {
00635             len = data_len;
00636             /* Does this command match our watch? */
00637             l = strlen(scripts[i].watch[w]);
00638             if (!l || strncmp(cmd, scripts[i].watch[w], l) == 0)
00639             {
00640                 char buf[10240];
00641 
00642                 data = data_initial;
00643                 if (!len)
00644                     snprintf(buf, sizeof(buf), "watch %s\n", cmd);
00645                 else
00646                     switch (format) {
00647                     case ASCII:
00648                         snprintf(buf, sizeof(buf), "watch %s %s\n", cmd, data);
00649                         break;
00650 
00651                     case SHORT_INT:
00652                         snprintf(buf, sizeof(buf), "watch %s %d %d\n", cmd, GetShort_String(data), GetInt_String(data+2));
00653                         break;
00654 
00655                     case SHORT_ARRAY:
00656                         {
00657                             int be;
00658                             int p;
00659 
00660                             be = snprintf(buf, sizeof(buf), "watch %s", cmd);
00661                             for (p = 0; p*2 < len && p < 100; ++p) {
00662                                 be += snprintf(buf+be, sizeof(buf)-be, " %d", GetShort_String(data+p*2));
00663                             }
00664                             be += snprintf(buf+be, sizeof(buf)-be, "\n");
00665                         }
00666                         break;
00667 
00668                     case INT_ARRAY:
00669                         {
00670                             int be;
00671                             int p;
00672 
00673                             be = snprintf(buf, sizeof(buf), "watch %s", cmd);
00674                             for (p = 0; p*4 < len; ++p) {
00675                                 be += snprintf(buf+be, sizeof(buf)-be, " %d", GetInt_String(data+p*4));
00676                             }
00677                             be += snprintf(buf+be, sizeof(buf)-be, "\n");
00678                         }
00679                         break;
00680 
00681                     case STATS:
00682                         {
00683                             /*
00684                              * We cheat here and log each stat as a separate command, even
00685                              * if the server sent a bunch of updates as a single message;
00686                              * most scripts will be easier to write if they only parse a fixed
00687                              * format.
00688                              */
00689                             int be = 0;
00690                             while (len) {
00691                                 int c; /* which stat */
00692 
00693                                 be += snprintf(buf+be, sizeof(buf)-be, "watch %s", cmd);
00694                                 c = *data;
00695                                 ++data;
00696                                 --len;
00697                                 if (c >= CS_STAT_RESIST_START && c <= CS_STAT_RESIST_END) {
00698                                     be += snprintf(buf+be, sizeof(buf)-be, " resists %d %d\n", c, GetShort_String(data));
00699                                     data += 2;
00700                                     len -= 2;
00701                                 } else if (c >= CS_STAT_SKILLINFO && c < (CS_STAT_SKILLINFO+CS_NUM_SKILLS)) {
00702                                     be += snprintf(buf+be, sizeof(buf)-be, " skill %d %d %" FMT64 "\n", c, *data, GetInt64_String(data+1));
00703                                     data += 9;
00704                                     len -= 9;
00705                                 } else
00706                                     switch (c) {
00707                                     case CS_STAT_HP:
00708                                         be += snprintf(buf+be, sizeof(buf)-be, " hp %d\n", GetShort_String(data));
00709                                         data += 2;
00710                                         len -= 2;
00711                                         break;
00712 
00713                                     case CS_STAT_MAXHP:
00714                                         be += snprintf(buf+be, sizeof(buf)-be, " maxhp %d\n", GetShort_String(data));
00715                                         data += 2;
00716                                         len -= 2;
00717                                         break;
00718 
00719                                     case CS_STAT_SP:
00720                                         be += snprintf(buf+be, sizeof(buf)-be, " sp %d\n", GetShort_String(data));
00721                                         data += 2;
00722                                         len -= 2;
00723                                         break;
00724 
00725                                     case CS_STAT_MAXSP:
00726                                         be += snprintf(buf+be, sizeof(buf)-be, " maxsp %d\n", GetShort_String(data));
00727                                         data += 2;
00728                                         len -= 2;
00729                                         break;
00730 
00731                                     case CS_STAT_GRACE:
00732                                         be += snprintf(buf+be, sizeof(buf)-be, " grace %d\n", GetShort_String(data));
00733                                         data += 2;
00734                                         len -= 2;
00735                                         break;
00736 
00737                                     case CS_STAT_MAXGRACE:
00738                                         be += snprintf(buf+be, sizeof(buf)-be, " maxgrace %d\n", GetShort_String(data));
00739                                         data += 2;
00740                                         len -= 2;
00741                                         break;
00742 
00743                                     case CS_STAT_STR:
00744                                         be += snprintf(buf+be, sizeof(buf)-be, " str %d\n", GetShort_String(data));
00745                                         data += 2;
00746                                         len -= 2;
00747                                         break;
00748 
00749                                     case CS_STAT_INT:
00750                                         be += snprintf(buf+be, sizeof(buf)-be, " int %d\n", GetShort_String(data));
00751                                         data += 2;
00752                                         len -= 2;
00753                                         break;
00754 
00755                                     case CS_STAT_POW:
00756                                         be += snprintf(buf+be, sizeof(buf)-be, " pow %d\n", GetShort_String(data));
00757                                         data += 2;
00758                                         len -= 2;
00759                                         break;
00760 
00761                                     case CS_STAT_WIS:
00762                                         be += snprintf(buf+be, sizeof(buf)-be, " wis %d\n", GetShort_String(data));
00763                                         data += 2;
00764                                         len -= 2;
00765                                         break;
00766 
00767                                     case CS_STAT_DEX:
00768                                         be += snprintf(buf+be, sizeof(buf)-be, " dex %d\n", GetShort_String(data));
00769                                         data += 2;
00770                                         len -= 2;
00771                                         break;
00772 
00773                                     case CS_STAT_CON:
00774                                         be += snprintf(buf+be, sizeof(buf)-be, " con %d\n", GetShort_String(data));
00775                                         data += 2;
00776                                         len -= 2;
00777                                         break;
00778 
00779                                     case CS_STAT_CHA:
00780                                         be += snprintf(buf+be, sizeof(buf)-be, " cha %d\n", GetShort_String(data));
00781                                         data += 2;
00782                                         len -= 2;
00783                                         break;
00784 
00785                                     case CS_STAT_EXP:
00786                                         be += snprintf(buf+be, sizeof(buf)-be, " exp %d\n", GetInt_String(data));
00787                                         data += 4;
00788                                         len -= 4;
00789                                         break;
00790 
00791                                     case CS_STAT_EXP64:
00792                                         be += snprintf(buf+be, sizeof(buf)-be, " exp %" FMT64 "\n", GetInt64_String(data));
00793                                         data += 8;
00794                                         len -= 8;
00795                                         break;
00796 
00797                                     case CS_STAT_LEVEL:
00798                                         be += snprintf(buf+be, sizeof(buf)-be, " level %d\n", GetShort_String(data));
00799                                         data += 2;
00800                                         len -= 2;
00801                                         break;
00802 
00803                                     case CS_STAT_WC:
00804                                         be += snprintf(buf+be, sizeof(buf)-be, " wc %d\n", GetShort_String(data));
00805                                         data += 2;
00806                                         len -= 2;
00807                                         break;
00808 
00809                                     case CS_STAT_AC:
00810                                         be += snprintf(buf+be, sizeof(buf)-be, " ac %d\n", GetShort_String(data));
00811                                         data += 2;
00812                                         len -= 2;
00813                                         break;
00814 
00815                                     case CS_STAT_DAM:
00816                                         be += snprintf(buf+be, sizeof(buf)-be, " dam %d\n", GetShort_String(data));
00817                                         data += 2;
00818                                         len -= 2;
00819                                         break;
00820 
00821                                     case CS_STAT_ARMOUR:
00822                                         be += snprintf(buf+be, sizeof(buf)-be, " armour %d\n", GetShort_String(data));
00823                                         data += 2;
00824                                         len -= 2;
00825                                         break;
00826 
00827                                     case CS_STAT_SPEED:
00828                                         be += snprintf(buf+be, sizeof(buf)-be, " speed %d\n", GetInt_String(data));
00829                                         data += 4;
00830                                         len -= 4;
00831                                         break;
00832 
00833                                     case CS_STAT_FOOD:
00834                                         be += snprintf(buf+be, sizeof(buf)-be, " food %d\n", GetShort_String(data));
00835                                         data += 2;
00836                                         len -= 2;
00837                                         break;
00838 
00839                                     case CS_STAT_WEAP_SP:
00840                                         be += snprintf(buf+be, sizeof(buf)-be, " weap_sp %d\n", GetInt_String(data));
00841                                         data += 4;
00842                                         len -= 4;
00843                                         break;
00844 
00845                                     case CS_STAT_FLAGS:
00846                                         be += snprintf(buf+be, sizeof(buf)-be, " flags %d\n", GetShort_String(data));
00847                                         data += 2;
00848                                         len -= 2;
00849                                         break;
00850 
00851                                     case CS_STAT_WEIGHT_LIM:
00852                                         be += snprintf(buf+be, sizeof(buf)-be, " weight_lim %d\n", GetInt_String(data));
00853                                         data += 4;
00854                                         len -= 4;
00855                                         break;
00856 
00857                                     case CS_STAT_RANGE:
00858                                         {
00859                                             int rlen = *data;
00860                                             ++data; --len;
00861                                             be += snprintf(buf+be, sizeof(buf)-be, " range %*.*s\n", rlen, rlen, data);
00862                                             data += rlen;
00863                                             len -= rlen;
00864                                             break;
00865                                         }
00866 
00867                                     case CS_STAT_TITLE:
00868                                         {
00869                                             int rlen = *data;
00870                                             ++data;
00871                                             --len;
00872                                             be += snprintf(buf+be, sizeof(buf)-be, " title %*.*s\n", rlen, rlen, data);
00873                                             data += rlen;
00874                                             len -= rlen;
00875                                             break;
00876                                         }
00877 
00878                                     default:
00879                                         be += snprintf(buf+be, sizeof(buf)-be, " unknown %d %d bytes left\n", c, len);
00880                                         len = 0;
00881                                     }
00882                             }
00883                         }
00884                         break;
00885 
00886                     case MIXED:
00887                         /* magicmap */
00888                         /* mapextended */
00889                         /* item1 item2 */
00890                         /* upditem */
00891                         /* image image2 */
00892                         /* face face1 face2 */
00893                         /* sound */
00894                         /* player */
00895                         /*
00896                          * If we find that scripts need data from any of the above, we can
00897                          * write special-case code as with stats.  In the meantime, fall
00898                          * through and just give a hex dump.  Script writers should not
00899                          * depend on that data format.
00900                          */
00901                     case NODATA:
00902                     default: {
00903                         int be;
00904                         int p;
00905 
00906                         /*we may receive an null data, in which case len has no meaning*/
00907                         if (!data)
00908                             len = 0;
00909                         be = snprintf(buf, sizeof(buf), "watch %s %d bytes unparsed:", cmd, len);
00910                         for (p = 0; p < len && p < 100; ++p) {
00911                             be += snprintf(buf+be, sizeof(buf)-be, " %02x", data[p]);
00912                         }
00913                         be += snprintf(buf+be, sizeof(buf)-be, "\n");
00914                     }
00915                         break;
00916                     }
00917                 write(scripts[i].out_fd, buf, strlen(buf));
00918             }
00919         }
00920     }
00921 }
00922 
00923 void script_monitor(const char *command, int repeat, int must_send) {
00924     int i;
00925 
00926     /* For each script... */
00927     for (i = 0; i < num_scripts; ++i) {
00928         /* Do we send the command? */
00929         if (scripts[i].monitor) {
00930             char buf[1024];
00931 
00932             snprintf(buf, sizeof(buf), "monitor %d %d %s\n", repeat, must_send, command);
00933             write(scripts[i].out_fd, buf, strlen(buf));
00934         }
00935     }
00936 }
00937 
00938 void script_monitor_str(const char *command) {
00939     int i;
00940 
00941     /* For each script... */
00942     for (i = 0; i < num_scripts; ++i) {
00943         /* Do we send the command? */
00944         if (scripts[i].monitor) {
00945             char buf[1024];
00946 
00947             snprintf(buf, sizeof(buf), "monitor %s\n", command);
00948             write(scripts[i].out_fd, buf, strlen(buf));
00949         }
00950     }
00951 }
00952 
00953 void script_tell(const char *params) {
00954     int i;
00955     char *p;
00956 
00957     if (params == NULL) {
00958         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00959             "Which script do you want to talk to?");
00960         return;
00961     }
00962     p = strchr(params, ' ');
00963     if (p == NULL) {
00964         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00965             "What do you want to tell the script?");
00966         return;
00967     }
00968     while (*p == ' ') {
00969         *p++ = '\0';
00970     }
00971 
00972     /* Find the script */
00973     i = script_by_name(params);
00974     if (i < 0) {
00975         draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT,
00976             "No such running script");
00977         return;
00978     }
00979 
00980     /* Send the message */
00981     write(scripts[i].out_fd, "scripttell ", 11);
00982     write(scripts[i].out_fd, p, strlen(p));
00983     write(scripts[i].out_fd, "\n", 1);
00984 }
00985 
00986 static int script_by_name(const char *name) {
00987     int i;
00988     int l;
00989 
00990     if (name == NULL) {
00991         return(num_scripts == 1 ? 0 : -1);
00992     }
00993 
00994     /* Parse script number */
00995     if (isdigit(*name)) {
00996         i = atoi(name);
00997         --i;
00998         if (i >= 0 && i < num_scripts)
00999             return(i);
01000     }
01001 
01002     /* Parse script name */
01003     l = 0;
01004     while (name[l] && name[l] != ' ')
01005         ++l;
01006     for (i = 0; i < num_scripts; ++i) {
01007         if (strncmp(name, scripts[i].name, l) == 0)
01008             return(i);
01009     }
01010     return(-1);
01011 }
01012 
01013 static void script_dead(int i) {
01014     int w;
01015 
01016     /* Release resources */
01017 #ifndef WIN32
01018     close(scripts[i].in_fd);
01019     close(scripts[i].out_fd);
01020 #else
01021     CloseHandle(scripts[i].in_fd);
01022     CloseHandle(scripts[i].out_fd);
01023     CloseHandle(scripts[i].process);
01024 #endif
01025     free(scripts[i].name);
01026     free(scripts[i].params);
01027     for (w = 0; w < scripts[i].num_watch; ++w)
01028         free(scripts[i].watch[w]);
01029     free(scripts[i].watch);
01030 
01031 #ifndef WIN32
01032     waitpid(-1, NULL, WNOHANG);
01033 #endif
01034 
01035     /* Move scripts with higher index numbers down one slot */
01036     if (i < (num_scripts-1)) {
01037         memmove(&scripts[i], &scripts[i+1], sizeof(scripts[i])*(num_scripts-i-1));
01038     }
01039 
01040     /* Update our count */
01041     --num_scripts;
01042 }
01043 
01044 static void send_map(int i, int x, int y) {
01045     char buf[1024];
01046 
01047     if (x < 0 || y < 0 || the_map.x <= x || the_map.y <= y) {
01048         snprintf(buf, sizeof(buf), "request map %d %d unknown\n", x, y);
01049         write(scripts[i].out_fd, buf, strlen(buf));
01050     }
01051     /*** FIXME *** send more relevant data ***/
01052     snprintf(buf, sizeof(buf), "request map %d %d  %d %c %c %c %c"
01053         " smooth %d %d %d heads %d %d %d tails %d %d %d\n",
01054         x, y, the_map.cells[x][y].darkness,
01055         the_map.cells[x][y].need_update ? 'y' : 'n',
01056         the_map.cells[x][y].have_darkness ? 'y' : 'n',
01057         the_map.cells[x][y].need_resmooth ? 'y' : 'n',
01058         the_map.cells[x][y].cleared ? 'y' : 'n',
01059         the_map.cells[x][y].smooth[0], the_map.cells[x][y].smooth[1], the_map.cells[x][y].smooth[2],
01060         the_map.cells[x][y].heads[0].face, the_map.cells[x][y].heads[1].face, the_map.cells[x][y].heads[2].face,
01061         the_map.cells[x][y].tails[0].face, the_map.cells[x][y].tails[1].face, the_map.cells[x][y].tails[2].face
01062     );
01063     write(scripts[i].out_fd, buf, strlen(buf));
01064 }
01065 
01066 static void script_process_cmd(int i) {
01067     char cmd[1024];
01068     char *c;
01069     int l;
01070 
01071     /*
01072      * Strip out just this one command
01073      */
01074     for (l = 0; l < scripts[i].cmd_count; ++l) {
01075         if (scripts[i].cmd[l] == '\n')
01076             break;
01077     }
01078     ++l;
01079     memcpy(cmd, scripts[i].cmd, l);
01080 #ifndef WIN32
01081     cmd[l-1] = 0;
01082 #else
01083     cmd[l-2] = 0;
01084 #endif
01085     if (l < scripts[i].cmd_count) {
01086         memmove(scripts[i].cmd, scripts[i].cmd+l, scripts[i].cmd_count-l);
01087         scripts[i].cmd_count -= l;
01088     } else {
01089         scripts[i].cmd_count = 0;
01090     }
01091 
01092     /*
01093      * Now the data in scripts[i] is ready for the next read.
01094      * We have a complete command in cmd[].
01095      * Process it.
01096      */
01097     /*
01098      * Script commands
01099      *
01100      * watch <command type>
01101      * unwatch <command type>
01102      * request <data type>
01103      * issue <repeat> <must_send> <command>
01104      * localcmd <command> [<params>]
01105      * draw <color> <text>
01106      * monitor
01107      * unmonitor
01108      */
01109     if (strncmp(cmd, "sync", 4) == 0) {
01110         c = cmd+4;
01111         while (*c && *c != ' ')
01112             ++c;
01113         while (*c == ' ')
01114             ++c;
01115         scripts[i].sync_watch = -1;
01116         if (isdigit(*c)) {
01117             scripts[i].sync_watch = atoi(c);
01118         }
01119         script_sync(csocket.command_sent - csocket.command_received); /* in case we are already there */
01120     } else if (strncmp(cmd, "watch", 5) == 0) {
01121         c = cmd+5;
01122         while (*c && *c != ' ')
01123             ++c;
01124         while (*c == ' ')
01125             ++c;
01126         c = strdup(c);
01127         scripts[i].watch = realloc(scripts[i].watch, (scripts[i].num_watch+1)*sizeof(scripts[i].watch[1]));
01128         scripts[i].watch[scripts[i].num_watch] = c;
01129         ++scripts[i].num_watch;
01130     } else if (strncmp(cmd, "unwatch", 7) == 0) {
01131         int w;
01132 
01133         c = cmd+7;
01134         while (*c && *c != ' ')
01135             ++c;
01136         while (*c == ' ')
01137             ++c;
01138         for (w = 0; w < scripts[i].num_watch; ++w) {
01139             if (strcmp(c, scripts[i].watch[w]) == 0) {
01140                 free(scripts[i].watch[w]);
01141                 while (w+1 < scripts[i].num_watch) {
01142                     scripts[i].watch[w] = scripts[i].watch[w+1];
01143                     ++w;
01144                 }
01145                 --scripts[i].num_watch;
01146                 break;
01147             }
01148         }
01149     } else if (strncmp(cmd, "request", 7) == 0) {
01150         c = cmd+7;
01151         while (*c && *c != ' ')
01152             ++c;
01153         while (*c == ' ')
01154             ++c;
01155         if (!*c)
01156             return; /* bad request */
01157         /*
01158          * Request information from the client's view of the world
01159          * (mostly defined in client.h)
01160          *
01161          * Valid requests:
01162          *
01163          *   player       Return the player's tag and title
01164          *   range        Return the type and name of the currently selected range attack
01165          *   stat <type>  Return the specified stats
01166          *   stat stats   Return Str,Con,Dex,Int,Wis,Pow,Cha
01167          *   stat cmbt    Return wc,ac,dam,speed,weapon_sp
01168          *   stat hp      Return hp,maxhp,sp,maxsp,grace,maxgrace,food
01169          *   stat xp      Return level,xp,skill-1 level,skill-1 xp,...
01170          *   stat resists Return resistances
01171          *   stat paths   Return spell paths: attuned, repelled, denied.
01172          *   weight       Return maxweight, weight
01173          *   flags        Return flags (fire, run)
01174          *   items inv    Return a list of items in the inventory, one per line
01175          *   items actv   Return a list of inventory items that are active, one per line
01176          *   items on     Return a list of items under the player, one per line
01177          *   items cont   Return a list of items in the open container, one per line
01178          *   map pos      Return the players x,y within the current map
01179          *   map near     Return the 3x3 grid of the map centered on the player
01180          *   map all      Return all the known map information
01181          *   map <x> <y>  Return the information about square x,y in the current map
01182          *   skills       Return a list of all skill names, one per line (see also stat xp)
01183          *   spells       Return a list of known spells, one per line
01184          */
01185         if (strncmp(c, "player", 6) == 0) {
01186             char buf[1024];
01187 
01188             snprintf(buf, sizeof(buf), "request player %d %s\n", cpl.ob->tag, cpl.title);
01189             write(scripts[i].out_fd, buf, strlen(buf));
01190         } else if (strncmp(c, "range", 5) == 0) {
01191             char buf[1024];
01192 
01193             snprintf(buf, sizeof(buf), "request range %s\n", cpl.range);
01194             write(scripts[i].out_fd, buf, strlen(buf));
01195         } else if (strncmp(c, "weight", 5) == 0) {
01196             char buf[1024];
01197 
01198             snprintf(buf, sizeof(buf), "request weight %d %d\n", cpl.stats.weight_limit, (int)(cpl.ob->weight*1000));
01199             write(scripts[i].out_fd, buf, strlen(buf));
01200         } else if (strncmp(c, "stat ", 5) == 0) {
01201             c += 4;
01202             while (*c && *c != ' ')
01203                 ++c;
01204             while (*c == ' ')
01205                 ++c;
01206             if (!*c)
01207                 return; /* bad request */
01208             /*
01209              *   stat stats   Return Str,Con,Dex,Int,Wis,Pow,Cha
01210              *   stat cmbt    Return wc,ac,dam,speed,weapon_sp
01211              *   stat hp      Return hp,maxhp,sp,maxsp,grace,maxgrace,food
01212              *   stat xp      Return level,xp,skill-1 level,skill-1 xp,...
01213              *   stat resists Return resistances
01214              */
01215             if (strncmp(c, "stats", 5) == 0) {
01216                 char buf[1024];
01217 
01218                 snprintf(buf, sizeof(buf), "request stat stats %d %d %d %d %d %d %d\n", cpl.stats.Str, cpl.stats.Con, cpl.stats.Dex, cpl.stats.Int, cpl.stats.Wis, cpl.stats.Pow, cpl.stats.Cha);
01219                 write(scripts[i].out_fd, buf, strlen(buf));
01220             } else if (strncmp(c, "cmbt", 4) == 0) {
01221                 char buf[1024];
01222 
01223                 snprintf(buf, sizeof(buf), "request stat cmbt %d %d %d %d %d\n", cpl.stats.wc, cpl.stats.ac, cpl.stats.dam, cpl.stats.speed, cpl.stats.weapon_sp);
01224                 write(scripts[i].out_fd, buf, strlen(buf));
01225             } else if (strncmp(c, "hp", 2) == 0) {
01226                 char buf[1024];
01227 
01228                 snprintf(buf, sizeof(buf), "request stat hp %d %d %d %d %d %d %d\n", cpl.stats.hp, cpl.stats.maxhp, cpl.stats.sp, cpl.stats.maxsp, cpl.stats.grace, cpl.stats.maxgrace, cpl.stats.food);
01229                 write(scripts[i].out_fd, buf, strlen(buf));
01230             } else if (strncmp(c, "xp", 2) == 0) {
01231                 char buf[1024];
01232                 int s;
01233 
01234                 snprintf(buf, sizeof(buf), "request stat xp %d %" FMT64, cpl.stats.level, cpl.stats.exp);
01235                 write(scripts[i].out_fd, buf, strlen(buf));
01236                 for (s = 0; s < MAX_SKILL; ++s) {
01237                     snprintf(buf, sizeof(buf), " %d %" FMT64, cpl.stats.skill_level[s], cpl.stats.skill_exp[s]);
01238                     write(scripts[i].out_fd, buf, strlen(buf));
01239                 }
01240                 write(scripts[i].out_fd, "\n", 1);
01241             } else if (strncmp(c, "resists", 7) == 0) {
01242                 char buf[1024];
01243                 int s;
01244 
01245                 snprintf(buf, sizeof(buf), "request stat resists");
01246                 write(scripts[i].out_fd, buf, strlen(buf));
01247                 for (s = 0; s < 30; ++s) {
01248                     snprintf(buf, sizeof(buf), " %d", cpl.stats.resists[s]);
01249                     write(scripts[i].out_fd, buf, strlen(buf));
01250                 }
01251                 write(scripts[i].out_fd, "\n", 1);
01252             } else if (strncmp(c, "paths", 2) == 0) {
01253                 char buf[1024];
01254 
01255                 snprintf(buf, sizeof(buf), "request stat paths %d %d %d\n", cpl.stats.attuned, cpl.stats.repelled, cpl.stats.denied);
01256                 write(scripts[i].out_fd, buf, strlen(buf));
01257             }
01258         } else if (strncmp(c, "flags", 5) == 0) {
01259             char buf[1024];
01260 
01261             snprintf(buf, sizeof(buf), "request flags %d %d %d %d\n", cpl.stats.flags, cpl.fire_on, cpl.run_on, cpl.no_echo);
01262             write(scripts[i].out_fd, buf, strlen(buf));
01263         } else if (strncmp(c, "items ", 6) == 0) {
01264             c += 5;
01265             while (*c && *c != ' ')
01266                 ++c;
01267             while (*c == ' ')
01268                 ++c;
01269             if (!*c)
01270                 return; /* bad request */
01271             /*
01272              *   items inv    Return a list of items in the inventory, one per line
01273              *   items actv   Return a list of inventory items that are active, one per line
01274              *   items on     Return a list of items under the player, one per line
01275              *   items cont   Return a list of items in the open container, one per line
01276              */
01277             if (strncmp(c, "inv", 3) == 0) {
01278                 char *buf;
01279                 item *it;
01280 
01281                 for (it = cpl.ob->inv; it; it = it->next) {
01282                     script_send_item(i, "request items inv ", it);
01283                 }
01284                 buf = "request items inv end\n";
01285                 write(scripts[i].out_fd, buf, strlen(buf));
01286             }
01287             if (strncmp(c, "actv", 4) == 0) {
01288                 char *buf;
01289                 item *it;
01290 
01291                 for (it = cpl.ob->inv; it; it = it->next) {
01292                     if (it->applied)
01293                         script_send_item(i, "request items actv ", it);
01294                 }
01295                 buf = "request items actv end\n";
01296                 write(scripts[i].out_fd, buf, strlen(buf));
01297             }
01298             if (strncmp(c, "on", 2) == 0) {
01299                 char *buf;
01300                 item *it;
01301 
01302                 for (it = cpl.below->inv; it; it = it->next) {
01303                     script_send_item(i, "request items on ", it);
01304                 }
01305                 buf = "request items on end\n";
01306                 write(scripts[i].out_fd, buf, strlen(buf));
01307             }
01308             if (strncmp(c, "cont", 4) == 0) {
01309                 char *buf;
01310                 item *it;
01311 
01312                 if (cpl.container) {
01313                     for (it = cpl.container->inv; it; it = it->next) {
01314                         script_send_item(i, "request items cont ", it);
01315                     }
01316                 }
01317                 buf = "request items cont end\n";
01318                 write(scripts[i].out_fd, buf, strlen(buf));
01319             }
01320         } else if (strncmp(c, "map ", 4) == 0) {
01321             int x, y;
01322 
01323             c += 3;
01324             while (*c && *c != ' ')
01325                 ++c;
01326             while (*c == ' ')
01327                 ++c;
01328             if (!*c)
01329                 return; /* bad request */
01330             /*
01331              *   map pos      Return the players x,y within the current map
01332              *   map near     Return the 3x3 grid of the map centered on the player
01333              *   map all      Return all the known map information
01334              *   map <x> <y>  Return the information about square x,y in the current map
01335              */
01336             if (strncmp(c, "pos", 3) == 0) {
01337                 char buf[1024];
01338 
01339                 snprintf(buf, sizeof(buf), "request map pos %d %d\n", pl_pos.x+use_config[CONFIG_MAPWIDTH]/2, pl_pos.y+use_config[CONFIG_MAPHEIGHT]/2);
01340                 write(scripts[i].out_fd, buf, strlen(buf));
01341             } else if (strncmp(c, "near", 4) == 0) {
01342                 for (y = 0; y < 3; ++y)
01343                     for (x = 0; x < 3; ++x)
01344                         send_map(i,
01345                             x+pl_pos.x+use_config[CONFIG_MAPWIDTH]/2-1,
01346                             y+pl_pos.y+use_config[CONFIG_MAPHEIGHT]/2-1
01347                         );
01348             } else if (strncmp(c, "all", 3) == 0) {
01349                 char buf[1024];
01350 
01351                 for (y = 0; y < the_map.y; ++y)
01352                     for (x = 0; x < the_map.x; ++x)
01353                         send_map(i, x, y);
01354                 snprintf(buf, sizeof(buf), "request map end\n");
01355                 write(scripts[i].out_fd, buf, strlen(buf));
01356             } else {
01357                 while (*c && !isdigit(*c))
01358                     ++c;
01359                 if (!*c)
01360                     return; /* No x specified */
01361                 x = atoi(c);
01362                 while (*c && *c != ' ')
01363                     ++c;
01364                 while (*c && !isdigit(*c))
01365                     ++c;
01366                 if (!*c)
01367                     return; /* No y specified */
01368                 y = atoi(c);
01369                 send_map(i, x, y);
01370             }
01371         } else if (strncmp(c, "skills", 6) == 0) {
01372             char buf[1024];
01373             int s;
01374 
01375             for (s = 0; s < CS_NUM_SKILLS; s++) {
01376                 if (skill_names[s]) {
01377                     sprintf(buf, "request skills %d %s\n", CS_STAT_SKILLINFO + s, skill_names[s]);
01378                     write(scripts[i].out_fd, buf, strlen(buf));
01379                 }
01380             }
01381             sprintf(buf, "request skills end\n");
01382             write(scripts[i].out_fd, buf, strlen(buf));
01383         } else if (strncmp(c, "spells", 6) == 0) {
01384             char buf[1024];
01385             Spell *spell;
01386 
01387             for (spell = cpl.spelldata; spell; spell = spell->next) {
01388                 sprintf(buf, "request spells %d %d %d %d %d %d %d %d %s\n",
01389                     spell->tag, spell->level, spell->sp, spell->grace,
01390                     spell->skill_number, spell->path, spell->time,
01391                     spell->dam, spell->name);
01392                 write(scripts[i].out_fd, buf, strlen(buf));
01393             }
01394             sprintf(buf, "request spells end\n");
01395             write(scripts[i].out_fd, buf, strlen(buf));
01396         } else {
01397             char buf[1024];
01398 
01399             snprintf(buf, sizeof(buf), "Script %d %s malfunction; unimplemented request:", i+1, scripts[i].name);
01400             draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, buf);
01401             draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, cmd);
01402         }
01403     } else if (strncmp(cmd, "issue", 5) == 0) {
01404         int repeat;
01405         int must_send;
01406 
01407         c = cmd+5;
01408         while (*c && *c == ' ')
01409             ++c;
01410         if (*c && (isdigit(*c) || *c == '-')) { /* repeat specified; use send_command() */
01411             repeat = atoi(c);
01412             while (*c && *c != ' ')
01413                 ++c;
01414             while (*c && !isdigit(*c) && *c != '-')
01415                 ++c;
01416             if (!*c)
01417                 return; /* No must_send specified */
01418             must_send = atoi(c);
01419             while (*c && *c != ' ')
01420                 ++c;
01421             if (!*c)
01422                 return; /* No command specified */
01423             while (*c == ' ')
01424                 ++c;
01425             if (repeat != -1) {
01426                 int r;
01427 
01428                 r = send_command(c, repeat, must_send);
01429                 if (r != 1) {
01430                     char buf[1024];
01431 
01432                     snprintf(buf, sizeof(buf), "Script %d %s malfunction; command not sent", i+1, scripts[i].name);
01433                     draw_ext_info(
01434                         NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, buf);
01435                     draw_ext_info(
01436                         NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, cmd);
01437                 }
01438             }
01439         } else {
01440             c = cmd+5;
01441             while (*c && *c != ' ')
01442                 ++c;
01443             while (*c == ' ')
01444                 ++c;
01445 
01446             /*
01447              * Check special cases: "mark <tag>" or "lock <new state> <tag>"
01448              */
01449             if (strncmp(c, "mark", 4) == 0) {
01450                 int tag;
01451                 SockList sl;
01452                 uint8 buf[MAX_BUF];
01453 
01454                 c += 4;
01455 
01456                 while (*c && !isdigit(*c))
01457                     ++c;
01458                 if (!*c)
01459                     return; /* No tag specified */
01460                 tag = atoi(c);
01461 
01462                 SockList_Init(&sl, buf);
01463                 SockList_AddString(&sl, "mark ");
01464                 SockList_AddInt(&sl, tag);
01465                 SockList_Send(&sl, csocket.fd);
01466             } else if (strncmp(c, "lock", 4) == 0) {
01467                 int tag, locked;
01468                 SockList sl;
01469                 uint8 buf[MAX_BUF];
01470 
01471                 c += 4;
01472 
01473                 while (*c && !isdigit(*c))
01474                     ++c;
01475                 if (!*c)
01476                     return; /* No state specified */
01477                 locked = atoi(c);
01478                 while (*c && *c != ' ')
01479                     ++c;
01480                 while (*c && !isdigit(*c))
01481                     ++c;
01482                 if (!*c)
01483                     return; /* No tag specified */
01484                 tag = atoi(c);
01485 
01486                 SockList_Init(&sl, buf);
01487                 SockList_AddString(&sl, "lock ");
01488                 SockList_AddChar(&sl, locked);
01489                 SockList_AddInt(&sl, tag);
01490                 SockList_Send(&sl, csocket.fd);
01491             } else {
01492                 cs_print_string(csocket.fd, "%s", c);
01493             }
01494         }
01495     } else if (strncmp(cmd, "localcmd", 8) == 0){
01496         char *param;
01497 
01498         c = cmd+8;
01499         while (*c == ' ')
01500             c++;
01501         param = c;
01502         while ((*param != '\0') && (*param != ' '))
01503             param++;
01504         if (*param == ' '){
01505             *param = '\0';
01506             param++;
01507         } else
01508             param = NULL;
01509 
01510         if (!handle_local_command(c, param)){
01511             char buf[1024];
01512 
01513             snprintf(buf, sizeof(buf), "Script %s malfunction; localcmd not understood", scripts[i].name);
01514             draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, buf);
01515             snprintf(buf, sizeof(buf), "Script <<localcmd %s %s>>", c, (param == NULL) ? "" : param);
01516             draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, buf);
01517         }
01518     } else if (strncmp(cmd, "draw", 4) == 0) {
01519         int color;
01520 
01521         c = cmd+4;
01522         while (*c && !isdigit(*c))
01523             ++c;
01524         if (!*c)
01525             return; /* No color specified */
01526         color = atoi(c);
01527         while (*c && *c != ' ')
01528             ++c;
01529         if (!*c)
01530             return; /* No message specified */
01531         while (*c == ' ')
01532             ++c;
01533         draw_ext_info(color, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, c);
01534     } else if (strncmp(cmd, "monitor", 7) == 0)
01535         scripts[i].monitor = 1;
01536     else if (strncmp(cmd, "unmonitor", 9) == 0)
01537         scripts[i].monitor = 0;
01538     else {
01539         char buf[1024];
01540 
01541         snprintf(buf, sizeof(buf), "Script %d %s malfunction; invalid command:", i+1, scripts[i].name);
01542         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, buf);
01543         draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SCRIPT, cmd);
01544     }
01545 }
01546 
01547 /*
01548  * script_send_item()
01549  *
01550  * Send one line to the script with item information.
01551  *
01552  * A header string is passed in.  The format is:
01553  *
01554  * <header>  tag num weight flags type name
01555  *
01556  * flags are a bitmask:
01557  *   magic, cursed, damned, unpaid, locked, applied, open, was_open, inv_updated
01558  *    256     128     64      32       16      8       4      2         1
01559  */
01560 static void script_send_item(int i, const char *head, const item *it) {
01561     char buf[4096];
01562     int flags;
01563 
01564     flags = it->magical;
01565     flags = (flags<<1)|it->cursed;
01566     flags = (flags<<1)|it->damned;
01567     flags = (flags<<1)|it->unpaid;
01568     flags = (flags<<1)|it->locked;
01569     flags = (flags<<1)|it->applied;
01570     flags = (flags<<1)|it->open;
01571     flags = (flags<<1)|it->was_open;
01572     flags = (flags<<1)|it->inv_updated;
01573     snprintf(buf, sizeof(buf), "%s%d %d %d %d %d %s\n", head, it->tag, it->nrof, (int)(it->weight*1000+0.5), flags, it->type, it->d_name);
01574     write(scripts[i].out_fd, buf, strlen(buf));
01575 }
01576 
01577 #endif /* CPROTO */