Merge branch 'v4.7.x' into master
[xboard.git] / usystem.c
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 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
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
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
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 }
267
268 static Boolean noEcho;
269
270 void
271 EchoOn ()
272 {
273     system("stty echo");
274     noEcho = False;
275 }
276
277 void
278 EchoOff ()
279 {
280     system("stty -echo");
281     noEcho = True;
282 }
283
284 char *oldICSInteractionTitle;
285
286 void
287 ShutDownFrontEnd ()
288 {
289     if (appData.icsActive && oldICSInteractionTitle != NULL) {
290         DisplayIcsInteractionTitle(oldICSInteractionTitle);
291     }
292     if (saveSettingsOnExit) SaveSettings(settingsFileName);
293     unlink(gameCopyFilename);
294     unlink(gamePasteFilename);
295     if(noEcho) EchoOn();
296 }
297
298 void
299 RunCommand (char *buf)
300 {
301     system(buf);
302 }
303
304 void
305 Colorize (ColorClass cc, int continuation)
306 {
307     char buf[MSG_SIZ];
308     int count, outCount, error;
309
310     if (textColors[(int)cc].bg > 0) {
311         if (textColors[(int)cc].fg > 0) {
312           snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
313                    textColors[(int)cc].fg, textColors[(int)cc].bg);
314         } else {
315           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
316                    textColors[(int)cc].bg);
317         }
318     } else {
319         if (textColors[(int)cc].fg > 0) {
320           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
321                     textColors[(int)cc].fg);
322         } else {
323           snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
324         }
325     }
326     count = strlen(buf);
327     outCount = OutputToProcess(NoProc, buf, count, &error);
328     if (outCount < count) {
329         DisplayFatalError(_("Error writing to display"), error, 1);
330     }
331
332     if (continuation) return;
333     PlaySoundForColor(cc);
334 }
335
336 char *
337 UserName ()
338 {
339     return getpwuid(getuid())->pw_name;
340 }
341
342 char *
343 ExpandPathName (char *path)
344 {
345     static char static_buf[4*MSG_SIZ];
346     char *d, *s, buf[4*MSG_SIZ];
347     struct passwd *pwd;
348
349     s = path;
350     d = static_buf;
351
352     while (*s && isspace(*s))
353       ++s;
354
355     if (!*s) {
356         *d = 0;
357         return static_buf;
358     }
359
360     if (*s == '~') {
361         if(s[1] == '~') { // use ~~ for XBoard's private data directory
362           snprintf(d, 4*MSG_SIZ, DATADIR "%s", s+2);
363         } else
364         if (*(s+1) == '/') {
365           safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
366           strcat(d, s+1);
367         }
368         else {
369           safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
370           { char *p; if(p = strchr(buf, '/')) *p = 0; }
371           pwd = getpwnam(buf);
372           if (!pwd)
373             {
374               fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
375                       buf, path);
376               return NULL;
377             }
378           safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
379           strcat(d, strchr(s+1, '/'));
380         }
381     }
382     else
383       safeStrCpy(d, s, 4*MSG_SIZ );
384
385     return static_buf;
386 }
387
388 int
389 MySearchPath (char *installDir, char *name, char *fullname)
390 { // just append installDir and name. Perhaps ExpandPath should be used here?
391   name = ExpandPathName(name);
392   if(name && name[0] == '/')
393     safeStrCpy(fullname, name, MSG_SIZ );
394   else {
395     sprintf(fullname, "%s%c%s", installDir, '/', name);
396   }
397   return 1;
398 }
399
400 int
401 MyGetFullPathName (char *name, char *fullname)
402 { // should use ExpandPath?
403   name = ExpandPathName(name);
404   safeStrCpy(fullname, name, MSG_SIZ );
405   return 1;
406 }
407
408 char *
409 HostName ()
410 {
411     static char host_name[MSG_SIZ];
412
413 #if HAVE_GETHOSTNAME
414     gethostname(host_name, MSG_SIZ);
415     return host_name;
416 #else  /* not HAVE_GETHOSTNAME */
417 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
418     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
419     return host_name;
420 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
421     return "localhost";
422 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
423 #endif /* not HAVE_GETHOSTNAME */
424 }
425
426
427 int
428 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
429 {
430     char *argv[64], *p;
431     int i, pid;
432     int to_prog[2], from_prog[2];
433     ChildProc *cp;
434     char buf[MSG_SIZ];
435
436     if (appData.debugMode) {
437         fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
438     }
439
440     /* We do NOT feed the cmdLine to the shell; we just
441        parse it into blank-separated arguments in the
442        most simple-minded way possible.
443        */
444     i = 0;
445     safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
446     p = buf;
447     for (;;) {
448         while(*p == ' ') p++;
449         argv[i++] = p;
450         if(*p == '"' || *p == '\'')
451              p = strchr(++argv[i-1], *p);
452         else p = strchr(p, ' ');
453         if (p == NULL) break;
454         *p++ = NULLCHAR;
455     }
456     argv[i] = NULL;
457
458     SetUpChildIO(to_prog, from_prog);
459
460     if ((pid = fork()) == 0) {
461         /* Child process */
462         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
463         close(to_prog[1]);     // first close the unused pipe ends
464         close(from_prog[0]);
465         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
466         dup2(from_prog[1], 1);
467         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
468         close(from_prog[1]);                   // and closing again loses one of the pipes!
469         if(fileno(stderr) >= 2) // better safe than sorry...
470                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
471
472         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
473             perror(dir);
474             exit(1);
475         }
476
477         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
478
479         execvp(argv[0], argv);
480
481         /* If we get here, exec failed */
482         perror(argv[0]);
483         exit(1);
484     }
485
486     /* Parent process */
487     close(to_prog[0]);
488     close(from_prog[1]);
489
490     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
491     cp->kind = CPReal;
492     cp->pid = pid;
493     cp->fdFrom = from_prog[0];
494     cp->fdTo = to_prog[1];
495     *pr = (ProcRef) cp;
496     return 0;
497 }
498
499 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
500 static RETSIGTYPE
501 AlarmCallBack (int n)
502 {
503     return;
504 }
505
506 void
507 DestroyChildProcess (ProcRef pr, int signalType)
508 {
509     ChildProc *cp = (ChildProc *) pr;
510
511     if (cp->kind != CPReal) return;
512     cp->kind = CPNone;
513     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
514         signal(SIGALRM, AlarmCallBack);
515         alarm(3);
516         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
517             kill(cp->pid, SIGKILL); // kill it forcefully
518             wait((int *) 0);        // and wait again
519         }
520     } else {
521         if (signalType) {
522             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
523         }
524         /* Process is exiting either because of the kill or because of
525            a quit command sent by the backend; either way, wait for it to die.
526         */
527         wait((int *) 0);
528     }
529     close(cp->fdFrom);
530     close(cp->fdTo);
531 }
532
533 void
534 InterruptChildProcess (ProcRef pr)
535 {
536     ChildProc *cp = (ChildProc *) pr;
537
538     if (cp->kind != CPReal) return;
539     (void) kill(cp->pid, SIGINT); /* stop it thinking */
540 }
541
542 int
543 OpenTelnet (char *host, char *port, ProcRef *pr)
544 {
545     char cmdLine[MSG_SIZ];
546
547     if (port[0] == NULLCHAR) {
548       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
549     } else {
550       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
551     }
552     return StartChildProcess(cmdLine, "", pr);
553 }
554
555 int
556 OpenTCP (char *host, char *port, ProcRef *pr)
557 {
558 #if OMIT_SOCKETS
559     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
560 #else  /* !OMIT_SOCKETS */
561     struct addrinfo hints;
562     struct addrinfo *ais, *ai;
563     int error;
564     int s=0;
565     ChildProc *cp;
566
567     memset(&hints, 0, sizeof(hints));
568     hints.ai_family = AF_UNSPEC;
569     hints.ai_socktype = SOCK_STREAM;
570
571     error = getaddrinfo(host, port, &hints, &ais);
572     if (error != 0) {
573       /* a getaddrinfo error is not an errno, so can't return it */
574       fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
575               host, port, gai_strerror(error));
576       return ENOENT;
577     }
578
579     for (ai = ais; ai != NULL; ai = ai->ai_next) {
580       if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
581         error = errno;
582         continue;
583       }
584       if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
585         error = errno;
586         continue;
587       }
588       error = 0;
589       break;
590     }
591     freeaddrinfo(ais);
592
593     if (error != 0) {
594       return error;
595     }
596
597     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
598     cp->kind = CPSock;
599     cp->pid = 0;
600     cp->fdFrom = s;
601     cp->fdTo = s;
602     *pr = (ProcRef) cp;
603 #endif /* !OMIT_SOCKETS */
604
605     return 0;
606 }
607
608 int
609 OpenCommPort (char *name, ProcRef *pr)
610 {
611     int fd;
612     ChildProc *cp;
613
614     fd = open(name, 2, 0);
615     if (fd < 0) return errno;
616
617     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
618     cp->kind = CPComm;
619     cp->pid = 0;
620     cp->fdFrom = fd;
621     cp->fdTo = fd;
622     *pr = (ProcRef) cp;
623
624     return 0;
625 }
626
627 int
628 OpenLoopback (ProcRef *pr)
629 {
630     ChildProc *cp;
631     int to[2], from[2];
632
633     SetUpChildIO(to, from);
634
635     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
636     cp->kind = CPLoop;
637     cp->pid = 0;
638     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
639     cp->fdTo = to[1];
640     *pr = (ProcRef) cp;
641
642     return 0;
643 }
644
645 int
646 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
647 {
648     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
649     return -1;
650 }
651
652 int
653 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
654 {
655     static int line = 0;
656     ChildProc *cp = (ChildProc *) pr;
657     int outCount;
658
659     if (pr == NoProc)
660     {
661         if (appData.noJoin || !appData.useInternalWrap)
662             outCount = fwrite(message, 1, count, stdout);
663         else
664         {
665             int width = get_term_width();
666             int len = wrap(NULL, message, count, width, &line);
667             char *msg = malloc(len);
668             int dbgchk;
669
670             if (!msg)
671                 outCount = fwrite(message, 1, count, stdout);
672             else
673             {
674                 dbgchk = wrap(msg, message, count, width, &line);
675                 if (dbgchk != len && appData.debugMode)
676                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
677                 outCount = fwrite(msg, 1, dbgchk, stdout);
678                 free(msg);
679             }
680         }
681     }
682     else
683       outCount = write(cp->fdTo, message, count);
684
685     if (outCount == -1)
686       *outError = errno;
687     else
688       *outError = 0;
689
690     return outCount;
691 }
692
693 /* Output message to process, with "ms" milliseconds of delay
694    between each character. This is needed when sending the logon
695    script to ICC, which for some reason doesn't like the
696    instantaneous send. */
697 int
698 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
699 {
700     ChildProc *cp = (ChildProc *) pr;
701     int outCount = 0;
702     int r;
703
704     while (count--) {
705         r = write(cp->fdTo, message++, 1);
706         if (r == -1) {
707             *outError = errno;
708             return outCount;
709         }
710         ++outCount;
711         if (msdelay >= 0)
712           TimeDelay(msdelay);
713     }
714
715     return outCount;
716 }
717
718 int
719 ICSInitScript ()
720 {
721   /* try to open the icsLogon script, either in the location given
722    * or in the users HOME directory
723    */
724
725   FILE *f;
726   char buf[MSG_SIZ];
727   char *homedir;
728
729   f = fopen(appData.icsLogon, "r");
730   if (f == NULL)
731     {
732       homedir = getenv("HOME");
733       if (homedir != NULL)
734         {
735           safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
736           strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
737           strncat(buf, appData.icsLogon,  MSG_SIZ - strlen(buf) - 1);
738           f = fopen(buf, "r");
739         }
740     }
741
742   if (f != NULL) {
743     ProcessICSInitScript(f);
744     return TRUE;
745   } else
746     printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
747
748   return FALSE;
749 }
750
751 void
752 ResetFrontEnd ()
753 {
754     CommentPopDown();
755     TagsPopDown();
756     return;
757 }
758
759 #include <sys/ioctl.h>
760 static int
761 get_term_width ()
762 {
763     int fd, default_width;
764
765     fd = STDIN_FILENO;
766     default_width = 79; // this is FICS default anyway...
767
768 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
769     struct ttysize win;
770     if (!ioctl(fd, TIOCGSIZE, &win))
771         default_width = win.ts_cols;
772 #elif defined(TIOCGWINSZ)
773     struct winsize win;
774     if (!ioctl(fd, TIOCGWINSZ, &win))
775         default_width = win.ws_col;
776 #endif
777     return default_width;
778 }
779
780 void
781 update_ics_width ()
782 {
783   static int old_width = 0;
784   int new_width = get_term_width();
785
786   if (old_width != new_width)
787     ics_printf("set width %d\n", new_width);
788   old_width = new_width;
789 }
790
791 void
792 NotifyFrontendLogin ()
793 {
794     update_ics_width();
795 }