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