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 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;
268 static Boolean noEcho;
280 system("stty -echo");
284 char *oldICSInteractionTitle;
289 if (appData.icsActive && oldICSInteractionTitle != NULL) {
290 DisplayIcsInteractionTitle(oldICSInteractionTitle);
292 if (saveSettingsOnExit) SaveSettings(settingsFileName);
293 unlink(gameCopyFilename);
294 unlink(gamePasteFilename);
299 RunCommand (char *buf)
305 Colorize (ColorClass cc, int continuation)
308 int count, outCount, error;
310 if (textColors[(int)cc].bg > 0) {
311 if (textColors[(int)cc].fg > 0) {
312 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
313 textColors[(int)cc].fg, textColors[(int)cc].bg);
315 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
316 textColors[(int)cc].bg);
319 if (textColors[(int)cc].fg > 0) {
320 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
321 textColors[(int)cc].fg);
323 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
327 outCount = OutputToProcess(NoProc, buf, count, &error);
328 if (outCount < count) {
329 DisplayFatalError(_("Error writing to display"), error, 1);
332 if (continuation) return;
333 PlaySoundForColor(cc);
339 return getpwuid(getuid())->pw_name;
343 ExpandPathName (char *path)
345 static char static_buf[4*MSG_SIZ];
346 char *d, *s, buf[4*MSG_SIZ];
352 while (*s && isspace(*s))
361 if(s[1] == '~') { // use ~~ for XBoard's private data directory
362 snprintf(d, 4*MSG_SIZ, DATADIR "%s", s+2);
365 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
369 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
370 { char *p; if(p = strchr(buf, '/')) *p = 0; }
374 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
378 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
379 strcat(d, strchr(s+1, '/'));
383 safeStrCpy(d, s, 4*MSG_SIZ );
389 MySearchPath (char *installDir, char *name, char *fullname)
390 { // just append installDir and name. Perhaps ExpandPath should be used here?
391 name = ExpandPathName(name);
392 if(name && name[0] == '/')
393 safeStrCpy(fullname, name, MSG_SIZ );
395 sprintf(fullname, "%s%c%s", installDir, '/', name);
401 MyGetFullPathName (char *name, char *fullname)
402 { // should use ExpandPath?
403 name = ExpandPathName(name);
404 safeStrCpy(fullname, name, MSG_SIZ );
411 static char host_name[MSG_SIZ];
414 gethostname(host_name, MSG_SIZ);
416 #else /* not HAVE_GETHOSTNAME */
417 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
418 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
420 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
422 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
423 #endif /* not HAVE_GETHOSTNAME */
428 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
432 int to_prog[2], from_prog[2];
436 if (appData.debugMode) {
437 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
440 /* We do NOT feed the cmdLine to the shell; we just
441 parse it into blank-separated arguments in the
442 most simple-minded way possible.
445 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
448 while(*p == ' ') p++;
450 if(*p == '"' || *p == '\'')
451 p = strchr(++argv[i-1], *p);
452 else p = strchr(p, ' ');
453 if (p == NULL) break;
458 SetUpChildIO(to_prog, from_prog);
460 if ((pid = fork()) == 0) {
462 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
463 close(to_prog[1]); // first close the unused pipe ends
465 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
466 dup2(from_prog[1], 1);
467 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
468 close(from_prog[1]); // and closing again loses one of the pipes!
469 if(fileno(stderr) >= 2) // better safe than sorry...
470 dup2(1, fileno(stderr)); /* force stderr to the pipe */
472 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
477 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
479 execvp(argv[0], argv);
481 /* If we get here, exec failed */
490 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
493 cp->fdFrom = from_prog[0];
494 cp->fdTo = to_prog[1];
499 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
501 AlarmCallBack (int n)
507 DestroyChildProcess (ProcRef pr, int signalType)
509 ChildProc *cp = (ChildProc *) pr;
511 if (cp->kind != CPReal) return;
513 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
514 signal(SIGALRM, AlarmCallBack);
516 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
517 kill(cp->pid, SIGKILL); // kill it forcefully
518 wait((int *) 0); // and wait again
522 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
524 /* Process is exiting either because of the kill or because of
525 a quit command sent by the backend; either way, wait for it to die.
534 InterruptChildProcess (ProcRef pr)
536 ChildProc *cp = (ChildProc *) pr;
538 if (cp->kind != CPReal) return;
539 (void) kill(cp->pid, SIGINT); /* stop it thinking */
543 OpenTelnet (char *host, char *port, ProcRef *pr)
545 char cmdLine[MSG_SIZ];
547 if (port[0] == NULLCHAR) {
548 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
550 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
552 return StartChildProcess(cmdLine, "", pr);
556 OpenTCP (char *host, char *port, ProcRef *pr)
559 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
560 #else /* !OMIT_SOCKETS */
561 struct addrinfo hints;
562 struct addrinfo *ais, *ai;
567 memset(&hints, 0, sizeof(hints));
568 hints.ai_family = AF_UNSPEC;
569 hints.ai_socktype = SOCK_STREAM;
571 error = getaddrinfo(host, port, &hints, &ais);
573 /* a getaddrinfo error is not an errno, so can't return it */
574 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
575 host, port, gai_strerror(error));
579 for (ai = ais; ai != NULL; ai = ai->ai_next) {
580 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
584 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
597 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
603 #endif /* !OMIT_SOCKETS */
609 OpenCommPort (char *name, ProcRef *pr)
614 fd = open(name, 2, 0);
615 if (fd < 0) return errno;
617 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
628 OpenLoopback (ProcRef *pr)
633 SetUpChildIO(to, from);
635 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
638 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
646 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
648 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
653 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
656 ChildProc *cp = (ChildProc *) pr;
661 if (appData.noJoin || !appData.useInternalWrap)
662 outCount = fwrite(message, 1, count, stdout);
665 int width = get_term_width();
666 int len = wrap(NULL, message, count, width, &line);
667 char *msg = malloc(len);
671 outCount = fwrite(message, 1, count, stdout);
674 dbgchk = wrap(msg, message, count, width, &line);
675 if (dbgchk != len && appData.debugMode)
676 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
677 outCount = fwrite(msg, 1, dbgchk, stdout);
683 outCount = write(cp->fdTo, message, count);
693 /* Output message to process, with "ms" milliseconds of delay
694 between each character. This is needed when sending the logon
695 script to ICC, which for some reason doesn't like the
696 instantaneous send. */
698 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
700 ChildProc *cp = (ChildProc *) pr;
705 r = write(cp->fdTo, message++, 1);
721 /* try to open the icsLogon script, either in the location given
722 * or in the users HOME directory
729 f = fopen(appData.icsLogon, "r");
732 homedir = getenv("HOME");
735 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
736 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
737 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
743 ProcessICSInitScript(f);
746 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
759 #include <sys/ioctl.h>
763 int fd, default_width;
766 default_width = 79; // this is FICS default anyway...
768 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
770 if (!ioctl(fd, TIOCGSIZE, &win))
771 default_width = win.ts_cols;
772 #elif defined(TIOCGWINSZ)
774 if (!ioctl(fd, TIOCGWINSZ, &win))
775 default_width = win.ws_col;
777 return default_width;
783 static int old_width = 0;
784 int new_width = get_term_width();
786 if (old_width != new_width)
787 ics_printf("set width %d\n", new_width);
788 old_width = new_width;
792 NotifyFrontendLogin ()