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);
653 Boolean stdoutClosed = FALSE;
656 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
659 ChildProc *cp = (ChildProc *) pr;
660 int outCount = count;
664 if (appData.noJoin || !appData.useInternalWrap) {
665 if(!stdoutClosed) outCount = fwrite(message, 1, count, stdout);
668 int width = get_term_width();
669 int len = wrap(NULL, message, count, width, &line);
670 char *msg = malloc(len);
674 outCount = fwrite(message, 1, count, stdout);
677 dbgchk = wrap(msg, message, count, width, &line);
678 if (dbgchk != len && appData.debugMode)
679 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
680 outCount = fwrite(msg, 1, dbgchk, stdout);
684 if(*message != '\033') ConsoleWrite(message, count);
687 outCount = write(cp->fdTo, message, count);
697 /* Output message to process, with "ms" milliseconds of delay
698 between each character. This is needed when sending the logon
699 script to ICC, which for some reason doesn't like the
700 instantaneous send. */
702 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
704 ChildProc *cp = (ChildProc *) pr;
709 r = write(cp->fdTo, message++, 1);
725 /* try to open the icsLogon script, either in the location given
726 * or in the users HOME directory
733 f = fopen(appData.icsLogon, "r");
736 homedir = getenv("HOME");
739 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
740 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
741 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
747 ProcessICSInitScript(f);
750 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
763 #include <sys/ioctl.h>
767 int fd, default_width;
770 default_width = 79; // this is FICS default anyway...
772 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
774 if (!ioctl(fd, TIOCGSIZE, &win))
775 default_width = win.ts_cols;
776 #elif defined(TIOCGWINSZ)
778 if (!ioctl(fd, TIOCGWINSZ, &win))
779 default_width = win.ws_col;
781 return default_width;
787 static int old_width = 0;
788 int new_width = get_term_width();
790 if (old_width != new_width)
791 ics_printf("set width %d\n", new_width);
792 old_width = new_width;
796 NotifyFrontendLogin ()