Add forgotten files 1.4.70b
[polyglot.git] / engine.cpp
1 #ifndef _WIN32\r
2 \r
3 // engine.cpp\r
4 \r
5 // includes\r
6 \r
7 #include <cerrno>\r
8 #include <cstdarg>\r
9 #include <cstdio>\r
10 #include <cstdlib>\r
11 #include <cstring>\r
12 \r
13 #include <sys/types.h>\r
14 #include <sys/resource.h>\r
15 #include <unistd.h>\r
16 \r
17 #include "engine.h"\r
18 #include "io.h"\r
19 #include "option.h"\r
20 #include "util.h"\r
21 \r
22 \r
23 // constants\r
24 \r
25 static const unsigned int StringSize = 4096;\r
26 \r
27 // variables\r
28 \r
29 engine_t Engine[1];\r
30 \r
31 // prototypes\r
32 \r
33 static void my_close (int fd);\r
34 static void my_dup2  (int old_fd, int new_fd);\r
35 \r
36 // functions\r
37 \r
38 // engine_is_ok()\r
39 \r
40 bool engine_is_ok(const engine_t * engine) {\r
41 \r
42    if (engine == NULL) return false;\r
43 \r
44    if (!io_is_ok(engine->io)) return false;\r
45 \r
46    return true;\r
47 }\r
48 \r
49 // engine_open()\r
50 \r
51 void engine_open(engine_t * engine) {\r
52 \r
53    const char * dir, * command;\r
54    char string[StringSize];\r
55    int argc;\r
56    char * ptr;\r
57    char * argv[256];\r
58    int from_engine[2], to_engine[2];\r
59    pid_t pid;\r
60 \r
61    ASSERT(engine!=NULL);\r
62 \r
63    // init\r
64 \r
65    dir = option_get_string("EngineDir");\r
66    my_log("POLYGLOT Dir \"%s\"\n",dir);\r
67 \r
68    command = option_get_string("EngineCommand");\r
69    my_log("POLYGLOT Command \"%s\"\n",command);\r
70 \r
71    // parse the command line and create the argument list\r
72 \r
73    if (strlen(command) >= StringSize) my_fatal("engine_open(): buffer overflow\n");\r
74    strcpy(string,command);\r
75 \r
76    argc = 0;\r
77 \r
78    for (ptr = strtok(string," "); ptr != NULL; ptr = strtok(NULL," ")) {\r
79       argv[argc++] = ptr;\r
80    }\r
81 \r
82    argv[argc] = NULL;\r
83 \r
84    // create the pipes\r
85 \r
86    if (pipe(from_engine) == -1) {\r
87       my_fatal("engine_open(): pipe(): %s\n",strerror(errno));\r
88    }\r
89 \r
90    if (pipe(to_engine) == -1) {\r
91       my_fatal("engine_open(): pipe(): %s\n",strerror(errno));\r
92    }\r
93 \r
94    // create the child process\r
95 \r
96    pid = fork();\r
97 \r
98    if (pid == -1) {\r
99 \r
100       my_fatal("engine_open(): fork(): %s\n",strerror(errno));\r
101 \r
102    } else if (pid == 0) {\r
103 \r
104       // child = engine\r
105 \r
106       // close unused pipe descriptors to avoid deadlocks\r
107 \r
108       my_close(from_engine[0]);\r
109       my_close(to_engine[1]);\r
110 \r
111       // attach the pipe to standard input\r
112 \r
113       my_dup2(to_engine[0],STDIN_FILENO);\r
114       my_close(to_engine[0]);\r
115 \r
116       // attach the pipe to standard output\r
117 \r
118       my_dup2(from_engine[1],STDOUT_FILENO);\r
119       my_close(from_engine[1]);\r
120 \r
121       // attach standard error to standard output\r
122 \r
123       my_dup2(STDOUT_FILENO,STDERR_FILENO);\r
124 \r
125       // set a low priority\r
126 \r
127       if (option_get_bool("UseNice")) {\r
128           my_log("POLYGLOT Adjust Engine Piority");\r
129           nice(option_get_int("NiceValue"));\r
130       }\r
131 \r
132       // change the current directory\r
133 \r
134       if (dir[0] != '\0' && chdir(dir) == -1) {\r
135          my_fatal("engine_open(): chdir(): %s\n",strerror(errno));\r
136       }\r
137 \r
138       // launch the new executable file\r
139 \r
140       execvp(argv[0],&argv[0]);\r
141 \r
142       // execvp() only returns when an error has occured\r
143 \r
144       my_fatal("engine_open(): execvp(): %s\n",strerror(errno));\r
145 \r
146    } else { // pid > 0\r
147 \r
148       ASSERT(pid>0);\r
149 \r
150       // parent = PolyGlot\r
151 \r
152       // close unused pipe descriptors to avoid deadlocks\r
153 \r
154       my_close(from_engine[1]);\r
155       my_close(to_engine[0]);\r
156 \r
157       // fill in the engine struct\r
158 \r
159       engine->io->in_fd = from_engine[0];\r
160       engine->io->out_fd = to_engine[1];\r
161       engine->io->name = "Engine";\r
162       engine->pid=pid;\r
163       engine->state|=ENGINE_ACTIVE; // can we test if this really true?\r
164 \r
165       io_init(engine->io);\r
166    }\r
167 }\r
168 \r
169 // engine_active\r
170 \r
171 bool engine_active(engine_t *engine){\r
172     return (engine->state & ENGINE_ACTIVE)!=0;\r
173 }\r
174 \r
175 // engine_eof\r
176 \r
177 bool engine_eof(engine_t *engine){\r
178     return (engine->state & ENGINE_EOF)!=0;\r
179 }\r
180 \r
181 // engine_set_nice_value()\r
182 \r
183 void engine_set_nice_value(engine_t * engine, int value){\r
184     setpriority(PRIO_PROCESS,engine->pid,value);\r
185 }\r
186 \r
187 \r
188 // engine_close()\r
189 \r
190 void engine_close(engine_t * engine) {\r
191 \r
192    ASSERT(engine_is_ok(engine));\r
193 \r
194    char string[StringSize];\r
195    io_close(engine->io);\r
196        // TODO: timeout\r
197    while (!engine_eof(engine)) {\r
198        engine_get(engine,string,StringSize); \r
199    }\r
200 \r
201 }\r
202 \r
203 // engine_get_non_blocking()\r
204 \r
205 bool engine_get_non_blocking(engine_t * engine, char string[], int size){\r
206     if(io_line_ready(engine->io)){\r
207         engine_get(engine,string,StringSize);\r
208         return true;\r
209     }else{\r
210         string[0]='\0';\r
211         return false;\r
212     }\r
213 }\r
214 \r
215 // engine_get()\r
216 \r
217 void engine_get(engine_t * engine, char string[], int size) {\r
218 \r
219    ASSERT(engine_is_ok(engine));\r
220    ASSERT(string!=NULL);\r
221    ASSERT(size>=256);\r
222 \r
223    while (!io_line_ready(engine->io)) {\r
224       io_get_update(engine->io);\r
225    }\r
226 \r
227    if (!io_get_line(engine->io,string,size)) { // EOF\r
228        engine->state|=ENGINE_EOF;\r
229    }\r
230 }\r
231 \r
232 // engine_send()\r
233 \r
234 void engine_send(engine_t * engine, const char format[], ...) {\r
235 \r
236    va_list arg_list;\r
237    char string[StringSize];\r
238 \r
239    ASSERT(engine_is_ok(engine));\r
240    ASSERT(format!=NULL);\r
241 \r
242    // format\r
243 \r
244    va_start(arg_list,format);\r
245    vsprintf(string,format,arg_list);\r
246    va_end(arg_list);\r
247 \r
248    // send\r
249 \r
250    io_send(engine->io,"%s",string);\r
251 }\r
252 \r
253 // engine_send_queue()\r
254 \r
255 void engine_send_queue(engine_t * engine, const char format[], ...) {\r
256 \r
257    va_list arg_list;\r
258    char string[StringSize];\r
259 \r
260    ASSERT(engine_is_ok(engine));\r
261    ASSERT(format!=NULL);\r
262 \r
263    // format\r
264 \r
265    va_start(arg_list,format);\r
266    vsprintf(string,format,arg_list);\r
267    va_end(arg_list);\r
268 \r
269    // send\r
270 \r
271    io_send_queue(engine->io,"%s",string);\r
272 }\r
273 \r
274 // my_close()\r
275 \r
276 static void my_close(int fd) {\r
277 \r
278    ASSERT(fd>=0);\r
279 \r
280    if (close(fd) == -1) my_fatal("my_close(): close(): %s\n",strerror(errno));\r
281 }\r
282 \r
283 // my_dup2()\r
284 \r
285 static void my_dup2(int old_fd, int new_fd) {\r
286 \r
287    ASSERT(old_fd>=0);\r
288    ASSERT(new_fd>=0);\r
289 \r
290    if (dup2(old_fd,new_fd) == -1) my_fatal("my_dup2(): dup2(): %s\n",strerror(errno));\r
291 }\r
292 \r
293 // end of posix part\r
294 #else\r
295 \r
296 // WIN32 part\r
297 \r
298 // includes\r
299 \r
300 #include <stdlib.h>\r
301 #include <string.h>\r
302 #include <windows.h>\r
303 #include <direct.h>\r
304 \r
305 \r
306 \r
307 #include "engine.h"\r
308 #include "option.h"\r
309 #include "pipe.h"\r
310 #include "posix.h"\r
311 \r
312 // constants\r
313 \r
314 static const int StringSize = 4096;\r
315 \r
316 // variables\r
317 \r
318 static int nQueuePtr = 0;\r
319 static char szQueueString[StringSize];\r
320 engine_t Engine[1];\r
321 \r
322 // functions\r
323 \r
324 void set_affinity(engine_t *engine, int affin){\r
325         if(affin==-1) return;\r
326 \r
327     typedef void (WINAPI *SPAM)(HANDLE, int);\r
328     SPAM pSPAM;\r
329     pSPAM = (SPAM) GetProcAddress(\r
330         GetModuleHandle(TEXT("kernel32.dll")), \r
331         "SetProcessAffinityMask");\r
332     if(NULL != pSPAM){\r
333             // [HGM] avoid crash on Win95 by first checking if API call exists\r
334         my_log("POLYGLOT Setting process affinity to %d\n",affin);\r
335         pSPAM((engine->io).hProcess,affin);\r
336     }else{\r
337         my_log("POLYGLOT API call \"SetProcessAffinityMask\" not available\n");\r
338     }\r
339 }\r
340 \r
341 DWORD GetWin32Priority(int nice)\r
342 {\r
343 /*\r
344 REALTIME_PRIORITY_CLASS     0x00000100\r
345 HIGH_PRIORITY_CLASS         0x00000080\r
346 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
347 NORMAL_PRIORITY_CLASS       0x00000020\r
348 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
349 IDLE_PRIORITY_CLASS         0x00000040\r
350 */\r
351         if (nice < -15) return 0x00000080;\r
352         if (nice < 0)   return 0x00008000;\r
353         if (nice == 0)  return 0x00000020;\r
354         if (nice < 15)  return 0x00004000;\r
355         return 0x00000040;\r
356 }\r
357 \r
358 void engine_set_nice_value(engine_t *engine, int value){\r
359     SetPriorityClass((engine->io).hProcess,\r
360                      GetWin32Priority(value));\r
361 }\r
362 \r
363 void engine_send_queue(engine_t * engine,const char *szFormat, ...) {\r
364     nQueuePtr += vsprintf(szQueueString + nQueuePtr, szFormat, (va_list) (&szFormat + 1));\r
365 }\r
366 \r
367 void engine_send(engine_t * engine, const char *szFormat, ...) {\r
368     vsprintf(szQueueString + nQueuePtr, szFormat, (va_list) (&szFormat + 1));\r
369     (engine->io).LineOutput(szQueueString);\r
370     my_log("Adapter->Engine: %s\n",szQueueString);\r
371     nQueuePtr = 0;\r
372 }\r
373 \r
374 void engine_close(engine_t * engine){\r
375     char string[StringSize];\r
376     (engine->io).Close();\r
377         // TODO: Timeout\r
378     while (!engine_eof(engine)) { \r
379       engine_get(Engine,string,StringSize);\r
380     }\r
381     (engine->io).Kill();\r
382 }\r
383 \r
384 void engine_open(engine_t * engine){\r
385     int affinity;\r
386     char *my_dir;\r
387     engine->state=0;\r
388     if( (my_dir = _getcwd( NULL, 0 )) == NULL )\r
389         my_fatal("Can't build path: %s\n",strerror(errno));\r
390     SetCurrentDirectory(option_get_string("EngineDir"));\r
391     (engine->io).Open(option_get_string("EngineCommand"));\r
392     if((engine->io).Active()){\r
393         engine->state|=ENGINE_ACTIVE;\r
394             //play with affinity (bad idea)\r
395         affinity=option_get_int("Affinity");\r
396         if(affinity!=-1) set_affinity(engine,affinity); //AAA\r
397             //lets go back\r
398         SetCurrentDirectory(my_dir);\r
399             // set a low priority\r
400         if (option_get_bool("UseNice")){\r
401             my_log("POLYGLOT Adjust Engine Piority\n");\r
402             engine_set_nice_value(engine, option_get_int("NiceValue"));\r
403         }\r
404     }\r
405     \r
406 }\r
407 \r
408 bool engine_active(engine_t *engine){\r
409     return (engine->state & ENGINE_ACTIVE)!=0;\r
410 }\r
411 \r
412 bool engine_eof(engine_t *engine){\r
413     return (engine->state & ENGINE_EOF)!=0;\r
414 }\r
415 \r
416 bool engine_get_non_blocking(engine_t * engine, char *szLineStr, int size){\r
417     if(engine_eof(engine)){ return false;}\r
418     if ((engine->io).GetBuffer(szLineStr)) {\r
419         my_log("Engine->Adapter: %s\n",szLineStr);\r
420         return true;\r
421     } else {\r
422         szLineStr[0]='\0';\r
423         if(engine->io.EOF_()){\r
424             engine->state|=ENGINE_EOF;\r
425             my_log("POLYGLOT *** EOF from Engine ***\n");\r
426         }\r
427         return false;\r
428     }\r
429 }\r
430 \r
431 void engine_get(engine_t * engine, char *szLineStr, int size){\r
432     (engine->io).LineInput(szLineStr);\r
433     if(engine->io.EOF_()){\r
434       engine->state|=ENGINE_EOF;\r
435       my_log("POLYGLOT *** EOF from Engine ***\n");\r
436     }else{\r
437       my_log("Engine->Adapter: %s\n",szLineStr);\r
438     }\r
439 }\r
440 \r
441 \r
442 #endif\r