Imported Debian patch 0.52-2
[pkg/netris.git] / robot.c
1 /*
2  * Netris -- A free networked version of T*tris
3  * Copyright (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  * $Id: robot.c,v 1.8 1996/02/09 08:22:15 mhw Exp $
20  */
21
22 #include "netris.h"
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <ctype.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <sys/types.h>
29 #include <fcntl.h>
30 #include <errno.h>
31
32 static MyEventType RobotGenFunc(EventGenRec *gen, MyEvent *event);
33
34 static EventGenRec robotGen =
35                 { NULL, 0, FT_read, -1, RobotGenFunc, EM_robot };
36
37 static int robotProcess;
38 static FILE *toRobot = NULL;
39 static int toRobotFd, fromRobotFd;
40
41 static char robotBuf[128];
42 static int robotBufSize, robotBufMsg;
43
44 static int gotSigPipe;
45
46 ExtFunc void InitRobot(char *robotProg)
47 {
48         int to[2], from[2];
49         int status;
50         MyEvent event;
51
52         signal(SIGPIPE, CatchPipe);
53         AtExit(CloseRobot);
54         if (pipe(to) || pipe(from))
55                 die("pipe");
56         robotProcess = fork();
57         if (robotProcess < 0)
58                 die("fork");
59         if (robotProcess == 0) {
60                 dup2(to[0], STDIN_FILENO);
61                 dup2(from[1], STDOUT_FILENO);
62                 close(to[0]);
63                 close(to[1]);
64                 close(from[0]);
65                 close(from[1]);
66                 execl("/bin/sh", "sh", "-c", robotProg, NULL);
67                 die("execl failed");
68         }
69         close(to[0]);
70         close(from[1]);
71         toRobotFd = to[1];
72         robotGen.fd = fromRobotFd = from[0];
73         if (!(toRobot = fdopen(toRobotFd, "w")))
74                 die("fdopen");
75         if ((status = fcntl(fromRobotFd, F_GETFL, 0)) < 0)
76                 die("fcntl/F_GETFL");
77         status |= O_NONBLOCK;
78         if (fcntl(fromRobotFd, F_SETFL, status) < 0)
79                 die("fcntl/F_SETFL");
80         AddEventGen(&robotGen);
81         RobotCmd(1, "Version %d\n", ROBOT_VERSION);
82         if (WaitMyEvent(&event, EM_robot) != E_robot)
83                 fatal("Robot didn't start successfully");
84         if (1 > sscanf(event.u.robot.data, "Version %d", &robotVersion)
85                         || robotVersion < 1)
86                 fatal("Invalid Version line from robot");
87         if (robotVersion > ROBOT_VERSION)
88                 robotVersion = ROBOT_VERSION;
89 }
90
91 ExtFunc void CatchPipe(int sig)
92 {
93         robotGen.ready = gotSigPipe = 1;
94 }
95
96 ExtFunc void RobotCmd(int flush, char *fmt, ...)
97 {
98         va_list args;
99
100         va_start(args, fmt);
101         vfprintf(toRobot, fmt, args);
102         va_end(args);
103         if (flush)
104                 fflush(toRobot);
105 }
106
107 ExtFunc void RobotTimeStamp(void)
108 {
109         RobotCmd(1, "TimeStamp %.3f\n", CurTimeval() / 1.0e6);
110 }
111
112 ExtFunc void CloseRobot(void)
113 {
114         RemoveEventGen(&robotGen);
115         if(toRobot) {
116                 if (robotProcess > 0)
117                         RobotCmd(1, "Exit\n");
118                 fclose(toRobot);
119                 close(fromRobotFd);
120                 toRobot = NULL;
121         }
122 }
123
124 static MyEventType RobotGenFunc(EventGenRec *gen, MyEvent *event)
125 {
126         static int more;
127         int result, i;
128         char *p;
129
130         if (gotSigPipe) {
131                 gotSigPipe = 0;
132                 robotGen.ready = more;
133                 return E_lostRobot;
134         }
135         if (robotBufMsg > 0) {
136                 /*
137                  *      Grrrrrr!  SunOS 4.1 doesn't have memmove (or atexit)
138                  *  I'm told some others have a broken memmove
139                  *
140                  *      memmove(robotBuf, robotBuf + robotBufMsg,
141                  *                      robotBufSize - robotBufMsg);
142                  */
143                 for (i = robotBufMsg; i < robotBufSize; ++i)
144                         robotBuf[i - robotBufMsg] = robotBuf[i];
145
146                 robotBufSize -= robotBufMsg;
147                 robotBufMsg = 0;
148         }
149         if (more)
150                 more = 0;
151         else {
152                 do {
153                         result = read(fromRobotFd, robotBuf + robotBufSize,
154                                         sizeof(robotBuf) - robotBufSize);
155                 } while (result < 0 && errno == EINTR);
156                 if (result <= 0)
157                         return E_lostRobot;
158                 robotBufSize += result;
159         }
160         if (!(p = memchr(robotBuf, '\n', robotBufSize))) {
161                 if (robotBufSize >= sizeof(robotBuf))
162                         fatal("Line from robot is too long");
163                 return E_none;
164         }
165         *p = 0;
166         robotBufMsg = p - robotBuf + 1;
167         robotGen.ready = more = (memchr(robotBuf + robotBufMsg, '\n',
168                         robotBufSize - robotBufMsg) != NULL);
169         event->u.robot.size = p - robotBuf;
170         event->u.robot.data = robotBuf;
171         return E_robot;
172 }
173
174 /*
175  * vi: ts=4 ai
176  * vim: noai si
177  */