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