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, 2015, 2016 Free
9 * Software Foundation, Inc.
11 * The following terms apply to Digital Equipment Corporation's copyright
13 * ------------------------------------------------------------------------
16 * Permission to use, copy, modify, and distribute this software and its
17 * documentation for any purpose and without fee is hereby granted,
18 * provided that the above copyright notice appear in all copies and that
19 * both that copyright notice and this permission notice appear in
20 * supporting documentation, and that the name of Digital not be
21 * used in advertising or publicity pertaining to distribution of the
22 * software without specific, written prior permission.
24 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
25 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
26 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
27 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
28 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
29 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
31 * ------------------------------------------------------------------------
33 * The following terms apply to the enhanced version of XBoard
34 * distributed by the Free Software Foundation:
35 * ------------------------------------------------------------------------
37 * GNU XBoard is free software: you can redistribute it and/or modify
38 * it under the terms of the GNU General Public License as published by
39 * the Free Software Foundation, either version 3 of the License, or (at
40 * your option) any later version.
42 * GNU XBoard is distributed in the hope that it will be useful, but
43 * WITHOUT ANY WARRANTY; without even the implied warranty of
44 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
45 * General Public License for more details.
47 * You should have received a copy of the GNU General Public License
48 * along with this program. If not, see http://www.gnu.org/licenses/. *
50 *------------------------------------------------------------------------
51 ** See the file ChangeLog for a revision history. */
59 #include <sys/types.h>
65 # if HAVE_SYS_SOCKET_H
66 # include <sys/socket.h>
67 # include <netinet/in.h>
69 # else /* not HAVE_SYS_SOCKET_H */
70 # if HAVE_LAN_SOCKET_H
71 # include <lan/socket.h>
73 # include <lan/netdb.h>
74 # else /* not HAVE_LAN_SOCKET_H */
75 # define OMIT_SOCKETS 1
76 # endif /* not HAVE_LAN_SOCKET_H */
77 # endif /* not HAVE_SYS_SOCKET_H */
78 #endif /* !OMIT_SOCKETS */
83 #else /* not STDC_HEADERS */
84 extern char *getenv();
87 # else /* not HAVE_STRING_H */
89 # endif /* not HAVE_STRING_H */
90 #endif /* not STDC_HEADERS */
93 # include <sys/fcntl.h>
94 #else /* not HAVE_SYS_FCNTL_H */
97 # endif /* HAVE_FCNTL_H */
98 #endif /* not HAVE_SYS_FCNTL_H */
100 #if HAVE_SYS_SYSTEMINFO_H
101 # include <sys/systeminfo.h>
102 #endif /* HAVE_SYS_SYSTEMINFO_H */
104 #if TIME_WITH_SYS_TIME
105 # include <sys/time.h>
109 # include <sys/time.h>
120 # include <sys/wait.h>
125 # define NAMLEN(dirent) strlen((dirent)->d_name)
126 # define HAVE_DIR_STRUCT
128 # define dirent direct
129 # define NAMLEN(dirent) (dirent)->d_namlen
131 # include <sys/ndir.h>
132 # define HAVE_DIR_STRUCT
135 # include <sys/dir.h>
136 # define HAVE_DIR_STRUCT
140 # define HAVE_DIR_STRUCT
148 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
151 #include "frontend.h"
163 #define usleep(t) _sleep2(((t)+500)/1000)
167 # define _(s) gettext (s)
168 # define N_(s) gettext_noop (s)
174 static int get_term_width P(());
176 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
177 "magenta", "cyan", "white" };
178 TextColors textColors[(int)NColorClasses];
180 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
182 parse_color (char *str, int which)
184 char *p, buf[100], *d;
187 if (strlen(str) > 99) /* watch bounds on buf */
192 for (i=0; i<which; ++i) {
199 /* Could be looking at something like:
201 .. in which case we want to stop on a comma also */
202 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
206 return -1; /* Use default for empty field */
209 if (which == 2 || isdigit(*p))
212 while (*p && isalpha(*p))
217 for (i=0; i<8; ++i) {
218 if (!StrCaseCmp(buf, cnames[i]))
219 return which? (i+40) : (i+30);
221 if (!StrCaseCmp(buf, "default")) return -1;
223 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
228 parse_cpair (ColorClass cc, char *str)
230 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
231 fprintf(stderr, _("%s: can't parse foreground color in '%s'\n"),
236 /* bg and attr are optional */
237 textColors[(int)cc].bg = parse_color(str, 1);
238 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
239 textColors[(int)cc].attr = 0;
245 ParseIcsTextColors ()
246 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
247 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
248 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
249 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
250 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
251 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
252 parse_cpair(ColorTell, appData.colorTell) < 0 ||
253 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
254 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
255 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
256 parse_cpair(ColorNormal, appData.colorNormal) < 0)
258 if (appData.colorize) {
260 _("%s: can't parse color names; disabling colorization\n"),
263 appData.colorize = FALSE;
265 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
266 textColors[ColorNone].attr = 0;
267 SetTextColor(cnames, textColors[ColorNormal].fg - 30, textColors[ColorNormal].bg - 40, -2); // kludge to announce background color to front-end
270 static Boolean noEcho;
282 system("stty -echo");
286 char *oldICSInteractionTitle;
291 if (appData.icsActive && oldICSInteractionTitle != NULL) {
292 DisplayIcsInteractionTitle(oldICSInteractionTitle);
294 if (saveSettingsOnExit) SaveSettings(settingsFileName);
295 unlink(gameCopyFilename);
296 unlink(gamePasteFilename);
301 RunCommand (char *buf)
307 Colorize (ColorClass cc, int continuation)
310 int count, outCount, error;
312 SetTextColor(cnames, textColors[(int)cc].fg - 30, textColors[(int)cc].bg - 40, textColors[(int)cc].attr); // for GTK widget
314 if (textColors[(int)cc].bg > 0) {
315 if (textColors[(int)cc].fg > 0) {
316 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
317 textColors[(int)cc].fg, textColors[(int)cc].bg);
319 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
320 textColors[(int)cc].bg);
323 if (textColors[(int)cc].fg > 0) {
324 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
325 textColors[(int)cc].fg);
327 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
331 outCount = OutputToProcess(NoProc, buf, count, &error);
332 if (outCount < count) {
333 DisplayFatalError(_("Error writing to display"), error, 1);
336 if (continuation) return;
337 PlaySoundForColor(cc);
343 return getpwuid(getuid())->pw_name;
347 ExpandPathName (char *path)
349 static char static_buf[4*MSG_SIZ];
350 char *d, *s, buf[4*MSG_SIZ];
356 while (*s && isspace(*s))
365 if(s[1] == '~') { // use ~~ for XBoard's private data directory
366 snprintf(d, 4*MSG_SIZ, DATADIR "%s", s+2);
369 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
373 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
374 { char *p; if(p = strchr(buf, '/')) *p = 0; }
378 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
382 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
383 strcat(d, strchr(s+1, '/'));
387 safeStrCpy(d, s, 4*MSG_SIZ );
393 MySearchPath (char *installDir, char *name, char *fullname)
394 { // just append installDir and name. Perhaps ExpandPath should be used here?
395 name = ExpandPathName(name);
396 if(name && name[0] == '/')
397 safeStrCpy(fullname, name, MSG_SIZ );
399 sprintf(fullname, "%s%c%s", installDir, '/', name);
405 MyGetFullPathName (char *name, char *fullname)
406 { // should use ExpandPath?
407 name = ExpandPathName(name);
408 safeStrCpy(fullname, name, MSG_SIZ );
415 static char host_name[MSG_SIZ];
418 gethostname(host_name, MSG_SIZ);
420 #else /* not HAVE_GETHOSTNAME */
421 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
422 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
424 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
426 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
427 #endif /* not HAVE_GETHOSTNAME */
432 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
436 int to_prog[2], from_prog[2];
440 if (appData.debugMode) {
441 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
444 /* We do NOT feed the cmdLine to the shell; we just
445 parse it into blank-separated arguments in the
446 most simple-minded way possible.
449 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
452 while(*p == ' ') p++;
454 if(*p == '"' || *p == '\'')
455 p = strchr(++argv[i-1], *p);
456 else p = strchr(p, ' ');
457 if (p == NULL) break;
462 SetUpChildIO(to_prog, from_prog);
464 if ((pid = fork()) == 0) {
466 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
467 close(to_prog[1]); // first close the unused pipe ends
469 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
470 dup2(from_prog[1], 1);
471 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
472 close(from_prog[1]); // and closing again loses one of the pipes!
473 if(fileno(stderr) >= 2) // better safe than sorry...
474 dup2(1, fileno(stderr)); /* force stderr to the pipe */
476 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
481 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
483 execvp(argv[0], argv);
485 /* If we get here, exec failed */
494 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
497 cp->fdFrom = from_prog[0];
498 cp->fdTo = to_prog[1];
503 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
507 AlarmCallBack (int n)
509 kill(pid, SIGKILL); // kill forcefully
514 DestroyChildProcess (ProcRef pr, int signalType)
516 ChildProc *cp = (ChildProc *) pr;
518 if (cp->kind != CPReal) return;
520 if (signalType & 1) {
521 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: for 9 hard-kill immediately
523 signal(SIGALRM, AlarmCallBack);
525 if(signalType & 4) alarm(1 + appData.delayAfterQuit); // [HGM] kill: schedule hard kill if so requested
526 /* Process is exiting either because of the kill or because of
527 a quit command sent by the backend; either way, wait for it to die.
530 alarm(0); // cancel alarm if still pending
536 InterruptChildProcess (ProcRef pr)
538 ChildProc *cp = (ChildProc *) pr;
540 if (cp->kind != CPReal) return;
541 (void) kill(cp->pid, SIGINT); /* stop it thinking */
545 OpenTelnet (char *host, char *port, ProcRef *pr)
547 char cmdLine[MSG_SIZ];
549 if (port[0] == NULLCHAR) {
550 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
552 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
554 return StartChildProcess(cmdLine, "", pr);
558 OpenTCP (char *host, char *port, ProcRef *pr)
561 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
562 #else /* !OMIT_SOCKETS */
563 struct addrinfo hints;
564 struct addrinfo *ais, *ai;
569 memset(&hints, 0, sizeof(hints));
570 hints.ai_family = AF_UNSPEC;
571 hints.ai_socktype = SOCK_STREAM;
573 error = getaddrinfo(host, port, &hints, &ais);
575 /* a getaddrinfo error is not an errno, so can't return it */
576 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
577 host, port, gai_strerror(error));
581 for (ai = ais; ai != NULL; ai = ai->ai_next) {
582 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
586 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
599 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
605 #endif /* !OMIT_SOCKETS */
611 OpenCommPort (char *name, ProcRef *pr)
616 fd = open(name, 2, 0);
617 if (fd < 0) return errno;
619 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
630 OpenLoopback (ProcRef *pr)
635 SetUpChildIO(to, from);
637 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
640 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
648 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
650 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
654 Boolean stdoutClosed = FALSE;
657 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
660 ChildProc *cp = (ChildProc *) pr;
661 int outCount = count;
665 if (appData.noJoin || !appData.useInternalWrap) {
666 if(!stdoutClosed) outCount = fwrite(message, 1, count, stdout);
669 int width = get_term_width();
670 int len = wrap(NULL, message, count, width, &line);
671 char *msg = malloc(len);
675 outCount = fwrite(message, 1, count, stdout);
678 dbgchk = wrap(msg, message, count, width, &line);
679 if (dbgchk != len && appData.debugMode)
680 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
681 outCount = fwrite(msg, 1, dbgchk, stdout);
685 if(*message != '\033') ConsoleWrite(message, count);
688 outCount = write(cp->fdTo, message, count);
698 /* Output message to process, with "ms" milliseconds of delay
699 between each character. This is needed when sending the logon
700 script to ICC, which for some reason doesn't like the
701 instantaneous send. */
703 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
705 ChildProc *cp = (ChildProc *) pr;
710 r = write(cp->fdTo, message++, 1);
726 /* try to open the icsLogon script, either in the location given
727 * or in the users HOME directory
734 f = fopen(appData.icsLogon, "r");
737 homedir = getenv("HOME");
740 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
741 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
742 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
748 ProcessICSInitScript(f);
751 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
764 #include <sys/ioctl.h>
768 int fd, default_width;
771 default_width = 79; // this is FICS default anyway...
773 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
775 if (!ioctl(fd, TIOCGSIZE, &win))
776 default_width = win.ts_cols;
777 #elif defined(TIOCGWINSZ)
779 if (!ioctl(fd, TIOCGWINSZ, &win))
780 default_width = win.ws_col;
782 return default_width;
788 static int old_width = 0;
789 int new_width = get_term_width();
791 if (old_width != new_width)
792 ics_printf("set width %d\n", new_width);
793 old_width = new_width;
797 NotifyFrontendLogin ()