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