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