Overhaul kill code
[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 int pid;
501
502 static RETSIGTYPE
503 AlarmCallBack (int n)
504 {
505     kill(pid, SIGKILL); // kill forcefully
506     return;
507 }
508
509 void
510 DestroyChildProcess (ProcRef pr, int signalType)
511 {
512     ChildProc *cp = (ChildProc *) pr;
513
514     if (cp->kind != CPReal) return;
515     cp->kind = CPNone;
516     if (signalType & 1) {
517             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: for 9 hard-kill immediately
518     }
519     signal(SIGALRM, AlarmCallBack);
520     pid = cp->pid;
521     if(signalType & 4) alarm(1 + appData.delayAfterQuit); // [HGM] kill: schedule hard kill if so requested
522     /* Process is exiting either because of the kill or because of
523        a quit command sent by the backend; either way, wait for it to die.
524     */
525     wait((int *) 0);
526     alarm(0); // cancel alarm if still pending
527     close(cp->fdFrom);
528     close(cp->fdTo);
529 }
530
531 void
532 InterruptChildProcess (ProcRef pr)
533 {
534     ChildProc *cp = (ChildProc *) pr;
535
536     if (cp->kind != CPReal) return;
537     (void) kill(cp->pid, SIGINT); /* stop it thinking */
538 }
539
540 int
541 OpenTelnet (char *host, char *port, ProcRef *pr)
542 {
543     char cmdLine[MSG_SIZ];
544
545     if (port[0] == NULLCHAR) {
546       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
547     } else {
548       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
549     }
550     return StartChildProcess(cmdLine, "", pr);
551 }
552
553 int
554 OpenTCP (char *host, char *port, ProcRef *pr)
555 {
556 #if OMIT_SOCKETS
557     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
558 #else  /* !OMIT_SOCKETS */
559     struct addrinfo hints;
560     struct addrinfo *ais, *ai;
561     int error;
562     int s=0;
563     ChildProc *cp;
564
565     memset(&hints, 0, sizeof(hints));
566     hints.ai_family = AF_UNSPEC;
567     hints.ai_socktype = SOCK_STREAM;
568
569     error = getaddrinfo(host, port, &hints, &ais);
570     if (error != 0) {
571       /* a getaddrinfo error is not an errno, so can't return it */
572       fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
573               host, port, gai_strerror(error));
574       return ENOENT;
575     }
576
577     for (ai = ais; ai != NULL; ai = ai->ai_next) {
578       if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
579         error = errno;
580         continue;
581       }
582       if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
583         error = errno;
584         continue;
585       }
586       error = 0;
587       break;
588     }
589     freeaddrinfo(ais);
590
591     if (error != 0) {
592       return error;
593     }
594
595     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
596     cp->kind = CPSock;
597     cp->pid = 0;
598     cp->fdFrom = s;
599     cp->fdTo = s;
600     *pr = (ProcRef) cp;
601 #endif /* !OMIT_SOCKETS */
602
603     return 0;
604 }
605
606 int
607 OpenCommPort (char *name, ProcRef *pr)
608 {
609     int fd;
610     ChildProc *cp;
611
612     fd = open(name, 2, 0);
613     if (fd < 0) return errno;
614
615     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
616     cp->kind = CPComm;
617     cp->pid = 0;
618     cp->fdFrom = fd;
619     cp->fdTo = fd;
620     *pr = (ProcRef) cp;
621
622     return 0;
623 }
624
625 int
626 OpenLoopback (ProcRef *pr)
627 {
628     ChildProc *cp;
629     int to[2], from[2];
630
631     SetUpChildIO(to, from);
632
633     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
634     cp->kind = CPLoop;
635     cp->pid = 0;
636     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
637     cp->fdTo = to[1];
638     *pr = (ProcRef) cp;
639
640     return 0;
641 }
642
643 int
644 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
645 {
646     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
647     return -1;
648 }
649
650 int
651 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
652 {
653     static int line = 0;
654     ChildProc *cp = (ChildProc *) pr;
655     int outCount;
656
657     if (pr == NoProc)
658     {
659         if (appData.noJoin || !appData.useInternalWrap)
660             outCount = fwrite(message, 1, count, stdout);
661         else
662         {
663             int width = get_term_width();
664             int len = wrap(NULL, message, count, width, &line);
665             char *msg = malloc(len);
666             int dbgchk;
667
668             if (!msg)
669                 outCount = fwrite(message, 1, count, stdout);
670             else
671             {
672                 dbgchk = wrap(msg, message, count, width, &line);
673                 if (dbgchk != len && appData.debugMode)
674                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
675                 outCount = fwrite(msg, 1, dbgchk, stdout);
676                 free(msg);
677             }
678         }
679     }
680     else
681       outCount = write(cp->fdTo, message, count);
682
683     if (outCount == -1)
684       *outError = errno;
685     else
686       *outError = 0;
687
688     return outCount;
689 }
690
691 /* Output message to process, with "ms" milliseconds of delay
692    between each character. This is needed when sending the logon
693    script to ICC, which for some reason doesn't like the
694    instantaneous send. */
695 int
696 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
697 {
698     ChildProc *cp = (ChildProc *) pr;
699     int outCount = 0;
700     int r;
701
702     while (count--) {
703         r = write(cp->fdTo, message++, 1);
704         if (r == -1) {
705             *outError = errno;
706             return outCount;
707         }
708         ++outCount;
709         if (msdelay >= 0)
710           TimeDelay(msdelay);
711     }
712
713     return outCount;
714 }
715
716 int
717 ICSInitScript ()
718 {
719   /* try to open the icsLogon script, either in the location given
720    * or in the users HOME directory
721    */
722
723   FILE *f;
724   char buf[MSG_SIZ];
725   char *homedir;
726
727   f = fopen(appData.icsLogon, "r");
728   if (f == NULL)
729     {
730       homedir = getenv("HOME");
731       if (homedir != NULL)
732         {
733           safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
734           strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
735           strncat(buf, appData.icsLogon,  MSG_SIZ - strlen(buf) - 1);
736           f = fopen(buf, "r");
737         }
738     }
739
740   if (f != NULL) {
741     ProcessICSInitScript(f);
742     return TRUE;
743   } else
744     printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
745
746   return FALSE;
747 }
748
749 void
750 ResetFrontEnd ()
751 {
752     CommentPopDown();
753     TagsPopDown();
754     return;
755 }
756
757 #include <sys/ioctl.h>
758 static int
759 get_term_width ()
760 {
761     int fd, default_width;
762
763     fd = STDIN_FILENO;
764     default_width = 79; // this is FICS default anyway...
765
766 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
767     struct ttysize win;
768     if (!ioctl(fd, TIOCGSIZE, &win))
769         default_width = win.ts_cols;
770 #elif defined(TIOCGWINSZ)
771     struct winsize win;
772     if (!ioctl(fd, TIOCGWINSZ, &win))
773         default_width = win.ws_col;
774 #endif
775     return default_width;
776 }
777
778 void
779 update_ics_width ()
780 {
781   static int old_width = 0;
782   int new_width = get_term_width();
783
784   if (old_width != new_width)
785     ics_printf("set width %d\n", new_width);
786   old_width = new_width;
787 }
788
789 void
790 NotifyFrontendLogin ()
791 {
792     update_ics_width();
793 }