1 /*
2 * usystem.c -- X-free, but Unix-like code for XBoard front end
3 *
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
5 * Massachusetts.
6 *
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
9 *
10 * The following terms apply to Digital Equipment Corporation's copyright
11 * interest in XBoard:
12 * ------------------------------------------------------------------------
13 * All Rights Reserved
14 *
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.
22 *
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
29 * SOFTWARE.
30 * ------------------------------------------------------------------------
31 *
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
35 *
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.
40 *
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.
45 *
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/. *
48 *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
51
52 #include "config.h"
53
54 #include <stdio.h>
55 #include <ctype.h>
56 #include <signal.h>
57 #include <errno.h>
58 #include <sys/types.h>
59 #include <sys/stat.h>
60 #include <pwd.h>
61 #include <math.h>
62
63 #if !OMIT_SOCKETS
64 # if HAVE_SYS_SOCKET_H
65 # include <sys/socket.h>
66 # include <netinet/in.h>
67 # include <netdb.h>
68 # else /* not HAVE_SYS_SOCKET_H */
69 # if HAVE_LAN_SOCKET_H
70 # include <lan/socket.h>
71 # include <lan/in.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 */
78
79 #if STDC_HEADERS
80 # include <stdlib.h>
81 # include <string.h>
82 #else /* not STDC_HEADERS */
83 extern char *getenv();
84 # if HAVE_STRING_H
85 # include <string.h>
86 # else /* not HAVE_STRING_H */
87 # include <strings.h>
88 # endif /* not HAVE_STRING_H */
89 #endif /* not STDC_HEADERS */
90
91 #if HAVE_SYS_FCNTL_H
92 # include <sys/fcntl.h>
93 #else /* not HAVE_SYS_FCNTL_H */
94 # if HAVE_FCNTL_H
95 # include <fcntl.h>
96 # endif /* HAVE_FCNTL_H */
97 #endif /* not HAVE_SYS_FCNTL_H */
98
99 #if HAVE_SYS_SYSTEMINFO_H
100 # include <sys/systeminfo.h>
101 #endif /* HAVE_SYS_SYSTEMINFO_H */
102
103 #if TIME_WITH_SYS_TIME
104 # include <sys/time.h>
105 # include <time.h>
106 #else
107 # if HAVE_SYS_TIME_H
108 # include <sys/time.h>
109 # else
110 # include <time.h>
111 # endif
112 #endif
113
114 #if HAVE_UNISTD_H
115 # include <unistd.h>
116 #endif
117
118 #if HAVE_SYS_WAIT_H
119 # include <sys/wait.h>
120 #endif
121
122 #if HAVE_DIRENT_H
123 # include <dirent.h>
124 # define NAMLEN(dirent) strlen((dirent)->d_name)
125 # define HAVE_DIR_STRUCT
126 #else
127 # define dirent direct
128 # define NAMLEN(dirent) (dirent)->d_namlen
129 # if HAVE_SYS_NDIR_H
130 # include <sys/ndir.h>
131 # define HAVE_DIR_STRUCT
132 # endif
133 # if HAVE_SYS_DIR_H
134 # include <sys/dir.h>
135 # define HAVE_DIR_STRUCT
136 # endif
137 # if HAVE_NDIR_H
138 # include <ndir.h>
139 # define HAVE_DIR_STRUCT
140 # endif
141 #endif
142
143 #if ENABLE_NLS
144 #include <locale.h>
145 #endif
146
147 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
148 #include "common.h"
149
150 #include "frontend.h"
151 #include "backend.h"
152 #include "childio.h"
153 #include "menus.h"
154 #include "usystem.h"
155 #include "gettext.h"
156
157
158 #ifdef __EMX__
159 #ifndef HAVE_USLEEP
160 #define HAVE_USLEEP
161 #endif
162 #define usleep(t) _sleep2(((t)+500)/1000)
163 #endif
164
165 #ifdef ENABLE_NLS
166 # define _(s) gettext (s)
167 # define N_(s) gettext_noop (s)
168 #else
169 # define _(s) (s)
170 # define N_(s) s
171 #endif
172
173 static int get_term_width P(());
174
175 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
176 "magenta", "cyan", "white" };
177 TextColors textColors[(int)NColorClasses];
178
179 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
180 static int
parse_color(char * str,int which)181 parse_color (char *str, int which)
182 {
183 char *p, buf[100], *d;
184 int i;
185
186 if (strlen(str) > 99) /* watch bounds on buf */
187 return -1;
188
189 p = str;
190 d = buf;
191 for (i=0; i<which; ++i) {
192 p = strchr(p, ',');
193 if (!p)
194 return -1;
195 ++p;
196 }
197
198 /* Could be looking at something like:
199 black, , 1
200 .. in which case we want to stop on a comma also */
201 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
202 ++p;
203
204 if (*p == ',') {
205 return -1; /* Use default for empty field */
206 }
207
208 if (which == 2 || isdigit(*p))
209 return atoi(p);
210
211 while (*p && isalpha(*p))
212 *(d++) = *(p++);
213
214 *d = 0;
215
216 for (i=0; i<8; ++i) {
217 if (!StrCaseCmp(buf, cnames[i]))
218 return which? (i+40) : (i+30);
219 }
220 if (!StrCaseCmp(buf, "default")) return -1;
221
222 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
223 return -2;
224 }
225
226 static int
parse_cpair(ColorClass cc,char * str)227 parse_cpair (ColorClass cc, char *str)
228 {
229 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
230 fprintf(stderr, _("%s: can't parse foreground color in '%s'\n"),
231 programName, str);
232 return -1;
233 }
234
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;
239 }
240 return 0;
241 }
242
243 void
ParseIcsTextColors()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)
256 {
257 if (appData.colorize) {
258 fprintf(stderr,
259 _("%s: can't parse color names; disabling colorization\n"),
260 programName);
261 }
262 appData.colorize = FALSE;
263 }
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
267 }
268
269 static Boolean noEcho;
270
271 void
EchoOn()272 EchoOn ()
273 {
274 system("stty echo");
275 noEcho = False;
276 }
277
278 void
EchoOff()279 EchoOff ()
280 {
281 system("stty -echo");
282 noEcho = True;
283 }
284
285 char *oldICSInteractionTitle;
286
287 void
ShutDownFrontEnd()288 ShutDownFrontEnd ()
289 {
290 if (appData.icsActive && oldICSInteractionTitle != NULL) {
291 DisplayIcsInteractionTitle(oldICSInteractionTitle);
292 }
293 if (saveSettingsOnExit) SaveSettings(settingsFileName);
294 unlink(gameCopyFilename);
295 unlink(gamePasteFilename);
296 if(noEcho) EchoOn();
297 }
298
299 void
RunCommand(char * buf)300 RunCommand (char *buf)
301 {
302 system(buf);
303 }
304
305 void
Colorize(ColorClass cc,int continuation)306 Colorize (ColorClass cc, int continuation)
307 {
308 char buf[MSG_SIZ];
309 int count, outCount, error;
310
311 SetTextColor(cnames, textColors[(int)cc].fg - 30, textColors[(int)cc].bg - 40, textColors[(int)cc].attr); // for GTK widget
312
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);
317 } else {
318 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
319 textColors[(int)cc].bg);
320 }
321 } else {
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);
325 } else {
326 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
327 }
328 }
329 count = strlen(buf);
330 outCount = OutputToProcess(NoProc, buf, count, &error);
331 if (outCount < count) {
332 DisplayFatalError(_("Error writing to display"), error, 1);
333 }
334
335 if (continuation) return;
336 PlaySoundForColor(cc);
337 }
338
339 char *
UserName()340 UserName ()
341 {
342 return getpwuid(getuid())->pw_name;
343 }
344
345 char *
ExpandPathName(char * path)346 ExpandPathName (char *path)
347 {
348 static char static_buf[4*MSG_SIZ];
349 char *d, *s, buf[4*MSG_SIZ];
350 struct passwd *pwd;
351
352 s = path;
353 d = static_buf;
354
355 while (*s && isspace(*s))
356 ++s;
357
358 if (!*s) {
359 *d = 0;
360 return static_buf;
361 }
362
363 if (*s == '~') {
364 if(s[1] == '~') { // use ~~ for XBoard's private data directory
365 snprintf(d, 4*MSG_SIZ, DATADIR "%s", s+2);
366 } else
367 if (*(s+1) == '/') {
368 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
369 strcat(d, s+1);
370 }
371 else {
372 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
373 { char *p; if(p = strchr(buf, '/')) *p = 0; }
374 pwd = getpwnam(buf);
375 if (!pwd)
376 {
377 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
378 buf, path);
379 return NULL;
380 }
381 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
382 strcat(d, strchr(s+1, '/'));
383 }
384 }
385 else
386 safeStrCpy(d, s, 4*MSG_SIZ );
387
388 return static_buf;
389 }
390
391 int
MySearchPath(char * installDir,char * name,char * fullname)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 );
397 else {
398 sprintf(fullname, "%s%c%s", installDir, '/', name);
399 }
400 return 1;
401 }
402
403 int
MyGetFullPathName(char * name,char * fullname)404 MyGetFullPathName (char *name, char *fullname)
405 { // should use ExpandPath?
406 name = ExpandPathName(name);
407 safeStrCpy(fullname, name, MSG_SIZ );
408 return 1;
409 }
410
411 char *
HostName()412 HostName ()
413 {
414 static char host_name[MSG_SIZ];
415
416 #if HAVE_GETHOSTNAME
417 gethostname(host_name, MSG_SIZ);
418 return host_name;
419 #else /* not HAVE_GETHOSTNAME */
420 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
421 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
422 return host_name;
423 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
424 return "localhost";
425 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
426 #endif /* not HAVE_GETHOSTNAME */
427 }
428
429
430 int
StartChildProcess(char * cmdLine,char * dir,ProcRef * pr)431 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
432 {
433 char *argv[64], *p;
434 int i, pid;
435 int to_prog[2], from_prog[2];
436 ChildProc *cp;
437 char buf[MSG_SIZ];
438
439 if (appData.debugMode) {
440 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
441 }
442
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.
446 */
447 i = 0;
448 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
449 p = buf;
450 for (;;) {
451 while(*p == ' ') p++;
452 argv[i++] = p;
453 if(*p == '"' || *p == '\'')
454 p = strchr(++argv[i-1], *p);
455 else p = strchr(p, ' ');
456 if (p == NULL) break;
457 *p++ = NULLCHAR;
458 }
459 argv[i] = NULL;
460
461 SetUpChildIO(to_prog, from_prog);
462
463 if ((pid = fork()) == 0) {
464 /* Child process */
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
467 close(from_prog[0]);
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 */
474
475 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
476 perror(dir);
477 exit(1);
478 }
479
480 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
481
482 execvp(argv[0], argv);
483
484 /* If we get here, exec failed */
485 perror(argv[0]);
486 exit(1);
487 }
488
489 /* Parent process */
490 close(to_prog[0]);
491 close(from_prog[1]);
492
493 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
494 cp->kind = CPReal;
495 cp->pid = pid;
496 cp->fdFrom = from_prog[0];
497 cp->fdTo = to_prog[1];
498 *pr = (ProcRef) cp;
499 return 0;
500 }
501
502 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
503 static int pid;
504
505 static RETSIGTYPE
AlarmCallBack(int n)506 AlarmCallBack (int n)
507 {
508 kill(pid, SIGKILL); // kill forcefully
509 return;
510 }
511
512 void
DestroyChildProcess(ProcRef pr,int signalType)513 DestroyChildProcess (ProcRef pr, int signalType)
514 {
515 ChildProc *cp = (ChildProc *) pr;
516
517 if (cp->kind != CPReal) return;
518 cp->kind = CPNone;
519 if (signalType & 1) {
520 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: for 9 hard-kill immediately
521 }
522 signal(SIGALRM, AlarmCallBack);
523 pid = cp->pid;
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.
527 */
528 wait((int *) 0);
529 alarm(0); // cancel alarm if still pending
530 close(cp->fdFrom);
531 close(cp->fdTo);
532 }
533
534 void
InterruptChildProcess(ProcRef pr)535 InterruptChildProcess (ProcRef pr)
536 {
537 ChildProc *cp = (ChildProc *) pr;
538
539 if (cp->kind != CPReal) return;
540 (void) kill(cp->pid, SIGINT); /* stop it thinking */
541 }
542
543 int
OpenTelnet(char * host,char * port,ProcRef * pr)544 OpenTelnet (char *host, char *port, ProcRef *pr)
545 {
546 char cmdLine[MSG_SIZ];
547
548 if (port[0] == NULLCHAR) {
549 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
550 } else {
551 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
552 }
553 return StartChildProcess(cmdLine, "", pr);
554 }
555
556 int
OpenTCP(char * host,char * port,ProcRef * pr)557 OpenTCP (char *host, char *port, ProcRef *pr)
558 {
559 #if OMIT_SOCKETS
560 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
561 #else /* !OMIT_SOCKETS */
562 struct addrinfo hints;
563 struct addrinfo *ais, *ai;
564 int error;
565 int s=0;
566 ChildProc *cp;
567
568 memset(&hints, 0, sizeof(hints));
569 hints.ai_family = AF_UNSPEC;
570 hints.ai_socktype = SOCK_STREAM;
571
572 error = getaddrinfo(host, port, &hints, &ais);
573 if (error != 0) {
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));
577 return ENOENT;
578 }
579
580 for (ai = ais; ai != NULL; ai = ai->ai_next) {
581 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
582 error = errno;
583 continue;
584 }
585 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
586 error = errno;
587 continue;
588 }
589 error = 0;
590 break;
591 }
592 freeaddrinfo(ais);
593
594 if (error != 0) {
595 return error;
596 }
597
598 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
599 cp->kind = CPSock;
600 cp->pid = 0;
601 cp->fdFrom = s;
602 cp->fdTo = s;
603 *pr = (ProcRef) cp;
604 #endif /* !OMIT_SOCKETS */
605
606 return 0;
607 }
608
609 int
OpenCommPort(char * name,ProcRef * pr)610 OpenCommPort (char *name, ProcRef *pr)
611 {
612 int fd;
613 ChildProc *cp;
614
615 fd = open(name, 2, 0);
616 if (fd < 0) return errno;
617
618 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
619 cp->kind = CPComm;
620 cp->pid = 0;
621 cp->fdFrom = fd;
622 cp->fdTo = fd;
623 *pr = (ProcRef) cp;
624
625 return 0;
626 }
627
628 int
OpenLoopback(ProcRef * pr)629 OpenLoopback (ProcRef *pr)
630 {
631 ChildProc *cp;
632 int to[2], from[2];
633
634 SetUpChildIO(to, from);
635
636 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
637 cp->kind = CPLoop;
638 cp->pid = 0;
639 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
640 cp->fdTo = to[1];
641 *pr = (ProcRef) cp;
642
643 return 0;
644 }
645
646 int
OpenRcmd(char * host,char * user,char * cmd,ProcRef * pr)647 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
648 {
649 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
650 return -1;
651 }
652
653 Boolean stdoutClosed = FALSE;
654
655 int
OutputToProcess(ProcRef pr,char * message,int count,int * outError)656 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
657 {
658 static int line = 0;
659 ChildProc *cp = (ChildProc *) pr;
660 int outCount = count;
661
662 if (pr == NoProc)
663 {
664 if (appData.noJoin || !appData.useInternalWrap) {
665 if(!stdoutClosed) outCount = fwrite(message, 1, count, stdout);
666 } else
667 {
668 int width = get_term_width();
669 int len = wrap(NULL, message, count, width, &line);
670 char *msg = malloc(len);
671 int dbgchk;
672
673 if (!msg)
674 outCount = fwrite(message, 1, count, stdout);
675 else
676 {
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);
681 free(msg);
682 }
683 }
684 if(*message != '\033') ConsoleWrite(message, count);
685 }
686 else
687 outCount = write(cp->fdTo, message, count);
688
689 if (outCount == -1)
690 *outError = errno;
691 else
692 *outError = 0;
693
694 return outCount;
695 }
696
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. */
701 int
OutputToProcessDelayed(ProcRef pr,char * message,int count,int * outError,long msdelay)702 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
703 {
704 ChildProc *cp = (ChildProc *) pr;
705 int outCount = 0;
706 int r;
707
708 while (count--) {
709 r = write(cp->fdTo, message++, 1);
710 if (r == -1) {
711 *outError = errno;
712 return outCount;
713 }
714 ++outCount;
715 if (msdelay >= 0)
716 TimeDelay(msdelay);
717 }
718
719 return outCount;
720 }
721
722 int
ICSInitScript()723 ICSInitScript ()
724 {
725 /* try to open the icsLogon script, either in the location given
726 * or in the users HOME directory
727 */
728
729 FILE *f;
730 char buf[MSG_SIZ];
731 char *homedir;
732
733 f = fopen(appData.icsLogon, "r");
734 if (f == NULL)
735 {
736 homedir = getenv("HOME");
737 if (homedir != NULL)
738 {
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);
742 f = fopen(buf, "r");
743 }
744 }
745
746 if (f != NULL) {
747 ProcessICSInitScript(f);
748 return TRUE;
749 } else
750 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
751
752 return FALSE;
753 }
754
755 void
ResetFrontEnd()756 ResetFrontEnd ()
757 {
758 CommentPopDown();
759 TagsPopDown();
760 return;
761 }
762
763 #include <sys/ioctl.h>
764 static int
get_term_width()765 get_term_width ()
766 {
767 int fd, default_width;
768
769 fd = STDIN_FILENO;
770 default_width = 79; // this is FICS default anyway...
771
772 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
773 struct ttysize win;
774 if (!ioctl(fd, TIOCGSIZE, &win))
775 default_width = win.ts_cols;
776 #elif defined(TIOCGWINSZ)
777 struct winsize win;
778 if (!ioctl(fd, TIOCGWINSZ, &win))
779 default_width = win.ws_col;
780 #endif
781 return default_width;
782 }
783
784 void
update_ics_width()785 update_ics_width ()
786 {
787 static int old_width = 0;
788 int new_width = get_term_width();
789
790 if (old_width != new_width)
791 ics_printf("set width %d\n", new_width);
792 old_width = new_width;
793 }
794
795 void
NotifyFrontendLogin()796 NotifyFrontendLogin ()
797 {
798 update_ics_width();
799 }
800