2 * usystem.c -- X-free, but Unix-like code for XBoard front end
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
58 #include <sys/types.h>
64 # if HAVE_SYS_SOCKET_H
65 # include <sys/socket.h>
66 # include <netinet/in.h>
68 # else /* not HAVE_SYS_SOCKET_H */
69 # if HAVE_LAN_SOCKET_H
70 # include <lan/socket.h>
72 # include <lan/netdb.h>
73 # else /* not HAVE_LAN_SOCKET_H */
74 # define OMIT_SOCKETS 1
75 # endif /* not HAVE_LAN_SOCKET_H */
76 # endif /* not HAVE_SYS_SOCKET_H */
77 #endif /* !OMIT_SOCKETS */
82 #else /* not STDC_HEADERS */
83 extern char *getenv();
86 # else /* not HAVE_STRING_H */
88 # endif /* not HAVE_STRING_H */
89 #endif /* not STDC_HEADERS */
92 # include <sys/fcntl.h>
93 #else /* not HAVE_SYS_FCNTL_H */
96 # endif /* HAVE_FCNTL_H */
97 #endif /* not HAVE_SYS_FCNTL_H */
99 #if HAVE_SYS_SYSTEMINFO_H
100 # include <sys/systeminfo.h>
101 #endif /* HAVE_SYS_SYSTEMINFO_H */
103 #if TIME_WITH_SYS_TIME
104 # include <sys/time.h>
108 # include <sys/time.h>
119 # include <sys/wait.h>
124 # define NAMLEN(dirent) strlen((dirent)->d_name)
125 # define HAVE_DIR_STRUCT
127 # define dirent direct
128 # define NAMLEN(dirent) (dirent)->d_namlen
130 # include <sys/ndir.h>
131 # define HAVE_DIR_STRUCT
134 # include <sys/dir.h>
135 # define HAVE_DIR_STRUCT
139 # define HAVE_DIR_STRUCT
147 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
150 #include "frontend.h"
162 #define usleep(t) _sleep2(((t)+500)/1000)
166 # define _(s) gettext (s)
167 # define N_(s) gettext_noop (s)
173 static int get_term_width P(());
175 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
176 "magenta", "cyan", "white" };
177 TextColors textColors[(int)NColorClasses];
179 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
181 parse_color (char *str, int which)
183 char *p, buf[100], *d;
186 if (strlen(str) > 99) /* watch bounds on buf */
191 for (i=0; i<which; ++i) {
198 /* Could be looking at something like:
200 .. in which case we want to stop on a comma also */
201 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
205 return -1; /* Use default for empty field */
208 if (which == 2 || isdigit(*p))
211 while (*p && isalpha(*p))
216 for (i=0; i<8; ++i) {
217 if (!StrCaseCmp(buf, cnames[i]))
218 return which? (i+40) : (i+30);
220 if (!StrCaseCmp(buf, "default")) return -1;
222 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
227 parse_cpair (ColorClass cc, char *str)
229 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
230 fprintf(stderr, _("%s: can't parse foreground color in '%s'\n"),
235 /* bg and attr are optional */
236 textColors[(int)cc].bg = parse_color(str, 1);
237 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
238 textColors[(int)cc].attr = 0;
244 ParseIcsTextColors ()
245 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
246 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
247 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
248 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
249 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
250 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
251 parse_cpair(ColorTell, appData.colorTell) < 0 ||
252 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
253 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
254 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
255 parse_cpair(ColorNormal, appData.colorNormal) < 0)
257 if (appData.colorize) {
259 _("%s: can't parse color names; disabling colorization\n"),
262 appData.colorize = FALSE;
264 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
265 textColors[ColorNone].attr = 0;
266 SetTextColor(cnames, textColors[ColorNormal].fg - 30, textColors[ColorNormal].bg - 40, -2); // kludge to announce background color to front-end
269 static Boolean noEcho;
281 system("stty -echo");
285 char *oldICSInteractionTitle;
290 if (appData.icsActive && oldICSInteractionTitle != NULL) {
291 DisplayIcsInteractionTitle(oldICSInteractionTitle);
293 if (saveSettingsOnExit) SaveSettings(settingsFileName);
294 unlink(gameCopyFilename);
295 unlink(gamePasteFilename);
300 RunCommand (char *buf)
306 Colorize (ColorClass cc, int continuation)
309 int count, outCount, error;
311 SetTextColor(cnames, textColors[(int)cc].fg - 30, textColors[(int)cc].bg - 40, textColors[(int)cc].attr); // for GTK widget
313 if (textColors[(int)cc].bg > 0) {
314 if (textColors[(int)cc].fg > 0) {
315 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
316 textColors[(int)cc].fg, textColors[(int)cc].bg);
318 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
319 textColors[(int)cc].bg);
322 if (textColors[(int)cc].fg > 0) {
323 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
324 textColors[(int)cc].fg);
326 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
330 outCount = OutputToProcess(NoProc, buf, count, &error);
331 if (outCount < count) {
332 DisplayFatalError(_("Error writing to display"), error, 1);
335 if (continuation) return;
336 PlaySoundForColor(cc);
342 return getpwuid(getuid())->pw_name;
346 ExpandPathName (char *path)
348 static char static_buf[4*MSG_SIZ];
349 char *d, *s, buf[4*MSG_SIZ];
355 while (*s && isspace(*s))
364 if(s[1] == '~') { // use ~~ for XBoard's private data directory
365 snprintf(d, 4*MSG_SIZ, DATADIR "%s", s+2);
368 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
372 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
373 { char *p; if(p = strchr(buf, '/')) *p = 0; }
377 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
381 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
382 strcat(d, strchr(s+1, '/'));
386 safeStrCpy(d, s, 4*MSG_SIZ );
392 MySearchPath (char *installDir, char *name, char *fullname)
393 { // just append installDir and name. Perhaps ExpandPath should be used here?
394 name = ExpandPathName(name);
395 if(name && name[0] == '/')
396 safeStrCpy(fullname, name, MSG_SIZ );
398 sprintf(fullname, "%s%c%s", installDir, '/', name);
404 MyGetFullPathName (char *name, char *fullname)
405 { // should use ExpandPath?
406 name = ExpandPathName(name);
407 safeStrCpy(fullname, name, MSG_SIZ );
414 static char host_name[MSG_SIZ];
417 gethostname(host_name, MSG_SIZ);
419 #else /* not HAVE_GETHOSTNAME */
420 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
421 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
423 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
425 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
426 #endif /* not HAVE_GETHOSTNAME */
431 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
435 int to_prog[2], from_prog[2];
439 if (appData.debugMode) {
440 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
443 /* We do NOT feed the cmdLine to the shell; we just
444 parse it into blank-separated arguments in the
445 most simple-minded way possible.
448 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
451 while(*p == ' ') p++;
453 if(*p == '"' || *p == '\'')
454 p = strchr(++argv[i-1], *p);
455 else p = strchr(p, ' ');
456 if (p == NULL) break;
461 SetUpChildIO(to_prog, from_prog);
463 if ((pid = fork()) == 0) {
465 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
466 close(to_prog[1]); // first close the unused pipe ends
468 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
469 dup2(from_prog[1], 1);
470 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
471 close(from_prog[1]); // and closing again loses one of the pipes!
472 if(fileno(stderr) >= 2) // better safe than sorry...
473 dup2(1, fileno(stderr)); /* force stderr to the pipe */
475 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
480 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
482 execvp(argv[0], argv);
484 /* If we get here, exec failed */
493 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
496 cp->fdFrom = from_prog[0];
497 cp->fdTo = to_prog[1];
502 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
506 AlarmCallBack (int n)
508 kill(pid, SIGKILL); // kill forcefully
513 DestroyChildProcess (ProcRef pr, int signalType)
515 ChildProc *cp = (ChildProc *) pr;
517 if (cp->kind != CPReal) return;
519 if (signalType & 1) {
520 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: for 9 hard-kill immediately
522 signal(SIGALRM, AlarmCallBack);
524 if(signalType & 4) alarm(1 + appData.delayAfterQuit); // [HGM] kill: schedule hard kill if so requested
525 /* Process is exiting either because of the kill or because of
526 a quit command sent by the backend; either way, wait for it to die.
529 alarm(0); // cancel alarm if still pending
535 InterruptChildProcess (ProcRef pr)
537 ChildProc *cp = (ChildProc *) pr;
539 if (cp->kind != CPReal) return;
540 (void) kill(cp->pid, SIGINT); /* stop it thinking */
544 OpenTelnet (char *host, char *port, ProcRef *pr)
546 char cmdLine[MSG_SIZ];
548 if (port[0] == NULLCHAR) {
549 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
551 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
553 return StartChildProcess(cmdLine, "", pr);
557 OpenTCP (char *host, char *port, ProcRef *pr)
560 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
561 #else /* !OMIT_SOCKETS */
562 struct addrinfo hints;
563 struct addrinfo *ais, *ai;
568 memset(&hints, 0, sizeof(hints));
569 hints.ai_family = AF_UNSPEC;
570 hints.ai_socktype = SOCK_STREAM;
572 error = getaddrinfo(host, port, &hints, &ais);
574 /* a getaddrinfo error is not an errno, so can't return it */
575 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
576 host, port, gai_strerror(error));
580 for (ai = ais; ai != NULL; ai = ai->ai_next) {
581 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
585 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
598 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
604 #endif /* !OMIT_SOCKETS */
610 OpenCommPort (char *name, ProcRef *pr)
615 fd = open(name, 2, 0);
616 if (fd < 0) return errno;
618 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
629 OpenLoopback (ProcRef *pr)
634 SetUpChildIO(to, from);
636 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
639 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
647 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
649 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
654 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
657 ChildProc *cp = (ChildProc *) pr;
662 if (appData.noJoin || !appData.useInternalWrap)
663 outCount = fwrite(message, 1, count, stdout);
666 int width = get_term_width();
667 int len = wrap(NULL, message, count, width, &line);
668 char *msg = malloc(len);
672 outCount = fwrite(message, 1, count, stdout);
675 dbgchk = wrap(msg, message, count, width, &line);
676 if (dbgchk != len && appData.debugMode)
677 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
678 outCount = fwrite(msg, 1, dbgchk, stdout);
682 if(*message != '\033') ConsoleWrite(message, count);
685 outCount = write(cp->fdTo, message, count);
695 /* Output message to process, with "ms" milliseconds of delay
696 between each character. This is needed when sending the logon
697 script to ICC, which for some reason doesn't like the
698 instantaneous send. */
700 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
702 ChildProc *cp = (ChildProc *) pr;
707 r = write(cp->fdTo, message++, 1);
723 /* try to open the icsLogon script, either in the location given
724 * or in the users HOME directory
731 f = fopen(appData.icsLogon, "r");
734 homedir = getenv("HOME");
737 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
738 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
739 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
745 ProcessICSInitScript(f);
748 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
761 #include <sys/ioctl.h>
765 int fd, default_width;
768 default_width = 79; // this is FICS default anyway...
770 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
772 if (!ioctl(fd, TIOCGSIZE, &win))
773 default_width = win.ts_cols;
774 #elif defined(TIOCGWINSZ)
776 if (!ioctl(fd, TIOCGWINSZ, &win))
777 default_width = win.ws_col;
779 return default_width;
785 static int old_width = 0;
786 int new_width = get_term_width();
788 if (old_width != new_width)
789 ics_printf("set width %d\n", new_width);
790 old_width = new_width;
794 NotifyFrontendLogin ()