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