unofficial version 0.7.1: ui improvements
[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 static EventGenRec robotGen =
34                 { NULL, 0, FT_read, -1, RobotGenFunc, EM_robot };
35
36 static int robotProcess;
37 static FILE *toRobot;
38 static int toRobotFd, fromRobotFd;
39
40 static char robotBuf[128];
41 static int robotBufSize, robotBufMsg;
42
43 static int gotSigPipe;
44
45 ExtFunc void InitRobot(char *robotProg)
46 {
47         int to[2], from[2];
48         int status;
49         MyEvent event;
50
51         signal(SIGPIPE, CatchPipe);
52         AtExit(CloseRobot);
53         if (pipe(to) || pipe(from))
54                 die("pipe");
55         robotProcess = fork();
56         if (robotProcess < 0)
57                 die("fork");
58         if (robotProcess == 0) {
59                 dup2(to[0], STDIN_FILENO);
60                 dup2(from[1], STDOUT_FILENO);
61                 close(to[0]);
62                 close(to[1]);
63                 close(from[0]);
64                 close(from[1]);
65                 execl("/bin/sh", "sh", "-c", robotProg, NULL);
66                 die("execl failed");
67         }
68         close(to[0]);
69         close(from[1]);
70         toRobotFd = to[1];
71         robotGen.fd = fromRobotFd = from[0];
72         if (!(toRobot = fdopen(toRobotFd, "w")))
73                 die("fdopen");
74         if ((status = fcntl(fromRobotFd, F_GETFL, 0)) < 0)
75                 die("fcntl/F_GETFL");
76         status |= O_NONBLOCK;
77         if (fcntl(fromRobotFd, F_SETFL, status) < 0)
78                 die("fcntl/F_SETFL");
79         AddEventGen(&robotGen);
80         RobotCmd(1, "Version %d\n", ROBOT_VERSION);
81         if (WaitMyEvent(&event, EM_robot) != E_robot)
82                 fatal("Robot didn't start successfully");
83         if (1 > sscanf(event.u.robot.data, "Version %d", &robotVersion)
84                         || robotVersion < 1)
85                 fatal("Invalid Version line from robot");
86         if (robotVersion > ROBOT_VERSION)
87                 robotVersion = ROBOT_VERSION;
88 }
89
90 ExtFunc void CatchPipe(int sig)
91 {
92         robotGen.ready = gotSigPipe = 1;
93 }
94
95 ExtFunc void RobotCmd(int flush, char *fmt, ...)
96 {
97         va_list args;
98
99         va_start(args, fmt);
100         vfprintf(toRobot, fmt, args);
101         va_end(args);
102         if (flush)
103                 fflush(toRobot);
104 }
105
106 ExtFunc void RobotTimeStamp(void)
107 {
108         RobotCmd(1, "TimeStamp %.3f\n", CurTimeval() / 1.0e6);
109 }
110
111 ExtFunc void CloseRobot(void)
112 {
113         RemoveEventGen(&robotGen);
114         if (robotProcess > 0)
115                 RobotCmd(1, "Exit\n");
116         fclose(toRobot);
117         close(fromRobotFd);
118 }
119
120 static MyEventType RobotGenFunc(EventGenRec *gen, MyEvent *event)
121 {
122         static int more;
123         int result, i;
124         char *p;
125
126         if (gotSigPipe) {
127                 gotSigPipe = 0;
128                 robotGen.ready = more;
129                 return E_lostRobot;
130         }
131         if (robotBufMsg > 0) {
132                 /*
133                  *      Grrrrrr!  SunOS 4.1 doesn't have memmove (or atexit)
134                  *  I'm told some others have a broken memmove
135                  *
136                  *      memmove(robotBuf, robotBuf + robotBufMsg,
137                  *                      robotBufSize - robotBufMsg);
138                  */
139                 for (i = robotBufMsg; i < robotBufSize; ++i)
140                         robotBuf[i - robotBufMsg] = robotBuf[i];
141
142                 robotBufSize -= robotBufMsg;
143                 robotBufMsg = 0;
144         }
145         if (more)
146                 more = 0;
147         else {
148                 do {
149                         result = read(fromRobotFd, robotBuf + robotBufSize,
150                                         sizeof(robotBuf) - robotBufSize);
151                 } while (result < 0 && errno == EINTR);
152                 if (result <= 0)
153                         return E_lostRobot;
154                 robotBufSize += result;
155         }
156         if (!(p = memchr(robotBuf, '\n', robotBufSize))) {
157                 if (robotBufSize >= sizeof(robotBuf))
158                         fatal("Line from robot is too long");
159                 return E_none;
160         }
161         *p = 0;
162         robotBufMsg = p - robotBuf + 1;
163         robotGen.ready = more = (memchr(robotBuf + robotBufMsg, '\n',
164                         robotBufSize - robotBufMsg) != NULL);
165         event->u.robot.size = p - robotBuf;
166         event->u.robot.data = robotBuf;
167         return E_robot;
168 }
169
170 /*
171  * vi: ts=4 ai
172  * vim: noai si
173  */