irc.c - irc - IRC client based on c9x.me/irc client
(HTM) git clone git://git.codemadness.org/irc
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
irc.c (17965B)
---
1 #include <assert.h>
2 #include <limits.h>
3 #include <signal.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <stdarg.h>
7 #include <string.h>
8 #include <time.h>
9 #include <errno.h>
10
11 #include <curses.h>
12 #include <unistd.h>
13 #include <arpa/inet.h>
14 #include <sys/types.h>
15 #include <sys/socket.h>
16 #include <sys/select.h>
17 #include <sys/ioctl.h>
18 #include <netinet/in.h>
19 #include <netinet/tcp.h>
20 #include <netdb.h>
21 #include <locale.h>
22 #include <wchar.h>
23
24 #include <tls.h>
25
26 #ifndef __OpenBSD__
27 #define pledge(a,b) 0
28 #endif
29
30 #undef CTRL
31 #define CTRL(x) (x & 037)
32
33 #define SCROLL 15
34 #define INDENT 23
35 #define DATEFMT "%H:%M"
36 #define PFMT " %12s : %s"
37 #define PFMTHIGH "> %12s : %s"
38 #define SRV "irc.oftc.net"
39 #define PORT "6667"
40
41 enum {
42 ChanLen = 64,
43 LineLen = 512,
44 MaxChans = 16,
45 BufSz = 2048,
46 LogSz = 4096,
47 MaxRecons = 10, /* -1 for infinitely many */
48 UtfSz = 4,
49 RuneInvalid = 0xFFFD,
50 };
51
52 typedef wchar_t Rune;
53
54 static struct {
55 int x;
56 int y;
57 WINDOW *sw, *mw, *iw;
58 } scr;
59
60 static struct Chan {
61 char name[ChanLen];
62 char *buf, *eol;
63 int n; /* Scroll offset. */
64 size_t sz; /* Size of buf. */
65 char high; /* Nick highlight. */
66 char new; /* New message. */
67 char join; /* Channel was 'j'-oined. */
68 } chl[MaxChans];
69
70 static int ssl;
71 static struct {
72 int fd;
73 struct tls *tls;
74 } srv;
75 static char nick[64];
76 static int quit, winchg;
77 static int nch, ch; /* Current number of channels, and current channel. */
78 static char outb[BufSz], *outp = outb; /* Output buffer. */
79 static FILE *logfp;
80
81 static unsigned char utfbyte[UtfSz + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
82 static unsigned char utfmask[UtfSz + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
83 static Rune utfmin[UtfSz + 1] = { 0, 0, 0x80, 0x800, 0x10000};
84 static Rune utfmax[UtfSz + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
85
86 static void scmd(char *, char *, char *, char *);
87 static void tdrawbar(void);
88 static void tredraw(void);
89 static void treset(void);
90
91 static void
92 panic(const char *m)
93 {
94 treset();
95 fprintf(stderr, "Panic: %s\n", m);
96 exit(1);
97 }
98
99 static size_t
100 utf8validate(Rune *u, size_t i)
101 {
102 if (*u < utfmin[i] || *u > utfmax[i] || (0xD800 <= *u && *u <= 0xDFFF))
103 *u = RuneInvalid;
104 for (i = 1; *u > utfmax[i]; ++i)
105 ;
106 return i;
107 }
108
109 static Rune
110 utf8decodebyte(unsigned char c, size_t *i)
111 {
112 for (*i = 0; *i < UtfSz + 1; ++(*i))
113 if ((c & utfmask[*i]) == utfbyte[*i])
114 return c & ~utfmask[*i];
115 return 0;
116 }
117
118 static size_t
119 utf8decode(char *c, Rune *u, size_t clen)
120 {
121 size_t i, j, len, type;
122 Rune udecoded;
123
124 *u = RuneInvalid;
125 if (!clen)
126 return 0;
127 udecoded = utf8decodebyte(c[0], &len);
128 if (len < 1 || len > UtfSz)
129 return 1;
130 for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
131 udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
132 if (type != 0)
133 return j;
134 }
135 if (j < len)
136 return 0;
137 *u = udecoded;
138 utf8validate(u, len);
139 return len;
140 }
141
142 static char
143 utf8encodebyte(Rune u, size_t i)
144 {
145 return utfbyte[i] | (u & ~utfmask[i]);
146 }
147
148 static size_t
149 utf8encode(Rune u, char *c)
150 {
151 size_t len, i;
152
153 len = utf8validate(&u, 0);
154 if (len > UtfSz)
155 return 0;
156 for (i = len - 1; i != 0; --i) {
157 c[i] = utf8encodebyte(u, 0);
158 u >>= 6;
159 }
160 c[0] = utf8encodebyte(u, len);
161 return len;
162 }
163
164 static void
165 sndf(const char *fmt, ...)
166 {
167 va_list vl;
168 size_t n, l = BufSz - (outp - outb);
169
170 if (l < 2)
171 return;
172 va_start(vl, fmt);
173 n = vsnprintf(outp, l - 2, fmt, vl);
174 va_end(vl);
175 outp += n > l - 2 ? l - 2 : n;
176 *outp++ = '\r';
177 *outp++ = '\n';
178 }
179
180 static int
181 srd(void)
182 {
183 static char l[BufSz], *p = l;
184 char *s, *usr, *cmd, *par, *data;
185 int rd;
186
187 if (p - l >= BufSz)
188 p = l; /* Input buffer overflow, there should something better to do. */
189 if (ssl)
190 rd = tls_read(srv.tls, p, BufSz - (p - l));
191 else
192 rd = read(srv.fd, p, BufSz - (p - l));
193 if (rd <= 0)
194 return 0;
195 p += rd;
196 for (;;) { /* Cycle on all received lines. */
197 if (!(s = memchr(l, '\n', p - l)))
198 return 1;
199 if (s > l && s[-1] == '\r')
200 s[-1] = 0;
201 *s++ = 0;
202 if (*l == ':') {
203 if (!(cmd = strchr(l, ' ')))
204 goto lskip;
205 *cmd++ = 0;
206 usr = l + 1;
207 } else {
208 usr = 0;
209 cmd = l;
210 }
211 if (!(par = strchr(cmd, ' ')))
212 goto lskip;
213 *par++ = 0;
214 if ((data = strchr(par, ':')))
215 *data++ = 0;
216 scmd(usr, cmd, par, data);
217 lskip:
218 memmove(l, s, p - s);
219 p -= s - l;
220 }
221 }
222
223 static void
224 sinit(const char *key, const char *nick, const char *user)
225 {
226 if (key)
227 sndf("PASS %s", key);
228 sndf("NICK %s", nick);
229 sndf("USER %s 8 * :%s", user, user);
230 sndf("MODE %s +i", nick);
231 }
232
233 static char *
234 dial(const char *host, const char *service)
235 {
236 struct addrinfo hints, *res = NULL, *rp;
237 char *err = 0;
238 int fd = -1, e;
239
240 memset(&hints, 0, sizeof(hints));
241 hints.ai_family = AF_UNSPEC; /* allow IPv4 or IPv6 */
242 hints.ai_flags = AI_NUMERICSERV; /* avoid name lookup for port */
243 hints.ai_socktype = SOCK_STREAM;
244 if ((e = getaddrinfo(host, service, &hints, &res)))
245 return "Getaddrinfo failed.";
246 for (rp = res; rp; rp = rp->ai_next) {
247 if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1)
248 continue;
249 if (connect(fd, res->ai_addr, res->ai_addrlen) == -1) {
250 close(fd);
251 continue;
252 }
253 break;
254 }
255 if (fd == -1) {
256 err = "Cannot connect to host.";
257 goto fail;
258 }
259 srv.fd = fd;
260 if (ssl) {
261 if (tls_init() < 0) {
262 err = "Could not initialize TLS.";
263 goto fail;
264 }
265 if (!(srv.tls = tls_client())) {
266 err = "Could not initialize TLS context.";
267 goto fail;
268 }
269 if (tls_connect_socket(srv.tls, srv.fd, host) < 0) {
270 err = "Could not connect with ssl.";
271 goto fail;
272 }
273 }
274 fail:
275 freeaddrinfo(res);
276 return err;
277 }
278
279 static void
280 hangup(void)
281 {
282 if (srv.tls) {
283 tls_close(srv.tls);
284 srv.tls = 0;
285 }
286 if (srv.fd) {
287 close(srv.fd);
288 srv.fd = 0;
289 }
290 }
291
292 static inline int
293 chfind(const char *name)
294 {
295 int i;
296
297 assert(name);
298 for (i = nch - 1; i > 0; i--)
299 if (!strcmp(chl[i].name, name))
300 break;
301 return i;
302 }
303
304 static int
305 chadd(const char *name, int joined)
306 {
307 int n;
308
309 if (nch >= MaxChans || strlen(name) >= ChanLen)
310 return -1;
311 if ((n = chfind(name)) > 0)
312 return n;
313 strcpy(chl[nch].name, name);
314 chl[nch].sz = LogSz;
315 chl[nch].buf = malloc(LogSz);
316 if (!chl[nch].buf)
317 panic("Out of memory.");
318 chl[nch].eol = chl[nch].buf;
319 chl[nch].n = 0;
320 chl[nch].join = joined;
321 if (joined)
322 ch = nch;
323 nch++;
324 tdrawbar();
325 return nch;
326 }
327
328 static int
329 chdel(char *name)
330 {
331 int n;
332
333 if (!(n = chfind(name)))
334 return 0;
335 nch--;
336 free(chl[n].buf);
337 memmove(&chl[n], &chl[n + 1], (nch - n) * sizeof(struct Chan));
338 ch = nch - 1;
339 tdrawbar();
340 return 1;
341 }
342
343 static char *
344 pushl(char *p, char *e)
345 {
346 int x, cl;
347 char *w;
348 Rune u[2];
349 cchar_t cc;
350
351 u[1] = 0;
352 if ((w = memchr(p, '\n', e - p)))
353 e = w + 1;
354 w = p;
355 x = 0;
356 for (;;) {
357 if (x >= scr.x) {
358 waddch(scr.mw, '\n');
359 for (x = 0; x < INDENT; x++)
360 waddch(scr.mw, ' ');
361 if (*w == ' ')
362 w++;
363 x += p - w;
364 }
365 if (p >= e || *p == ' ' || p - w + INDENT >= scr.x - 1) {
366 while (w < p) {
367 w += utf8decode(w, u, UtfSz);
368 if (wcwidth(*u) > 0 || *u == '\n') {
369 setcchar(&cc, u, 0, 0, 0);
370 wadd_wch(scr.mw, &cc);
371 }
372 }
373 if (p >= e)
374 return e;
375 }
376 p += utf8decode(p, u, UtfSz);
377 if ((cl = wcwidth(*u)) >= 0)
378 x += cl;
379 }
380 }
381
382 static void
383 pushf(int cn, const char *fmt, ...)
384 {
385 struct Chan *const c = &chl[cn];
386 size_t n, blen = c->eol - c->buf;
387 va_list vl;
388 time_t t;
389 char *s;
390 struct tm *tm, *gmtm;
391
392 if (blen + LineLen >= c->sz) {
393 c->sz *= 2;
394 c->buf = realloc(c->buf, c->sz);
395 if (!c->buf)
396 panic("Out of memory.");
397 c->eol = c->buf + blen;
398 }
399 t = time(0);
400 if (!(tm = localtime(&t)))
401 panic("Localtime failed.");
402 n = strftime(c->eol, LineLen, DATEFMT, tm);
403 if (!(gmtm = gmtime(&t)))
404 panic("Gmtime failed.");
405 c->eol[n++] = ' ';
406 va_start(vl, fmt);
407 s = c->eol + n;
408 n += vsnprintf(s, LineLen - n - 1, fmt, vl);
409 va_end(vl);
410
411 if (logfp) {
412 fprintf(logfp, "%-12.12s\t%04d-%02d-%02dT%02d:%02d:%02dZ\t%s\n",
413 c->name,
414 gmtm->tm_year + 1900, gmtm->tm_mon + 1, gmtm->tm_mday,
415 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec, s);
416 fflush(logfp);
417 }
418
419 strcat(c->eol, "\n");
420 if (n >= LineLen - 1)
421 c->eol += LineLen - 1;
422 else
423 c->eol += n + 1;
424 if (cn == ch && c->n == 0) {
425 char *p = c->eol - n - 1;
426
427 if (p != c->buf)
428 waddch(scr.mw, '\n');
429 pushl(p, c->eol - 1);
430 wrefresh(scr.mw);
431 }
432 }
433
434 static void
435 scmd(char *usr, char *cmd, char *par, char *data)
436 {
437 int s, c;
438 char *pm = strtok(par, " "), *chan;
439
440 if (!usr)
441 usr = "?";
442 else {
443 char *bang = strchr(usr, '!');
444 if (bang)
445 *bang = 0;
446 }
447 if (!strcmp(cmd, "PRIVMSG")) {
448 if (!pm || !data)
449 return;
450 if (strchr("&#!+.~", pm[0]))
451 chan = pm;
452 else
453 chan = usr;
454 if (!(c = chfind(chan))) {
455 if (chadd(chan, 0) < 0)
456 return;
457 tredraw();
458 }
459 c = chfind(chan);
460 if (strcasestr(data, nick)) {
461 pushf(c, PFMTHIGH, usr, data);
462 chl[c].high |= ch != c;
463 } else
464 pushf(c, PFMT, usr, data);
465 if (ch != c) {
466 chl[c].new = 1;
467 tdrawbar();
468 }
469 } else if (!strcmp(cmd, "PING")) {
470 sndf("PONG :%s", data ? data : "(null)");
471 } else if (!strcmp(cmd, "PART")) {
472 if (!pm)
473 return;
474 pushf(chfind(pm), "-!- %s has left %s", usr, pm);
475 } else if (!strcmp(cmd, "JOIN")) {
476 if (!pm)
477 return;
478 pushf(chfind(pm), "-!- %s has joined %s", usr, pm);
479 } else if (!strcmp(cmd, "470")) { /* Channel forwarding. */
480 char *ch = strtok(0, " "), *fch = strtok(0, " ");
481
482 if (!ch || !fch || !(s = chfind(ch)))
483 return;
484 chl[s].name[0] = 0;
485 strncat(chl[s].name, fch, ChanLen - 1);
486 tdrawbar();
487 } else if (!strcmp(cmd, "471") || !strcmp(cmd, "473")
488 || !strcmp(cmd, "474") || !strcmp(cmd, "475")) { /* Join error. */
489 if ((pm = strtok(0, " "))) {
490 chdel(pm);
491 pushf(0, "-!- Cannot join channel %s (%s)", pm, cmd);
492 tredraw();
493 }
494 } else if (!strcmp(cmd, "QUIT")) { /* Commands we don't care about. */
495 return;
496 } else if (!strcmp(cmd, "NOTICE") || !strcmp(cmd, "375")
497 || !strcmp(cmd, "372") || !strcmp(cmd, "376")) {
498 pushf(0, "%s", data ? data : "");
499 } else
500 pushf(0, "%s - %s %s", cmd, par, data ? data : "(null)");
501 }
502
503 static void
504 uparse(char *m)
505 {
506 char *p = m;
507
508 if (p[0] != '/') {
509 pmsg:
510 if (ch == 0)
511 return;
512 m += strspn(m, " ");
513 if (!*m)
514 return;
515 pushf(ch, PFMT, nick, m);
516 sndf("PRIVMSG %s :%s", chl[ch].name, m);
517 return;
518 }
519 switch (*(++p)) {
520 case 'j': /* Join channels. */
521 p += 1 + (p[1] == ' ');
522 p = strtok(p, " ");
523 while (p) {
524 if (chadd(p, 1) < 0)
525 break;
526 sndf("JOIN %s", p);
527 p = strtok(0, " ");
528 }
529 tredraw();
530 return;
531 case 'l': /* Leave channels. */
532 p += 1 + (p[1] == ' ');
533 if (!*p) {
534 if (ch == 0)
535 return; /* Cannot leave server window. */
536 strcat(p, chl[ch].name);
537 }
538 p = strtok(p, " ");
539 while (p) {
540 if (chdel(p))
541 sndf("PART %s", p);
542 p = strtok(0, " ");
543 }
544 tredraw();
545 return;
546 case 'm': /* Private message. */
547 m = p + 1 + (p[1] == ' ');
548 if (!(p = strchr(m, ' ')))
549 return;
550 *p++ = 0;
551 sndf("PRIVMSG %s :%s", m, p);
552 return;
553 case 'r': /* Send raw. */
554 if (p[1])
555 sndf("%s", &p[2]);
556 return;
557 case 'q': /* Quit. */
558 quit = 1;
559 return;
560 }
561 }
562
563 static void
564 sigwinch(int sig)
565 {
566 if (sig)
567 winchg = 1;
568 }
569
570 static void
571 tinit(void)
572 {
573 setlocale(LC_ALL, "");
574 signal(SIGWINCH, sigwinch);
575 initscr();
576 raw();
577 noecho();
578 getmaxyx(stdscr, scr.y, scr.x);
579 if (scr.y < 4)
580 panic("Screen too small.");
581 if ((scr.sw = newwin(1, scr.x, 0, 0)) == 0
582 || (scr.mw = newwin(scr.y - 2, scr.x, 1, 0)) == 0
583 || (scr.iw = newwin(1, scr.x, scr.y - 1, 0)) == 0)
584 panic("Cannot create windows.");
585 keypad(scr.iw, 1);
586 scrollok(scr.mw, 1);
587 if (has_colors() == TRUE) {
588 start_color();
589 use_default_colors();
590 init_pair(1, COLOR_WHITE, COLOR_BLUE);
591 wbkgd(scr.sw, COLOR_PAIR(1));
592 }
593 }
594
595 static void
596 tresize(void)
597 {
598 struct winsize ws;
599
600 winchg = 0;
601 if (ioctl(0, TIOCGWINSZ, &ws) < 0)
602 panic("Ioctl (TIOCGWINSZ) failed.");
603 if (ws.ws_row <= 2)
604 return;
605 resizeterm(scr.y = ws.ws_row, scr.x = ws.ws_col);
606 wresize(scr.mw, scr.y - 2, scr.x);
607 wresize(scr.iw, 1, scr.x);
608 wresize(scr.sw, 1, scr.x);
609 mvwin(scr.iw, scr.y - 1, 0);
610 tredraw();
611 tdrawbar();
612 }
613
614 static void
615 tredraw(void)
616 {
617 struct Chan *const c = &chl[ch];
618 char *q, *p;
619 int nl = -1;
620
621 if (c->eol == c->buf) {
622 wclear(scr.mw);
623 wrefresh(scr.mw);
624 return;
625 }
626 p = c->eol - 1;
627 if (c->n) {
628 int i = c->n;
629 for (; p > c->buf; p--)
630 if (*p == '\n' && !i--)
631 break;
632 if (p == c->buf)
633 c->n -= i;
634 }
635 q = p;
636 while (nl < scr.y - 2) {
637 while (*q != '\n' && q > c->buf)
638 q--;
639 nl++;
640 if (q == c->buf)
641 break;
642 q--;
643 }
644 if (q != c->buf)
645 q += 2;
646 wclear(scr.mw);
647 wmove(scr.mw, 0, 0);
648 while (q < p)
649 q = pushl(q, p);
650 wrefresh(scr.mw);
651 }
652
653 static void
654 tdrawbar(void)
655 {
656 size_t l;
657 int fst = ch;
658
659 for (l = 0; fst > 0 && l < scr.x / 2; fst--)
660 l += strlen(chl[fst].name) + 3;
661
662 werase(scr.sw);
663 for (l = 0; fst < nch && l < scr.x; fst++) {
664 char *p = chl[fst].name;
665 if (fst == ch)
666 wattron(scr.sw, A_BOLD);
667 waddch(scr.sw, '['), l++;
668 if (chl[fst].high)
669 waddch(scr.sw, '>'), l++;
670 else if (chl[fst].new)
671 waddch(scr.sw, '+'), l++;
672 for (; *p && l < scr.x; p++, l++)
673 waddch(scr.sw, *p);
674 if (l < scr.x - 1)
675 waddstr(scr.sw, "] "), l += 2;
676 if (fst == ch)
677 wattroff(scr.sw, A_BOLD);
678 }
679 wrefresh(scr.sw);
680 }
681
682 static void
683 tgetch(void)
684 {
685 static char l[BufSz];
686 static size_t shft, cu, len;
687 size_t dirty = len + 1, i;
688 int c;
689
690 c = wgetch(scr.iw);
691 switch (c) {
692 case CTRL('n'):
693 ch = (ch + 1) % nch;
694 chl[ch].high = chl[ch].new = 0;
695 tdrawbar();
696 tredraw();
697 return;
698 case CTRL('p'):
699 ch = (ch + nch - 1) % nch;
700 chl[ch].high = chl[ch].new = 0;
701 tdrawbar();
702 tredraw();
703 return;
704 case KEY_PPAGE:
705 chl[ch].n += SCROLL;
706 tredraw();
707 return;
708 case KEY_NPAGE:
709 chl[ch].n -= SCROLL;
710 if (chl[ch].n < 0)
711 chl[ch].n = 0;
712 tredraw();
713 return;
714 case CTRL('a'):
715 cu = 0;
716 break;
717 case CTRL('e'):
718 cu = len;
719 break;
720 case CTRL('b'):
721 case KEY_LEFT:
722 if (cu)
723 cu--;
724 break;
725 case CTRL('f'):
726 case KEY_RIGHT:
727 if (cu < len)
728 cu++;
729 break;
730 case CTRL('k'):
731 dirty = len = cu;
732 break;
733 case CTRL('u'):
734 if (cu == 0)
735 return;
736 len -= cu;
737 memmove(l, &l[cu], len);
738 dirty = cu = 0;
739 break;
740 case CTRL('d'):
741 if (cu >= len)
742 return;
743 memmove(&l[cu], &l[cu + 1], len - cu - 1);
744 dirty = cu;
745 len--;
746 break;
747 case CTRL('h'):
748 case KEY_BACKSPACE:
749 if (cu == 0)
750 return;
751 memmove(&l[cu - 1], &l[cu], len - cu);
752 dirty = --cu;
753 len--;
754 break;
755 case CTRL('w'):
756 if (cu == 0)
757 break;
758 i = 1;
759 while (l[cu - i] == ' ' && cu - i != 0) i++;
760 while (l[cu - i] != ' ' && cu - i != 0) i++;
761 if (cu - i != 0) i--;
762 memmove(&l[cu - i], &l[cu], len - cu);
763 cu -= i;
764 dirty = cu;
765 len -= i;
766 break;
767 case '\n':
768 l[len] = 0;
769 uparse(l);
770 dirty = cu = len = 0;
771 break;
772 default:
773 if (c > CHAR_MAX || len >= BufSz - 1)
774 return; /* Skip other curses codes. */
775 memmove(&l[cu + 1], &l[cu], len - cu);
776 dirty = cu;
777 len++;
778 l[cu++] = c;
779 break;
780 }
781 while (cu < shft)
782 dirty = 0, shft -= shft >= scr.x / 2 ? scr.x / 2 : shft;
783 while (cu >= scr.x + shft)
784 dirty = 0, shft += scr.x / 2;
785 if (dirty <= shft)
786 i = shft;
787 else if (dirty > scr.x + shft || dirty > len)
788 goto mvcur;
789 else
790 i = dirty;
791 wmove(scr.iw, 0, i - shft);
792 wclrtoeol(scr.iw);
793 for (; i - shft < scr.x && i < len; i++)
794 waddch(scr.iw, l[i]);
795 mvcur: wmove(scr.iw, 0, cu - shft);
796 }
797
798 static void
799 treset(void)
800 {
801 if (scr.mw)
802 delwin(scr.mw);
803 if (scr.sw)
804 delwin(scr.sw);
805 if (scr.iw)
806 delwin(scr.iw);
807 endwin();
808 }
809
810 int
811 main(int argc, char *argv[])
812 {
813 const char *user = getenv("USER");
814 const char *ircnick = getenv("IRCNICK");
815 const char *key = getenv("IRCPASS");
816 const char *server = SRV;
817 const char *port = PORT;
818 char *err;
819 int o, reconn;
820
821 signal(SIGPIPE, SIG_IGN);
822 if (pledge("stdio dns inet rpath cpath wpath tty", NULL) < 0) {
823 fprintf(stderr, "pledge: %s\n", strerror(errno));
824 return 1;
825 }
826
827 while ((o = getopt(argc, argv, "thk:n:u:s:p:l:")) >= 0)
828 switch (o) {
829 case 'h':
830 case '?':
831 usage:
832 fputs("usage: irc [-n NICK] [-u USER] [-s SERVER] [-p PORT] [-l LOGFILE ] [-t] [-h]\n", stderr);
833 exit(0);
834 case 'l':
835 if (!(logfp = fopen(optarg, "a")))
836 panic("fopen: logfile");
837 break;
838 case 'n':
839 if (strlen(optarg) >= sizeof nick)
840 goto usage;
841 strcpy(nick, optarg);
842 break;
843 case 't':
844 ssl = 1;
845 break;
846 case 'u':
847 user = optarg;
848 break;
849 case 's':
850 server = optarg;
851 break;
852 case 'p':
853 port = optarg;
854 break;
855 }
856
857 if (pledge("stdio dns inet rpath tty", NULL) < 0) {
858 fprintf(stderr, "pledge: %s\n", strerror(errno));
859 return 1;
860 }
861 if (!user)
862 user = "anonymous";
863 if (!nick[0] && ircnick && strlen(ircnick) < sizeof nick)
864 strcpy(nick, ircnick);
865 if (!nick[0] && strlen(user) < sizeof nick)
866 strcpy(nick, user);
867 if (!nick[0])
868 goto usage;
869 tinit();
870 err = dial(server, port);
871 if (err)
872 panic(err);
873 chadd(server, 0);
874 sinit(key, nick, user);
875 reconn = 0;
876 while (!quit) {
877 struct timeval t = {.tv_sec = 5};
878 struct Chan *c;
879 fd_set rfs, wfs;
880 int ret;
881
882 if (winchg)
883 tresize();
884 FD_ZERO(&wfs);
885 FD_ZERO(&rfs);
886 FD_SET(0, &rfs);
887 if (!reconn) {
888 FD_SET(srv.fd, &rfs);
889 if (outp != outb)
890 FD_SET(srv.fd, &wfs);
891 }
892 ret = select(srv.fd + 1, &rfs, &wfs, 0, &t);
893 if (ret < 0) {
894 if (errno == EINTR)
895 continue;
896 panic("Select failed.");
897 }
898 if (reconn) {
899 hangup();
900 if (reconn++ == MaxRecons + 1)
901 panic("Link lost.");
902 pushf(0, "-!- Link lost, attempting reconnection...");
903 if (dial(server, port) != 0)
904 continue;
905 sinit(key, nick, user);
906 for (c = chl; c < &chl[nch]; ++c)
907 if (c->join)
908 sndf("JOIN %s", c->name);
909 reconn = 0;
910 }
911 if (FD_ISSET(srv.fd, &rfs)) {
912 if (!srd()) {
913 reconn = 1;
914 continue;
915 }
916 }
917 if (FD_ISSET(srv.fd, &wfs)) {
918 int wr;
919
920 if (ssl)
921 wr = tls_write(srv.tls, outb, outp - outb);
922 else
923 wr = write(srv.fd, outb, outp - outb);
924 if (wr <= 0) {
925 reconn = wr < 0;
926 continue;
927 }
928 outp -= wr;
929 memmove(outb, outb + wr, outp - outb);
930 }
931 if (FD_ISSET(0, &rfs)) {
932 tgetch();
933 wrefresh(scr.iw);
934 }
935 }
936 hangup();
937 while (nch--)
938 free(chl[nch].buf);
939 treset();
940 exit(0);
941 }