Introduction
Introduction Statistics Contact Development Disclaimer Help
irc.c - irc - IRC client based on c9x.me/irc client
git clone git://git.codemadness.org/irc
Log
Files
Refs
README
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 <= 0…
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 …
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->…
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…
413 c->name,
414 gmtm->tm_year + 1900, gmtm->tm_mon + 1, gmtm->tm…
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")) { /* J…
489 if ((pm = strtok(0, " "))) {
490 chdel(pm);
491 pushf(0, "-!- Cannot join channel %s (%s)", pm, …
492 tredraw();
493 }
494 } else if (!strcmp(cmd, "QUIT")) { /* Commands we don't care abo…
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…
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 - out…
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 }
You are viewing proxied material from codemadness.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.