updated copyright for 2016
[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, 2015, 2016 Free
9  * Software Foundation, Inc.
10  *
11  * The following terms apply to Digital Equipment Corporation's copyright
12  * interest in XBoard:
13  * ------------------------------------------------------------------------
14  * All Rights Reserved
15  *
16  * Permission to use, copy, modify, and distribute this software and its
17  * documentation for any purpose and without fee is hereby granted,
18  * provided that the above copyright notice appear in all copies and that
19  * both that copyright notice and this permission notice appear in
20  * supporting documentation, and that the name of Digital not be
21  * used in advertising or publicity pertaining to distribution of the
22  * software without specific, written prior permission.
23  *
24  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
25  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
26  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
27  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
28  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
29  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30  * SOFTWARE.
31  * ------------------------------------------------------------------------
32  *
33  * The following terms apply to the enhanced version of XBoard
34  * distributed by the Free Software Foundation:
35  * ------------------------------------------------------------------------
36  *
37  * GNU XBoard is free software: you can redistribute it and/or modify
38  * it under the terms of the GNU General Public License as published by
39  * the Free Software Foundation, either version 3 of the License, or (at
40  * your option) any later version.
41  *
42  * GNU XBoard is distributed in the hope that it will be useful, but
43  * WITHOUT ANY WARRANTY; without even the implied warranty of
44  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
45  * General Public License for more details.
46  *
47  * You should have received a copy of the GNU General Public License
48  * along with this program. If not, see http://www.gnu.org/licenses/.  *
49  *
50  *------------------------------------------------------------------------
51  ** See the file ChangeLog for a revision history.  */
52
53 #include "config.h"
54
55 #include <stdio.h>
56 #include <ctype.h>
57 #include <signal.h>
58 #include <errno.h>
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <pwd.h>
62 #include <math.h>
63
64 #if !OMIT_SOCKETS
65 # if HAVE_SYS_SOCKET_H
66 #  include <sys/socket.h>
67 #  include <netinet/in.h>
68 #  include <netdb.h>
69 # else /* not HAVE_SYS_SOCKET_H */
70 #  if HAVE_LAN_SOCKET_H
71 #   include <lan/socket.h>
72 #   include <lan/in.h>
73 #   include <lan/netdb.h>
74 #  else /* not HAVE_LAN_SOCKET_H */
75 #   define OMIT_SOCKETS 1
76 #  endif /* not HAVE_LAN_SOCKET_H */
77 # endif /* not HAVE_SYS_SOCKET_H */
78 #endif /* !OMIT_SOCKETS */
79
80 #if STDC_HEADERS
81 # include <stdlib.h>
82 # include <string.h>
83 #else /* not STDC_HEADERS */
84 extern char *getenv();
85 # if HAVE_STRING_H
86 #  include <string.h>
87 # else /* not HAVE_STRING_H */
88 #  include <strings.h>
89 # endif /* not HAVE_STRING_H */
90 #endif /* not STDC_HEADERS */
91
92 #if HAVE_SYS_FCNTL_H
93 # include <sys/fcntl.h>
94 #else /* not HAVE_SYS_FCNTL_H */
95 # if HAVE_FCNTL_H
96 #  include <fcntl.h>
97 # endif /* HAVE_FCNTL_H */
98 #endif /* not HAVE_SYS_FCNTL_H */
99
100 #if HAVE_SYS_SYSTEMINFO_H
101 # include <sys/systeminfo.h>
102 #endif /* HAVE_SYS_SYSTEMINFO_H */
103
104 #if TIME_WITH_SYS_TIME
105 # include <sys/time.h>
106 # include <time.h>
107 #else
108 # if HAVE_SYS_TIME_H
109 #  include <sys/time.h>
110 # else
111 #  include <time.h>
112 # endif
113 #endif
114
115 #if HAVE_UNISTD_H
116 # include <unistd.h>
117 #endif
118
119 #if HAVE_SYS_WAIT_H
120 # include <sys/wait.h>
121 #endif
122
123 #if HAVE_DIRENT_H
124 # include <dirent.h>
125 # define NAMLEN(dirent) strlen((dirent)->d_name)
126 # define HAVE_DIR_STRUCT
127 #else
128 # define dirent direct
129 # define NAMLEN(dirent) (dirent)->d_namlen
130 # if HAVE_SYS_NDIR_H
131 #  include <sys/ndir.h>
132 #  define HAVE_DIR_STRUCT
133 # endif
134 # if HAVE_SYS_DIR_H
135 #  include <sys/dir.h>
136 #  define HAVE_DIR_STRUCT
137 # endif
138 # if HAVE_NDIR_H
139 #  include <ndir.h>
140 #  define HAVE_DIR_STRUCT
141 # endif
142 #endif
143
144 #if ENABLE_NLS
145 #include <locale.h>
146 #endif
147
148 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
149 #include "common.h"
150
151 #include "frontend.h"
152 #include "backend.h"
153 #include "childio.h"
154 #include "menus.h"
155 #include "usystem.h"
156 #include "gettext.h"
157
158
159 #ifdef __EMX__
160 #ifndef HAVE_USLEEP
161 #define HAVE_USLEEP
162 #endif
163 #define usleep(t)   _sleep2(((t)+500)/1000)
164 #endif
165
166 #ifdef ENABLE_NLS
167 # define  _(s) gettext (s)
168 # define N_(s) gettext_noop (s)
169 #else
170 # define  _(s) (s)
171 # define N_(s)  s
172 #endif
173
174 static int get_term_width P(());
175
176 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
177                              "magenta", "cyan", "white" };
178 TextColors textColors[(int)NColorClasses];
179
180 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
181 static int
182 parse_color (char *str, int which)
183 {
184     char *p, buf[100], *d;
185     int i;
186
187     if (strlen(str) > 99)       /* watch bounds on buf */
188       return -1;
189
190     p = str;
191     d = buf;
192     for (i=0; i<which; ++i) {
193         p = strchr(p, ',');
194         if (!p)
195           return -1;
196         ++p;
197     }
198
199     /* Could be looking at something like:
200        black, , 1
201        .. in which case we want to stop on a comma also */
202     while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
203       ++p;
204
205     if (*p == ',') {
206         return -1;              /* Use default for empty field */
207     }
208
209     if (which == 2 || isdigit(*p))
210       return atoi(p);
211
212     while (*p && isalpha(*p))
213       *(d++) = *(p++);
214
215     *d = 0;
216
217     for (i=0; i<8; ++i) {
218         if (!StrCaseCmp(buf, cnames[i]))
219           return which? (i+40) : (i+30);
220     }
221     if (!StrCaseCmp(buf, "default")) return -1;
222
223     fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
224     return -2;
225 }
226
227 static int
228 parse_cpair (ColorClass cc, char *str)
229 {
230     if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
231         fprintf(stderr, _("%s: can't parse foreground color in '%s'\n"),
232                 programName, str);
233         return -1;
234     }
235
236     /* bg and attr are optional */
237     textColors[(int)cc].bg = parse_color(str, 1);
238     if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
239         textColors[(int)cc].attr = 0;
240     }
241     return 0;
242 }
243
244 void
245 ParseIcsTextColors ()
246 {   // [HGM] tken out of main(), so it can be called from ICS-Options dialog
247     if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
248         parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
249         parse_cpair(ColorChannel1, appData.colorChannel1) < 0  ||
250         parse_cpair(ColorChannel, appData.colorChannel) < 0  ||
251         parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
252         parse_cpair(ColorTell, appData.colorTell) < 0 ||
253         parse_cpair(ColorChallenge, appData.colorChallenge) < 0  ||
254         parse_cpair(ColorRequest, appData.colorRequest) < 0  ||
255         parse_cpair(ColorSeek, appData.colorSeek) < 0  ||
256         parse_cpair(ColorNormal, appData.colorNormal) < 0)
257       {
258           if (appData.colorize) {
259               fprintf(stderr,
260                       _("%s: can't parse color names; disabling colorization\n"),
261                       programName);
262           }
263           appData.colorize = FALSE;
264       }
265     textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
266     textColors[ColorNone].attr = 0;
267     SetTextColor(cnames, textColors[ColorNormal].fg - 30, textColors[ColorNormal].bg - 40, -2); // kludge to announce background color to front-end 
268 }
269
270 static Boolean noEcho;
271
272 void
273 EchoOn ()
274 {
275     system("stty echo");
276     noEcho = False;
277 }
278
279 void
280 EchoOff ()
281 {
282     system("stty -echo");
283     noEcho = True;
284 }
285
286 char *oldICSInteractionTitle;
287
288 void
289 ShutDownFrontEnd ()
290 {
291     if (appData.icsActive && oldICSInteractionTitle != NULL) {
292         DisplayIcsInteractionTitle(oldICSInteractionTitle);
293     }
294     if (saveSettingsOnExit) SaveSettings(settingsFileName);
295     unlink(gameCopyFilename);
296     unlink(gamePasteFilename);
297     if(noEcho) EchoOn();
298 }
299
300 void
301 RunCommand (char *buf)
302 {
303     system(buf);
304 }
305
306 void
307 Colorize (ColorClass cc, int continuation)
308 {
309     char buf[MSG_SIZ];
310     int count, outCount, error;
311
312     SetTextColor(cnames, textColors[(int)cc].fg - 30, textColors[(int)cc].bg - 40, textColors[(int)cc].attr); // for GTK widget
313
314     if (textColors[(int)cc].bg > 0) {
315         if (textColors[(int)cc].fg > 0) {
316           snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
317                    textColors[(int)cc].fg, textColors[(int)cc].bg);
318         } else {
319           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
320                    textColors[(int)cc].bg);
321         }
322     } else {
323         if (textColors[(int)cc].fg > 0) {
324           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
325                     textColors[(int)cc].fg);
326         } else {
327           snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
328         }
329     }
330     count = strlen(buf);
331     outCount = OutputToProcess(NoProc, buf, count, &error);
332     if (outCount < count) {
333         DisplayFatalError(_("Error writing to display"), error, 1);
334     }
335
336     if (continuation) return;
337     PlaySoundForColor(cc);
338 }
339
340 char *
341 UserName ()
342 {
343     return getpwuid(getuid())->pw_name;
344 }
345
346 char *
347 ExpandPathName (char *path)
348 {
349     static char static_buf[4*MSG_SIZ];
350     char *d, *s, buf[4*MSG_SIZ];
351     struct passwd *pwd;
352
353     s = path;
354     d = static_buf;
355
356     while (*s && isspace(*s))
357       ++s;
358
359     if (!*s) {
360         *d = 0;
361         return static_buf;
362     }
363
364     if (*s == '~') {
365         if(s[1] == '~') { // use ~~ for XBoard's private data directory
366           snprintf(d, 4*MSG_SIZ, DATADIR "%s", s+2);
367         } else
368         if (*(s+1) == '/') {
369           safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
370           strcat(d, s+1);
371         }
372         else {
373           safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
374           { char *p; if(p = strchr(buf, '/')) *p = 0; }
375           pwd = getpwnam(buf);
376           if (!pwd)
377             {
378               fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
379                       buf, path);
380               return NULL;
381             }
382           safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
383           strcat(d, strchr(s+1, '/'));
384         }
385     }
386     else
387       safeStrCpy(d, s, 4*MSG_SIZ );
388
389     return static_buf;
390 }
391
392 int
393 MySearchPath (char *installDir, char *name, char *fullname)
394 { // just append installDir and name. Perhaps ExpandPath should be used here?
395   name = ExpandPathName(name);
396   if(name && name[0] == '/')
397     safeStrCpy(fullname, name, MSG_SIZ );
398   else {
399     sprintf(fullname, "%s%c%s", installDir, '/', name);
400   }
401   return 1;
402 }
403
404 int
405 MyGetFullPathName (char *name, char *fullname)
406 { // should use ExpandPath?
407   name = ExpandPathName(name);
408   safeStrCpy(fullname, name, MSG_SIZ );
409   return 1;
410 }
411
412 char *
413 HostName ()
414 {
415     static char host_name[MSG_SIZ];
416
417 #if HAVE_GETHOSTNAME
418     gethostname(host_name, MSG_SIZ);
419     return host_name;
420 #else  /* not HAVE_GETHOSTNAME */
421 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
422     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
423     return host_name;
424 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
425     return "localhost";
426 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
427 #endif /* not HAVE_GETHOSTNAME */
428 }
429
430
431 int
432 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
433 {
434     char *argv[64], *p;
435     int i, pid;
436     int to_prog[2], from_prog[2];
437     ChildProc *cp;
438     char buf[MSG_SIZ];
439
440     if (appData.debugMode) {
441         fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
442     }
443
444     /* We do NOT feed the cmdLine to the shell; we just
445        parse it into blank-separated arguments in the
446        most simple-minded way possible.
447        */
448     i = 0;
449     safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
450     p = buf;
451     for (;;) {
452         while(*p == ' ') p++;
453         argv[i++] = p;
454         if(*p == '"' || *p == '\'')
455              p = strchr(++argv[i-1], *p);
456         else p = strchr(p, ' ');
457         if (p == NULL) break;
458         *p++ = NULLCHAR;
459     }
460     argv[i] = NULL;
461
462     SetUpChildIO(to_prog, from_prog);
463
464     if ((pid = fork()) == 0) {
465         /* Child process */
466         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
467         close(to_prog[1]);     // first close the unused pipe ends
468         close(from_prog[0]);
469         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
470         dup2(from_prog[1], 1);
471         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
472         close(from_prog[1]);                   // and closing again loses one of the pipes!
473         if(fileno(stderr) >= 2) // better safe than sorry...
474                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
475
476         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
477             perror(dir);
478             exit(1);
479         }
480
481         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
482
483         execvp(argv[0], argv);
484
485         /* If we get here, exec failed */
486         perror(argv[0]);
487         exit(1);
488     }
489
490     /* Parent process */
491     close(to_prog[0]);
492     close(from_prog[1]);
493
494     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
495     cp->kind = CPReal;
496     cp->pid = pid;
497     cp->fdFrom = from_prog[0];
498     cp->fdTo = to_prog[1];
499     *pr = (ProcRef) cp;
500     return 0;
501 }
502
503 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
504 static int pid;
505
506 static RETSIGTYPE
507 AlarmCallBack (int n)
508 {
509     kill(pid, SIGKILL); // kill forcefully
510     return;
511 }
512
513 void
514 DestroyChildProcess (ProcRef pr, int signalType)
515 {
516     ChildProc *cp = (ChildProc *) pr;
517
518     if (cp->kind != CPReal) return;
519     cp->kind = CPNone;
520     if (signalType & 1) {
521             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: for 9 hard-kill immediately
522     }
523     signal(SIGALRM, AlarmCallBack);
524     pid = cp->pid;
525     if(signalType & 4) alarm(1 + appData.delayAfterQuit); // [HGM] kill: schedule hard kill if so requested
526     /* Process is exiting either because of the kill or because of
527        a quit command sent by the backend; either way, wait for it to die.
528     */
529     wait((int *) 0);
530     alarm(0); // cancel alarm if still pending
531     close(cp->fdFrom);
532     close(cp->fdTo);
533 }
534
535 void
536 InterruptChildProcess (ProcRef pr)
537 {
538     ChildProc *cp = (ChildProc *) pr;
539
540     if (cp->kind != CPReal) return;
541     (void) kill(cp->pid, SIGINT); /* stop it thinking */
542 }
543
544 int
545 OpenTelnet (char *host, char *port, ProcRef *pr)
546 {
547     char cmdLine[MSG_SIZ];
548
549     if (port[0] == NULLCHAR) {
550       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
551     } else {
552       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
553     }
554     return StartChildProcess(cmdLine, "", pr);
555 }
556
557 int
558 OpenTCP (char *host, char *port, ProcRef *pr)
559 {
560 #if OMIT_SOCKETS
561     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
562 #else  /* !OMIT_SOCKETS */
563     struct addrinfo hints;
564     struct addrinfo *ais, *ai;
565     int error;
566     int s=0;
567     ChildProc *cp;
568
569     memset(&hints, 0, sizeof(hints));
570     hints.ai_family = AF_UNSPEC;
571     hints.ai_socktype = SOCK_STREAM;
572
573     error = getaddrinfo(host, port, &hints, &ais);
574     if (error != 0) {
575       /* a getaddrinfo error is not an errno, so can't return it */
576       fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
577               host, port, gai_strerror(error));
578       return ENOENT;
579     }
580
581     for (ai = ais; ai != NULL; ai = ai->ai_next) {
582       if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
583         error = errno;
584         continue;
585       }
586       if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
587         error = errno;
588         continue;
589       }
590       error = 0;
591       break;
592     }
593     freeaddrinfo(ais);
594
595     if (error != 0) {
596       return error;
597     }
598
599     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
600     cp->kind = CPSock;
601     cp->pid = 0;
602     cp->fdFrom = s;
603     cp->fdTo = s;
604     *pr = (ProcRef) cp;
605 #endif /* !OMIT_SOCKETS */
606
607     return 0;
608 }
609
610 int
611 OpenCommPort (char *name, ProcRef *pr)
612 {
613     int fd;
614     ChildProc *cp;
615
616     fd = open(name, 2, 0);
617     if (fd < 0) return errno;
618
619     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
620     cp->kind = CPComm;
621     cp->pid = 0;
622     cp->fdFrom = fd;
623     cp->fdTo = fd;
624     *pr = (ProcRef) cp;
625
626     return 0;
627 }
628
629 int
630 OpenLoopback (ProcRef *pr)
631 {
632     ChildProc *cp;
633     int to[2], from[2];
634
635     SetUpChildIO(to, from);
636
637     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
638     cp->kind = CPLoop;
639     cp->pid = 0;
640     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
641     cp->fdTo = to[1];
642     *pr = (ProcRef) cp;
643
644     return 0;
645 }
646
647 int
648 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
649 {
650     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
651     return -1;
652 }
653
654 Boolean stdoutClosed = FALSE;
655
656 int
657 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
658 {
659     static int line = 0;
660     ChildProc *cp = (ChildProc *) pr;
661     int outCount = count;
662
663     if (pr == NoProc)
664     {
665         if (appData.noJoin || !appData.useInternalWrap) {
666             if(!stdoutClosed) outCount = fwrite(message, 1, count, stdout);
667         } else
668         {
669             int width = get_term_width();
670             int len = wrap(NULL, message, count, width, &line);
671             char *msg = malloc(len);
672             int dbgchk;
673
674             if (!msg)
675                 outCount = fwrite(message, 1, count, stdout);
676             else
677             {
678                 dbgchk = wrap(msg, message, count, width, &line);
679                 if (dbgchk != len && appData.debugMode)
680                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
681                 outCount = fwrite(msg, 1, dbgchk, stdout);
682                 free(msg);
683             }
684         }
685         if(*message != '\033') ConsoleWrite(message, count);
686     }
687     else
688       outCount = write(cp->fdTo, message, count);
689
690     if (outCount == -1)
691       *outError = errno;
692     else
693       *outError = 0;
694
695     return outCount;
696 }
697
698 /* Output message to process, with "ms" milliseconds of delay
699    between each character. This is needed when sending the logon
700    script to ICC, which for some reason doesn't like the
701    instantaneous send. */
702 int
703 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
704 {
705     ChildProc *cp = (ChildProc *) pr;
706     int outCount = 0;
707     int r;
708
709     while (count--) {
710         r = write(cp->fdTo, message++, 1);
711         if (r == -1) {
712             *outError = errno;
713             return outCount;
714         }
715         ++outCount;
716         if (msdelay >= 0)
717           TimeDelay(msdelay);
718     }
719
720     return outCount;
721 }
722
723 int
724 ICSInitScript ()
725 {
726   /* try to open the icsLogon script, either in the location given
727    * or in the users HOME directory
728    */
729
730   FILE *f;
731   char buf[MSG_SIZ];
732   char *homedir;
733
734   f = fopen(appData.icsLogon, "r");
735   if (f == NULL)
736     {
737       homedir = getenv("HOME");
738       if (homedir != NULL)
739         {
740           safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
741           strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
742           strncat(buf, appData.icsLogon,  MSG_SIZ - strlen(buf) - 1);
743           f = fopen(buf, "r");
744         }
745     }
746
747   if (f != NULL) {
748     ProcessICSInitScript(f);
749     return TRUE;
750   } else
751     printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
752
753   return FALSE;
754 }
755
756 void
757 ResetFrontEnd ()
758 {
759     CommentPopDown();
760     TagsPopDown();
761     return;
762 }
763
764 #include <sys/ioctl.h>
765 static int
766 get_term_width ()
767 {
768     int fd, default_width;
769
770     fd = STDIN_FILENO;
771     default_width = 79; // this is FICS default anyway...
772
773 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
774     struct ttysize win;
775     if (!ioctl(fd, TIOCGSIZE, &win))
776         default_width = win.ts_cols;
777 #elif defined(TIOCGWINSZ)
778     struct winsize win;
779     if (!ioctl(fd, TIOCGWINSZ, &win))
780         default_width = win.ws_col;
781 #endif
782     return default_width;
783 }
784
785 void
786 update_ics_width ()
787 {
788   static int old_width = 0;
789   int new_width = get_term_width();
790
791   if (old_width != new_width)
792     ics_printf("set width %d\n", new_width);
793   old_width = new_width;
794 }
795
796 void
797 NotifyFrontendLogin ()
798 {
799     update_ics_width();
800 }