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 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 char *cnames[9] = { "black", "red", "green", "yellow", "blue",
174 "magenta", "cyan", "white" };
175 TextColors textColors[(int)NColorClasses];
177 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
179 parse_color (char *str, int which)
181 char *p, buf[100], *d;
184 if (strlen(str) > 99) /* watch bounds on buf */
189 for (i=0; i<which; ++i) {
196 /* Could be looking at something like:
198 .. in which case we want to stop on a comma also */
199 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
203 return -1; /* Use default for empty field */
206 if (which == 2 || isdigit(*p))
209 while (*p && isalpha(*p))
214 for (i=0; i<8; ++i) {
215 if (!StrCaseCmp(buf, cnames[i]))
216 return which? (i+40) : (i+30);
218 if (!StrCaseCmp(buf, "default")) return -1;
220 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
225 parse_cpair (ColorClass cc, char *str)
227 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
228 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
233 /* bg and attr are optional */
234 textColors[(int)cc].bg = parse_color(str, 1);
235 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
236 textColors[(int)cc].attr = 0;
242 ParseIcsTextColors ()
243 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
244 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
245 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
246 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
247 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
248 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
249 parse_cpair(ColorTell, appData.colorTell) < 0 ||
250 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
251 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
252 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
253 parse_cpair(ColorNormal, appData.colorNormal) < 0)
255 if (appData.colorize) {
257 _("%s: can't parse color names; disabling colorization\n"),
260 appData.colorize = FALSE;
262 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
263 textColors[ColorNone].attr = 0;
266 static Boolean noEcho;
278 system("stty -echo");
282 char *oldICSInteractionTitle;
287 if (appData.icsActive && oldICSInteractionTitle != NULL) {
288 DisplayIcsInteractionTitle(oldICSInteractionTitle);
290 if (saveSettingsOnExit) SaveSettings(settingsFileName);
291 unlink(gameCopyFilename);
292 unlink(gamePasteFilename);
297 RunCommand (char *buf)
303 Colorize (ColorClass cc, int continuation)
306 int count, outCount, error;
308 if (textColors[(int)cc].bg > 0) {
309 if (textColors[(int)cc].fg > 0) {
310 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
311 textColors[(int)cc].fg, textColors[(int)cc].bg);
313 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
314 textColors[(int)cc].bg);
317 if (textColors[(int)cc].fg > 0) {
318 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
319 textColors[(int)cc].fg);
321 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
325 outCount = OutputToProcess(NoProc, buf, count, &error);
326 if (outCount < count) {
327 DisplayFatalError(_("Error writing to display"), error, 1);
330 if (continuation) return;
331 PlaySoundForColor(cc);
337 return getpwuid(getuid())->pw_name;
341 ExpandPathName (char *path)
343 static char static_buf[4*MSG_SIZ];
344 char *d, *s, buf[4*MSG_SIZ];
350 while (*s && isspace(*s))
360 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
364 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
365 { char *p; if(p = strchr(buf, '/')) *p = 0; }
369 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
373 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
374 strcat(d, strchr(s+1, '/'));
378 safeStrCpy(d, s, 4*MSG_SIZ );
384 MySearchPath (char *installDir, char *name, char *fullname)
385 { // just append installDir and name. Perhaps ExpandPath should be used here?
386 name = ExpandPathName(name);
387 if(name && name[0] == '/')
388 safeStrCpy(fullname, name, MSG_SIZ );
390 sprintf(fullname, "%s%c%s", installDir, '/', name);
396 MyGetFullPathName (char *name, char *fullname)
397 { // should use ExpandPath?
398 name = ExpandPathName(name);
399 safeStrCpy(fullname, name, MSG_SIZ );
406 static char host_name[MSG_SIZ];
409 gethostname(host_name, MSG_SIZ);
411 #else /* not HAVE_GETHOSTNAME */
412 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
413 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
415 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
417 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
418 #endif /* not HAVE_GETHOSTNAME */
423 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
427 int to_prog[2], from_prog[2];
431 if (appData.debugMode) {
432 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
435 /* We do NOT feed the cmdLine to the shell; we just
436 parse it into blank-separated arguments in the
437 most simple-minded way possible.
440 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
443 while(*p == ' ') p++;
445 if(*p == '"' || *p == '\'')
446 p = strchr(++argv[i-1], *p);
447 else p = strchr(p, ' ');
448 if (p == NULL) break;
453 SetUpChildIO(to_prog, from_prog);
455 if ((pid = fork()) == 0) {
457 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
458 close(to_prog[1]); // first close the unused pipe ends
460 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
461 dup2(from_prog[1], 1);
462 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
463 close(from_prog[1]); // and closing again loses one of the pipes!
464 if(fileno(stderr) >= 2) // better safe than sorry...
465 dup2(1, fileno(stderr)); /* force stderr to the pipe */
467 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
472 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
474 execvp(argv[0], argv);
476 /* If we get here, exec failed */
485 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
488 cp->fdFrom = from_prog[0];
489 cp->fdTo = to_prog[1];
494 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
496 AlarmCallBack (int n)
502 DestroyChildProcess (ProcRef pr, int signalType)
504 ChildProc *cp = (ChildProc *) pr;
506 if (cp->kind != CPReal) return;
508 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
509 signal(SIGALRM, AlarmCallBack);
511 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
512 kill(cp->pid, SIGKILL); // kill it forcefully
513 wait((int *) 0); // and wait again
517 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
519 /* Process is exiting either because of the kill or because of
520 a quit command sent by the backend; either way, wait for it to die.
529 InterruptChildProcess (ProcRef pr)
531 ChildProc *cp = (ChildProc *) pr;
533 if (cp->kind != CPReal) return;
534 (void) kill(cp->pid, SIGINT); /* stop it thinking */
538 OpenTelnet (char *host, char *port, ProcRef *pr)
540 char cmdLine[MSG_SIZ];
542 if (port[0] == NULLCHAR) {
543 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
545 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
547 return StartChildProcess(cmdLine, "", pr);
551 OpenTCP (char *host, char *port, ProcRef *pr)
554 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
555 #else /* !OMIT_SOCKETS */
556 struct addrinfo hints;
557 struct addrinfo *ais, *ai;
562 memset(&hints, 0, sizeof(hints));
563 hints.ai_family = AF_UNSPEC;
564 hints.ai_socktype = SOCK_STREAM;
566 error = getaddrinfo(host, port, &hints, &ais);
568 /* a getaddrinfo error is not an errno, so can't return it */
569 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
570 host, port, gai_strerror(error));
574 for (ai = ais; ai != NULL; ai = ai->ai_next) {
575 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
579 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
592 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
598 #endif /* !OMIT_SOCKETS */
604 OpenCommPort (char *name, ProcRef *pr)
609 fd = open(name, 2, 0);
610 if (fd < 0) return errno;
612 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
623 OpenLoopback (ProcRef *pr)
628 SetUpChildIO(to, from);
630 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
633 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
641 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
643 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
648 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
651 ChildProc *cp = (ChildProc *) pr;
656 if (appData.noJoin || !appData.useInternalWrap)
657 outCount = fwrite(message, 1, count, stdout);
660 int width = get_term_width();
661 int len = wrap(NULL, message, count, width, &line);
662 char *msg = malloc(len);
666 outCount = fwrite(message, 1, count, stdout);
669 dbgchk = wrap(msg, message, count, width, &line);
670 if (dbgchk != len && appData.debugMode)
671 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
672 outCount = fwrite(msg, 1, dbgchk, stdout);
678 outCount = write(cp->fdTo, message, count);
688 /* Output message to process, with "ms" milliseconds of delay
689 between each character. This is needed when sending the logon
690 script to ICC, which for some reason doesn't like the
691 instantaneous send. */
693 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
695 ChildProc *cp = (ChildProc *) pr;
700 r = write(cp->fdTo, message++, 1);
716 /* try to open the icsLogon script, either in the location given
717 * or in the users HOME directory
724 f = fopen(appData.icsLogon, "r");
727 homedir = getenv("HOME");
730 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
731 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
732 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
738 ProcessICSInitScript(f);
740 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
753 #include <sys/ioctl.h>
757 int fd, default_width;
760 default_width = 79; // this is FICS default anyway...
762 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
764 if (!ioctl(fd, TIOCGSIZE, &win))
765 default_width = win.ts_cols;
766 #elif defined(TIOCGWINSZ)
768 if (!ioctl(fd, TIOCGWINSZ, &win))
769 default_width = win.ws_col;
771 return default_width;
777 static int old_width = 0;
778 int new_width = get_term_width();
780 if (old_width != new_width)
781 ics_printf("set width %d\n", new_width);
782 old_width = new_width;
786 NotifyFrontendLogin ()