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