only print debug output when XCHATDEBUG env variable is set
[rofl0r-ixchat.git] / src / common / text.c
blobe2dac3b3b5f249944281834e5890854261059f3e
1 /* X-Chat
2 * Copyright (C) 1998 Peter Zelezny.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <ctype.h>
24 #include <time.h>
25 #include <sys/types.h>
26 #include <fcntl.h>
27 #include <sys/stat.h>
28 #include <sys/mman.h>
30 #include "xchat.h"
31 #include <glib.h>
32 #include "cfgfiles.h"
33 #include "chanopt.h"
34 #include "plugin.h"
35 #include "fe.h"
36 #include "server.h"
37 #include "util.h"
38 #include "outbound.h"
39 #include "xchatc.h"
40 #include "text.h"
42 struct pevt_stage1
44 int len;
45 char *data;
46 struct pevt_stage1 *next;
50 static void mkdir_p (char *dir);
51 static char *log_create_filename (char *channame);
54 static char *
55 scrollback_get_filename (session *sess, char *buf, int max)
57 char *net, *chan;
59 net = server_get_network (sess->server, FALSE);
60 if (!net)
61 return NULL;
63 snprintf (buf, max, "%s/scrollback/%s/%s.txt", get_xdir_fs (), net, "");
64 mkdir_p (buf);
66 chan = log_create_filename (sess->channel);
67 snprintf (buf, max, "%s/scrollback/%s/%s.txt", get_xdir_fs (), net, chan);
68 free (chan);
70 return buf;
73 #if 0
75 static void
76 scrollback_unlock (session *sess)
78 char buf[1024];
80 if (scrollback_get_filename (sess, buf, sizeof (buf) - 6) == NULL)
81 return;
83 strcat (buf, ".lock");
84 unlink (buf);
87 static gboolean
88 scrollback_lock (session *sess)
90 char buf[1024];
91 int fh;
93 if (scrollback_get_filename (sess, buf, sizeof (buf) - 6) == NULL)
94 return FALSE;
96 strcat (buf, ".lock");
98 if (access (buf, F_OK) == 0)
99 return FALSE; /* can't get lock */
101 fh = open (buf, O_CREAT | O_TRUNC | O_APPEND | O_WRONLY, 0644);
102 if (fh == -1)
103 return FALSE;
105 return TRUE;
108 #endif
110 void
111 scrollback_close (session *sess)
113 if (sess->scrollfd != -1)
115 close (sess->scrollfd);
116 sess->scrollfd = -1;
120 static char *
121 file_to_buffer (char *file, int *len)
123 int fh;
124 char *buf;
125 struct stat st;
127 fh = open (file, O_RDONLY | OFLAGS);
128 if (fh == -1)
129 return NULL;
131 fstat (fh, &st);
133 buf = malloc (st.st_size);
134 if (!buf)
136 close (fh);
137 return NULL;
140 if (read (fh, buf, st.st_size) != st.st_size)
142 free (buf);
143 close (fh);
144 return NULL;
147 *len = st.st_size;
148 close (fh);
149 return buf;
152 /* shrink the file to roughly prefs.max_lines */
154 static void
155 scrollback_shrink (session *sess)
157 char file[1024];
158 char *buf;
159 int fh;
160 int lines;
161 int line;
162 int len;
163 char *p;
165 scrollback_close (sess);
166 sess->scrollwritten = 0;
167 lines = 0;
169 if (scrollback_get_filename (sess, file, sizeof (file)) == NULL)
170 return;
172 buf = file_to_buffer (file, &len);
173 if (!buf)
174 return;
176 /* count all lines */
177 p = buf;
178 while (p != buf + len)
180 if (*p == '\n')
181 lines++;
182 p++;
185 fh = open (file, O_CREAT | O_TRUNC | O_APPEND | O_WRONLY, 0644);
186 if (fh == -1)
188 free (buf);
189 return;
192 line = 0;
193 p = buf;
194 while (p != buf + len)
196 if (*p == '\n')
198 line++;
199 if (line >= lines - prefs.max_lines &&
200 p + 1 != buf + len)
202 p++;
203 write (fh, p, len - (p - buf));
204 break;
207 p++;
210 close (fh);
211 free (buf);
214 static void
215 scrollback_save (session *sess, char *text)
217 char buf[512 * 4];
218 time_t stamp;
219 int len;
221 if (sess->type == SESS_SERVER)
222 return;
224 if (sess->text_scrollback == SET_DEFAULT)
226 if (!prefs.text_replay)
227 return;
229 else
231 if (sess->text_scrollback != SET_ON)
232 return;
235 if (sess->scrollfd == -1)
237 if (scrollback_get_filename (sess, buf, sizeof (buf)) == NULL)
238 return;
240 sess->scrollfd = open (buf, O_CREAT | O_APPEND | O_WRONLY, 0644);
241 if (sess->scrollfd == -1)
242 return;
245 stamp = time (0);
246 if (sizeof (stamp) == 4) /* gcc will optimize one of these out */
247 write (sess->scrollfd, buf, snprintf (buf, sizeof (buf), "T %d ", (int)stamp));
248 else
249 write (sess->scrollfd, buf, snprintf (buf, sizeof (buf), "T %"G_GINT64_FORMAT" ", (gint64)stamp));
251 len = strlen (text);
252 write (sess->scrollfd, text, len);
253 if (len && text[len - 1] != '\n')
254 write (sess->scrollfd, "\n", 1);
256 sess->scrollwritten++;
258 if ((sess->scrollwritten * 2 > prefs.max_lines && prefs.max_lines > 0) ||
259 sess->scrollwritten > 32000)
260 scrollback_shrink (sess);
263 void
264 scrollback_load (session *sess)
266 int fh;
267 char buf[512 * 4];
268 char *text;
269 time_t stamp;
270 int lines;
271 char *map, *end_map;
272 struct stat statbuf;
273 const char *begin, *eol;
275 if (sess->text_scrollback == SET_DEFAULT)
277 if (!prefs.text_replay)
278 return;
280 else
282 if (sess->text_scrollback != SET_ON)
283 return;
286 if (scrollback_get_filename (sess, buf, sizeof (buf)) == NULL)
287 return;
289 fh = open (buf, O_RDONLY | OFLAGS);
290 if (fh == -1)
291 return;
293 if (fstat (fh, &statbuf) < 0)
294 return;
296 map = mmap (NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, fh, 0);
297 if (map == MAP_FAILED)
298 return;
300 end_map = map + statbuf.st_size;
302 lines = 0;
303 begin = map;
304 while (begin < end_map)
306 int n_bytes;
308 eol = memchr (begin, '\n', end_map - begin);
310 if (!eol)
311 eol = end_map;
313 n_bytes = MIN (eol - begin, sizeof (buf) - 1);
315 strncpy (buf, begin, n_bytes);
317 buf[n_bytes] = 0;
319 if (buf[0] == 'T')
321 if (sizeof (time_t) == 4)
322 stamp = strtoul (buf + 2, NULL, 10);
323 else
324 stamp = strtoull (buf + 2, NULL, 10); /* just incase time_t is 64 bits */
325 text = strchr (buf + 3, ' ');
326 if (text)
328 text = strip_color (text + 1, -1, STRIP_COLOR);
329 fe_print_text (sess, text, stamp);
330 g_free (text);
332 lines++;
335 begin = eol + 1;
338 sess->scrollwritten = lines;
340 if (lines)
342 text = ctime (&stamp);
343 text[24] = 0; /* get rid of the \n */
344 snprintf (buf, sizeof (buf), "\n*\t%s %s\n\n", _("Loaded log from"), text);
345 fe_print_text (sess, buf, 0);
346 /*EMIT_SIGNAL (XP_TE_GENMSG, sess, "*", buf, NULL, NULL, NULL, 0);*/
349 munmap (map, statbuf.st_size);
350 close (fh);
353 void
354 log_close (session *sess)
356 char obuf[512];
357 time_t currenttime;
359 if (sess->logfd != -1)
361 currenttime = time (NULL);
362 write (sess->logfd, obuf,
363 snprintf (obuf, sizeof (obuf) - 1, _("**** ENDING LOGGING AT %s\n"),
364 ctime (&currenttime)));
365 close (sess->logfd);
366 sess->logfd = -1;
370 static void
371 mkdir_p (char *dir) /* like "mkdir -p" from a shell, FS encoding */
373 char *start = dir;
375 /* the whole thing already exists? */
376 if (access (dir, F_OK) == 0)
377 return;
379 while (*dir)
381 #ifdef WIN32
382 if (dir != start && (*dir == '/' || *dir == '\\'))
383 #else
384 if (dir != start && *dir == '/')
385 #endif
387 *dir = 0;
388 #ifdef WIN32
389 mkdir (start);
390 #else
391 mkdir (start, S_IRUSR | S_IWUSR | S_IXUSR);
392 #endif
393 *dir = '/';
395 dir++;
399 static char *
400 log_create_filename (char *channame)
402 char *tmp, *ret;
403 int mbl;
405 ret = tmp = strdup (channame);
406 while (*tmp)
408 mbl = g_utf8_skip[((unsigned char *)tmp)[0]];
409 if (mbl == 1)
411 #ifndef WIN32
412 *tmp = rfc_tolower (*tmp);
413 if (*tmp == '/')
414 #else
415 /* win32 can't handle filenames with \|/><:"*? characters */
416 if (*tmp == '\\' || *tmp == '|' || *tmp == '/' ||
417 *tmp == '>' || *tmp == '<' || *tmp == ':' ||
418 *tmp == '\"' || *tmp == '*' || *tmp == '?')
419 #endif
420 *tmp = '_';
422 tmp += mbl;
425 return ret;
428 /* like strcpy, but % turns into %% */
430 static char *
431 log_escape_strcpy (char *dest, char *src, char *end)
433 while (*src)
435 *dest = *src;
436 if (dest + 1 == end)
437 break;
438 dest++;
439 src++;
441 if (*src == '%')
443 if (dest + 1 == end)
444 break;
445 dest[0] = '%';
446 dest++;
450 dest[0] = 0;
451 return dest - 1;
454 /* substitutes %c %n %s into buffer */
456 static void
457 log_insert_vars (char *buf, int bufsize, char *fmt, char *c, char *n, char *s)
459 char *end = buf + bufsize;
461 while (1)
463 switch (fmt[0])
465 case 0:
466 buf[0] = 0;
467 return;
469 case '%':
470 fmt++;
471 switch (fmt[0])
473 case 'c':
474 buf = log_escape_strcpy (buf, c, end);
475 break;
476 case 'n':
477 buf = log_escape_strcpy (buf, n, end);
478 break;
479 case 's':
480 buf = log_escape_strcpy (buf, s, end);
481 break;
482 default:
483 buf[0] = '%';
484 buf++;
485 buf[0] = fmt[0];
486 break;
488 break;
490 default:
491 buf[0] = fmt[0];
493 fmt++;
494 buf++;
495 /* doesn't fit? */
496 if (buf == end)
498 buf[-1] = 0;
499 return;
504 static char *
505 log_create_pathname (char *servname, char *channame, char *netname)
507 char fname[384];
508 char fnametime[384];
509 char *fs;
510 struct tm *tm;
511 time_t now;
513 if (!netname)
514 netname = "NETWORK";
516 /* first, everything is in UTF-8 */
517 if (!rfc_casecmp (channame, servname))
518 channame = strdup ("server");
519 else
520 channame = log_create_filename (channame);
521 log_insert_vars (fname, sizeof (fname), prefs.logmask, channame, netname, servname);
522 free (channame);
524 /* insert time/date */
525 now = time (NULL);
526 tm = localtime (&now);
527 strftime (fnametime, sizeof (fnametime), fname, tm);
529 /* create final path/filename */
530 #ifdef WIN32
531 if (fnametime[0] == '/' || (fnametime[0] >= 'A' && fnametime[1] == ':'))
532 #else
533 if (fnametime[0] == '/') /* is it fullpath already? */
534 #endif
535 snprintf (fname, sizeof (fname), "%s", fnametime);
536 else
537 snprintf (fname, sizeof (fname), "%s/xchatlogs/%s", get_xdir_utf8 (), fnametime);
539 /* now we need it in FileSystem encoding */
540 fs = xchat_filename_from_utf8 (fname, -1, 0, 0, 0);
542 /* create all the subdirectories */
543 if (fs)
544 mkdir_p (fs);
546 return fs;
549 static int
550 log_open_file (char *servname, char *channame, char *netname)
552 char buf[512];
553 int fd;
554 char *file;
555 time_t currenttime;
557 file = log_create_pathname (servname, channame, netname);
558 if (!file)
559 return -1;
561 #ifdef WIN32
562 fd = open (file, O_CREAT | O_APPEND | O_WRONLY, S_IREAD|S_IWRITE);
563 #else
564 fd = open (file, O_CREAT | O_APPEND | O_WRONLY, 0644);
565 #endif
566 g_free (file);
568 if (fd == -1)
569 return -1;
570 currenttime = time (NULL);
571 write (fd, buf,
572 snprintf (buf, sizeof (buf), _("**** BEGIN LOGGING AT %s\n"),
573 ctime (&currenttime)));
575 return fd;
578 static void
579 log_open (session *sess)
581 static gboolean log_error = FALSE;
583 log_close (sess);
584 sess->logfd = log_open_file (sess->server->servername, sess->channel,
585 server_get_network (sess->server, FALSE));
587 if (!log_error && sess->logfd == -1)
589 char message[512];
590 snprintf (message, sizeof (message),
591 _("* Can't open log file(s) for writing. Check the\n" \
592 " permissions on %s/xchatlogs"), get_xdir_utf8 ());
593 fe_message (message, FE_MSG_WAIT | FE_MSG_ERROR);
595 log_error = TRUE;
599 void
600 log_open_or_close (session *sess)
602 if (sess->text_logging == SET_DEFAULT)
604 if (prefs.logging)
605 log_open (sess);
606 else
607 log_close (sess);
609 else
611 if (sess->text_logging)
612 log_open (sess);
613 else
614 log_close (sess);
619 get_stamp_str (char *fmt, time_t tim, char **ret)
621 char *loc = NULL;
622 char dest[128];
623 gsize len;
625 /* strftime wants the format string in LOCALE! */
626 if (!prefs.utf8_locale)
628 const gchar *charset;
630 g_get_charset (&charset);
631 loc = g_convert_with_fallback (fmt, -1, charset, "UTF-8", "?", 0, 0, 0);
632 if (loc)
633 fmt = loc;
636 len = strftime (dest, sizeof (dest), fmt, localtime (&tim));
637 if (len)
639 if (prefs.utf8_locale)
640 *ret = g_strdup (dest);
641 else
642 *ret = g_locale_to_utf8 (dest, len, 0, &len, 0);
645 if (loc)
646 g_free (loc);
648 return len;
651 static void
652 log_write (session *sess, char *text)
654 char *temp;
655 char *stamp;
656 char *file;
657 int len;
659 if (sess->text_logging == SET_DEFAULT)
661 if (!prefs.logging)
662 return;
664 else
666 if (sess->text_logging != SET_ON)
667 return;
670 if (sess->logfd == -1)
671 log_open (sess);
673 /* change to a different log file? */
674 file = log_create_pathname (sess->server->servername, sess->channel,
675 server_get_network (sess->server, FALSE));
676 if (file)
678 if (access (file, F_OK) != 0)
680 close (sess->logfd);
681 sess->logfd = log_open_file (sess->server->servername, sess->channel,
682 server_get_network (sess->server, FALSE));
684 g_free (file);
687 if (prefs.timestamp_logs)
689 len = get_stamp_str (prefs.timestamp_log_format, time (0), &stamp);
690 if (len)
692 write (sess->logfd, stamp, len);
693 g_free (stamp);
696 temp = strip_color (text, -1, STRIP_ALL);
697 len = strlen (temp);
698 write (sess->logfd, temp, len);
699 /* lots of scripts/plugins print without a \n at the end */
700 if (temp[len - 1] != '\n')
701 write (sess->logfd, "\n", 1); /* emulate what xtext would display */
702 g_free (temp);
705 /* converts a CP1252/ISO-8859-1(5) hybrid to UTF-8 */
706 /* Features: 1. It never fails, all 00-FF chars are converted to valid UTF-8 */
707 /* 2. Uses CP1252 in the range 80-9f because ISO doesn't have any- */
708 /* thing useful in this range and it helps us receive from mIRC */
709 /* 3. The five undefined chars in CP1252 80-9f are replaced with */
710 /* ISO-8859-15 control codes. */
711 /* 4. Handles 0xa4 as a Euro symbol ala ISO-8859-15. */
712 /* 5. Uses ISO-8859-1 (which matches CP1252) for everything else. */
713 /* 6. This routine measured 3x faster than g_convert :) */
715 static unsigned char *
716 iso_8859_1_to_utf8 (unsigned char *text, int len, gsize *bytes_written)
718 unsigned int idx;
719 unsigned char *res, *output;
720 static const unsigned short lowtable[] = /* 74 byte table for 80-a4 */
722 /* compressed utf-8 table: if the first byte's 0x20 bit is set, it
723 indicates a 2-byte utf-8 sequence, otherwise prepend a 0xe2. */
724 0x82ac, /* 80 Euro. CP1252 from here on... */
725 0xe281, /* 81 NA */
726 0x809a, /* 82 */
727 0xe692, /* 83 */
728 0x809e, /* 84 */
729 0x80a6, /* 85 */
730 0x80a0, /* 86 */
731 0x80a1, /* 87 */
732 0xeb86, /* 88 */
733 0x80b0, /* 89 */
734 0xe5a0, /* 8a */
735 0x80b9, /* 8b */
736 0xe592, /* 8c */
737 0xe28d, /* 8d NA */
738 0xe5bd, /* 8e */
739 0xe28f, /* 8f NA */
740 0xe290, /* 90 NA */
741 0x8098, /* 91 */
742 0x8099, /* 92 */
743 0x809c, /* 93 */
744 0x809d, /* 94 */
745 0x80a2, /* 95 */
746 0x8093, /* 96 */
747 0x8094, /* 97 */
748 0xeb9c, /* 98 */
749 0x84a2, /* 99 */
750 0xe5a1, /* 9a */
751 0x80ba, /* 9b */
752 0xe593, /* 9c */
753 0xe29d, /* 9d NA */
754 0xe5be, /* 9e */
755 0xe5b8, /* 9f */
756 0xe2a0, /* a0 */
757 0xe2a1, /* a1 */
758 0xe2a2, /* a2 */
759 0xe2a3, /* a3 */
760 0x82ac /* a4 ISO-8859-15 Euro. */
763 if (len == -1)
764 len = strlen (text);
766 /* worst case scenario: every byte turns into 3 bytes */
767 res = output = g_malloc ((len * 3) + 1);
768 if (!output)
769 return NULL;
771 while (len)
773 if (G_LIKELY (*text < 0x80))
775 *output = *text; /* ascii maps directly */
777 else if (*text <= 0xa4) /* 80-a4 use a lookup table */
779 idx = *text - 0x80;
780 if (lowtable[idx] & 0x2000)
782 *output++ = (lowtable[idx] >> 8) & 0xdf; /* 2 byte utf-8 */
783 *output = lowtable[idx] & 0xff;
785 else
787 *output++ = 0xe2; /* 3 byte utf-8 */
788 *output++ = (lowtable[idx] >> 8) & 0xff;
789 *output = lowtable[idx] & 0xff;
792 else if (*text < 0xc0)
794 *output++ = 0xc2;
795 *output = *text;
797 else
799 *output++ = 0xc3;
800 *output = *text - 0x40;
802 output++;
803 text++;
804 len--;
806 *output = 0; /* terminate */
807 *bytes_written = output - res;
809 return res;
812 char *
813 text_validate (char **text, int *len)
815 char *utf;
816 gsize utf_len;
818 /* valid utf8? */
819 if (g_utf8_validate (*text, *len, 0))
820 return NULL;
822 #ifdef WIN32
823 if (GetACP () == 1252) /* our routine is better than iconv's 1252 */
824 #else
825 if (prefs.utf8_locale)
826 #endif
827 /* fallback to iso-8859-1 */
828 utf = iso_8859_1_to_utf8 (*text, *len, &utf_len);
829 else
831 /* fallback to locale */
832 utf = g_locale_to_utf8 (*text, *len, 0, &utf_len, NULL);
833 if (!utf)
834 utf = iso_8859_1_to_utf8 (*text, *len, &utf_len);
837 if (!utf)
839 *text = g_strdup ("%INVALID%");
840 *len = 9;
841 } else
843 *text = utf;
844 *len = utf_len;
847 return utf;
850 void
851 PrintText (session *sess, char *text)
853 char *conv;
855 if (!sess)
857 if (!sess_list)
858 return;
859 sess = (session *) sess_list->data;
862 /* make sure it's valid utf8 */
863 if (text[0] == 0)
865 text = "\n";
866 conv = NULL;
867 } else
869 int len = -1;
870 conv = text_validate ((char **)&text, &len);
873 log_write (sess, text);
874 scrollback_save (sess, text);
875 fe_print_text (sess, text, 0);
877 if (conv)
878 g_free (conv);
881 void
882 PrintTextf (session *sess, char *format, ...)
884 va_list args;
885 char *buf;
887 va_start (args, format);
888 buf = g_strdup_vprintf (format, args);
889 va_end (args);
891 PrintText (sess, buf);
892 g_free (buf);
895 /* Print Events stuff here --AGL */
897 /* Consider the following a NOTES file:
899 The main upshot of this is:
900 * Plugins and Perl scripts (when I get round to signaling perl.c) can intercept text events and do what they like
901 * The default text engine can be config'ed
903 By default it should appear *exactly* the same (I'm working hard not to change the default style) but if you go into Settings->Edit Event Texts you can change the text's. The format is thus:
905 The normal %Cx (color) and %B (bold) etc work
907 $x is replaced with the data in var x (e.g. $1 is often the nick)
909 $axxx is replace with a single byte of value xxx (in base 10)
911 AGL (990507)
914 /* These lists are thus:
915 pntevts_text[] are the strings the user sees (WITH %x etc)
916 pntevts[] are the data strings with \000 etc
919 /* To add a new event:
921 Think up a name (like "Join")
922 Make up a pevt_name_help struct
923 Add an entry to textevents.in
924 Type: make textevents
927 /* Internals:
929 On startup ~/.xchat/printevents.conf is loaded if it doesn't exist the
930 defaults are loaded. Any missing events are filled from defaults.
931 Each event is parsed by pevt_build_string and a binary output is produced
932 which looks like:
934 (byte) value: 0 = {
935 (int) numbers of bytes
936 (char []) that number of byte to be memcpy'ed into the buffer
939 (byte) number of varable to insert
940 2 = end of buffer
942 Each XP_TE_* signal is hard coded to call text_emit which calls
943 display_event which decodes the data
945 This means that this system *should be faster* than snprintf because
946 it always 'knows' that format of the string (basically is preparses much
947 of the work)
949 --AGL
952 char *pntevts_text[NUM_XP];
953 char *pntevts[NUM_XP];
955 #define pevt_generic_none_help NULL
957 static char * const pevt_genmsg_help[] = {
958 N_("Left message"),
959 N_("Right message"),
962 static char * const pevt_join_help[] = {
963 N_("The nick of the joining person"),
964 N_("The channel being joined"),
965 N_("The host of the person"),
968 static char * const pevt_chanaction_help[] = {
969 N_("Nickname"),
970 N_("The action"),
971 N_("Mode char"),
972 N_("Identified text"),
975 static char * const pevt_chanmsg_help[] = {
976 N_("Nickname"),
977 N_("The text"),
978 N_("Mode char"),
979 N_("Identified text"),
982 static char * const pevt_privmsg_help[] = {
983 N_("Nickname"),
984 N_("The message"),
985 N_("Identified text")
988 static char * const pevt_changenick_help[] = {
989 N_("Old nickname"),
990 N_("New nickname"),
993 static char * const pevt_newtopic_help[] = {
994 N_("Nick of person who changed the topic"),
995 N_("Topic"),
996 N_("Channel"),
999 static char * const pevt_topic_help[] = {
1000 N_("Channel"),
1001 N_("Topic"),
1004 static char * const pevt_kick_help[] = {
1005 N_("The nickname of the kicker"),
1006 N_("The person being kicked"),
1007 N_("The channel"),
1008 N_("The reason"),
1011 static char * const pevt_part_help[] = {
1012 N_("The nick of the person leaving"),
1013 N_("The host of the person"),
1014 N_("The channel"),
1017 static char * const pevt_chandate_help[] = {
1018 N_("The channel"),
1019 N_("The time"),
1022 static char * const pevt_topicdate_help[] = {
1023 N_("The channel"),
1024 N_("The creator"),
1025 N_("The time"),
1028 static char * const pevt_quit_help[] = {
1029 N_("Nick"),
1030 N_("Reason"),
1031 N_("Host"),
1034 static char * const pevt_pingrep_help[] = {
1035 N_("Who it's from"),
1036 N_("The time in x.x format (see below)"),
1039 static char * const pevt_notice_help[] = {
1040 N_("Who it's from"),
1041 N_("The message"),
1044 static char * const pevt_channotice_help[] = {
1045 N_("Who it's from"),
1046 N_("The Channel it's going to"),
1047 N_("The message"),
1050 static char * const pevt_uchangenick_help[] = {
1051 N_("Old nickname"),
1052 N_("New nickname"),
1055 static char * const pevt_ukick_help[] = {
1056 N_("The person being kicked"),
1057 N_("The channel"),
1058 N_("The nickname of the kicker"),
1059 N_("The reason"),
1062 static char * const pevt_partreason_help[] = {
1063 N_("The nick of the person leaving"),
1064 N_("The host of the person"),
1065 N_("The channel"),
1066 N_("The reason"),
1069 static char * const pevt_ctcpsnd_help[] = {
1070 N_("The sound"),
1071 N_("The nick of the person"),
1072 N_("The channel"),
1075 static char * const pevt_ctcpgen_help[] = {
1076 N_("The CTCP event"),
1077 N_("The nick of the person"),
1080 static char * const pevt_ctcpgenc_help[] = {
1081 N_("The CTCP event"),
1082 N_("The nick of the person"),
1083 N_("The Channel it's going to"),
1086 static char * const pevt_chansetkey_help[] = {
1087 N_("The nick of the person who set the key"),
1088 N_("The key"),
1091 static char * const pevt_chansetlimit_help[] = {
1092 N_("The nick of the person who set the limit"),
1093 N_("The limit"),
1096 static char * const pevt_chanop_help[] = {
1097 N_("The nick of the person who did the op'ing"),
1098 N_("The nick of the person who has been op'ed"),
1101 static char * const pevt_chanhop_help[] = {
1102 N_("The nick of the person who has been halfop'ed"),
1103 N_("The nick of the person who did the halfop'ing"),
1106 static char * const pevt_chanvoice_help[] = {
1107 N_("The nick of the person who did the voice'ing"),
1108 N_("The nick of the person who has been voice'ed"),
1111 static char * const pevt_chanban_help[] = {
1112 N_("The nick of the person who did the banning"),
1113 N_("The ban mask"),
1116 static char * const pevt_chanrmkey_help[] = {
1117 N_("The nick who removed the key"),
1120 static char * const pevt_chanrmlimit_help[] = {
1121 N_("The nick who removed the limit"),
1124 static char * const pevt_chandeop_help[] = {
1125 N_("The nick of the person of did the deop'ing"),
1126 N_("The nick of the person who has been deop'ed"),
1128 static char * const pevt_chandehop_help[] = {
1129 N_("The nick of the person of did the dehalfop'ing"),
1130 N_("The nick of the person who has been dehalfop'ed"),
1133 static char * const pevt_chandevoice_help[] = {
1134 N_("The nick of the person of did the devoice'ing"),
1135 N_("The nick of the person who has been devoice'ed"),
1138 static char * const pevt_chanunban_help[] = {
1139 N_("The nick of the person of did the unban'ing"),
1140 N_("The ban mask"),
1143 static char * const pevt_chanexempt_help[] = {
1144 N_("The nick of the person who did the exempt"),
1145 N_("The exempt mask"),
1148 static char * const pevt_chanrmexempt_help[] = {
1149 N_("The nick of the person removed the exempt"),
1150 N_("The exempt mask"),
1153 static char * const pevt_chaninvite_help[] = {
1154 N_("The nick of the person who did the invite"),
1155 N_("The invite mask"),
1158 static char * const pevt_chanrminvite_help[] = {
1159 N_("The nick of the person removed the invite"),
1160 N_("The invite mask"),
1163 static char * const pevt_chanmodegen_help[] = {
1164 N_("The nick of the person setting the mode"),
1165 N_("The mode's sign (+/-)"),
1166 N_("The mode letter"),
1167 N_("The channel it's being set on"),
1170 static char * const pevt_whois1_help[] = {
1171 N_("Nickname"),
1172 N_("Username"),
1173 N_("Host"),
1174 N_("Full name"),
1177 static char * const pevt_whois2_help[] = {
1178 N_("Nickname"),
1179 N_("Channel Membership/\"is an IRC operator\""),
1182 static char * const pevt_whois3_help[] = {
1183 N_("Nickname"),
1184 N_("Server Information"),
1187 static char * const pevt_whois4_help[] = {
1188 N_("Nickname"),
1189 N_("Idle time"),
1192 static char * const pevt_whois4t_help[] = {
1193 N_("Nickname"),
1194 N_("Idle time"),
1195 N_("Signon time"),
1198 static char * const pevt_whois5_help[] = {
1199 N_("Nickname"),
1200 N_("Away reason"),
1203 static char * const pevt_whois6_help[] = {
1204 N_("Nickname"),
1207 static char * const pevt_whoisid_help[] = {
1208 N_("Nickname"),
1209 N_("Message"),
1210 "Numeric"
1213 static char * const pevt_whoisauth_help[] = {
1214 N_("Nickname"),
1215 N_("Message"),
1216 N_("Account"),
1219 static char * const pevt_whoisrealhost_help[] = {
1220 N_("Nickname"),
1221 N_("Real user@host"),
1222 N_("Real IP"),
1223 N_("Message"),
1226 static char * const pevt_generic_channel_help[] = {
1227 N_("Channel Name"),
1230 static char * const pevt_servertext_help[] = {
1231 N_("Text"),
1232 N_("Server Name"),
1233 N_("Raw Numeric or Identifier")
1236 static char * const pevt_sslmessage_help[] = {
1237 N_("Text"),
1238 N_("Server Name")
1241 static char * const pevt_invited_help[] = {
1242 N_("Channel Name"),
1243 N_("Nick of person who invited you"),
1244 N_("Server Name"),
1247 static char * const pevt_usersonchan_help[] = {
1248 N_("Channel Name"),
1249 N_("Users"),
1252 static char * const pevt_nickclash_help[] = {
1253 N_("Nickname in use"),
1254 N_("Nick being tried"),
1257 static char * const pevt_connfail_help[] = {
1258 N_("Error"),
1261 static char * const pevt_connect_help[] = {
1262 N_("Host"),
1263 N_("IP"),
1264 N_("Port"),
1267 static char * const pevt_sconnect_help[] = {
1268 "PID"
1271 static char * const pevt_generic_nick_help[] = {
1272 N_("Nickname"),
1273 N_("Server Name"),
1274 N_("Network")
1277 static char * const pevt_chanmodes_help[] = {
1278 N_("Channel Name"),
1279 N_("Modes string"),
1282 static char * const pevt_rawmodes_help[] = {
1283 N_("Nickname"),
1284 N_("Modes string"),
1287 static char * const pevt_kill_help[] = {
1288 N_("Nickname"),
1289 N_("Reason"),
1292 static char * const pevt_dccchaterr_help[] = {
1293 N_("Nickname"),
1294 N_("IP address"),
1295 N_("Port"),
1296 N_("Error"),
1299 static char * const pevt_dccstall_help[] = {
1300 N_("DCC Type"),
1301 N_("Filename"),
1302 N_("Nickname"),
1305 static char * const pevt_generic_file_help[] = {
1306 N_("Filename"),
1307 N_("Error"),
1310 static char * const pevt_dccrecverr_help[] = {
1311 N_("Filename"),
1312 N_("Destination filename"),
1313 N_("Nickname"),
1314 N_("Error"),
1317 static char * const pevt_dccrecvcomp_help[] = {
1318 N_("Filename"),
1319 N_("Destination filename"),
1320 N_("Nickname"),
1321 N_("CPS"),
1324 static char * const pevt_dccconfail_help[] = {
1325 N_("DCC Type"),
1326 N_("Nickname"),
1327 N_("Error"),
1330 static char * const pevt_dccchatcon_help[] = {
1331 N_("Nickname"),
1332 N_("IP address"),
1335 static char * const pevt_dcccon_help[] = {
1336 N_("Nickname"),
1337 N_("IP address"),
1338 N_("Filename"),
1341 static char * const pevt_dccsendfail_help[] = {
1342 N_("Filename"),
1343 N_("Nickname"),
1344 N_("Error"),
1347 static char * const pevt_dccsendcomp_help[] = {
1348 N_("Filename"),
1349 N_("Nickname"),
1350 N_("CPS"),
1353 static char * const pevt_dccoffer_help[] = {
1354 N_("Filename"),
1355 N_("Nickname"),
1356 N_("Pathname"),
1359 static char * const pevt_dccfileabort_help[] = {
1360 N_("Nickname"),
1361 N_("Filename")
1364 static char * const pevt_dccchatabort_help[] = {
1365 N_("Nickname"),
1368 static char * const pevt_dccresumeoffer_help[] = {
1369 N_("Nickname"),
1370 N_("Filename"),
1371 N_("Position"),
1374 static char * const pevt_dccsendoffer_help[] = {
1375 N_("Nickname"),
1376 N_("Filename"),
1377 N_("Size"),
1378 N_("IP address"),
1381 static char * const pevt_dccgenericoffer_help[] = {
1382 N_("DCC String"),
1383 N_("Nickname"),
1386 static char * const pevt_notifynumber_help[] = {
1387 N_("Number of notify items"),
1390 static char * const pevt_serverlookup_help[] = {
1391 N_("Server Name"),
1394 static char * const pevt_servererror_help[] = {
1395 N_("Text"),
1398 static char * const pevt_foundip_help[] = {
1399 N_("IP"),
1402 static char * const pevt_dccrename_help[] = {
1403 N_("Old Filename"),
1404 N_("New Filename"),
1407 static char * const pevt_ctcpsend_help[] = {
1408 N_("Receiver"),
1409 N_("Message"),
1412 static char * const pevt_ignoreaddremove_help[] = {
1413 N_("Hostmask"),
1416 static char * const pevt_resolvinguser_help[] = {
1417 N_("Nickname"),
1418 N_("Hostname"),
1421 static char * const pevt_malformed_help[] = {
1422 N_("Nickname"),
1423 N_("The Packet"),
1426 static char * const pevt_pingtimeout_help[] = {
1427 N_("Seconds"),
1430 static char * const pevt_uinvite_help[] = {
1431 N_("Nick of person who have been invited"),
1432 N_("Channel Name"),
1433 N_("Server Name"),
1436 static char * const pevt_banlist_help[] = {
1437 N_("Channel"),
1438 N_("Banmask"),
1439 N_("Who set the ban"),
1440 N_("Ban time"),
1443 static char * const pevt_discon_help[] = {
1444 N_("Error"),
1447 #include "textevents.h"
1449 static void
1450 pevent_load_defaults ()
1452 int i;
1454 for (i = 0; i < NUM_XP; i++)
1456 if (pntevts_text[i])
1457 free (pntevts_text[i]);
1459 /* make-te.c sets this 128 flag (DON'T call gettext() flag) */
1460 if (te[i].num_args & 128)
1461 pntevts_text[i] = strdup (te[i].def);
1462 else
1463 pntevts_text[i] = strdup (_(te[i].def));
1467 void
1468 pevent_make_pntevts ()
1470 int i, m;
1471 char out[1024];
1473 for (i = 0; i < NUM_XP; i++)
1475 if (pntevts[i] != NULL)
1476 free (pntevts[i]);
1477 if (pevt_build_string (pntevts_text[i], &(pntevts[i]), &m) != 0)
1479 snprintf (out, sizeof (out),
1480 _("Error parsing event %s.\nLoading default."), te[i].name);
1481 fe_message (out, FE_MSG_WARN);
1482 free (pntevts_text[i]);
1483 /* make-te.c sets this 128 flag (DON'T call gettext() flag) */
1484 if (te[i].num_args & 128)
1485 pntevts_text[i] = strdup (te[i].def);
1486 else
1487 pntevts_text[i] = strdup (_(te[i].def));
1488 if (pevt_build_string (pntevts_text[i], &(pntevts[i]), &m) != 0)
1490 fprintf (stderr,
1491 "XChat CRITICAL *** default event text failed to build!\n");
1492 abort ();
1498 /* Loading happens at 2 levels:
1499 1) File is read into blocks
1500 2) Pe block is parsed and loaded
1502 --AGL */
1504 /* Better hope you pass good args.. --AGL */
1506 static void
1507 pevent_trigger_load (int *i_penum, char **i_text, char **i_snd)
1509 int penum = *i_penum, len;
1510 char *text = *i_text, *snd = *i_snd;
1512 if (penum != -1 && text != NULL)
1514 len = strlen (text) + 1;
1515 if (pntevts_text[penum])
1516 free (pntevts_text[penum]);
1517 pntevts_text[penum] = malloc (len);
1518 memcpy (pntevts_text[penum], text, len);
1521 if (text)
1522 free (text);
1523 if (snd)
1524 free (snd);
1525 *i_text = NULL;
1526 *i_snd = NULL;
1527 *i_penum = 0;
1530 static int
1531 pevent_find (char *name, int *i_i)
1533 int i = *i_i, j;
1535 j = i + 1;
1536 while (1)
1538 if (j == NUM_XP)
1539 j = 0;
1540 if (strcmp (te[j].name, name) == 0)
1542 *i_i = j;
1543 return j;
1545 if (j == i)
1546 return -1;
1547 j++;
1552 pevent_load (char *filename)
1554 /* AGL, I've changed this file and pevent_save, could you please take a look at
1555 * the changes and possibly modify them to suit you
1556 * //David H
1558 char *buf, *ibuf;
1559 int fd, i = 0, pnt = 0;
1560 struct stat st;
1561 char *text = NULL, *snd = NULL;
1562 int penum = 0;
1563 char *ofs;
1565 if (filename == NULL)
1566 fd = xchat_open_file ("pevents.conf", O_RDONLY, 0, 0);
1567 else
1568 fd = xchat_open_file (filename, O_RDONLY, 0, XOF_FULLPATH);
1570 if (fd == -1)
1571 return 1;
1572 if (fstat (fd, &st) != 0)
1573 return 1;
1574 ibuf = malloc (st.st_size);
1575 read (fd, ibuf, st.st_size);
1576 close (fd);
1578 while (buf_get_line (ibuf, &buf, &pnt, st.st_size))
1580 if (buf[0] == '#')
1581 continue;
1582 if (strlen (buf) == 0)
1583 continue;
1585 ofs = strchr (buf, '=');
1586 if (!ofs)
1587 continue;
1588 *ofs = 0;
1589 ofs++;
1590 /*if (*ofs == 0)
1591 continue;*/
1593 if (strcmp (buf, "event_name") == 0)
1595 if (penum >= 0)
1596 pevent_trigger_load (&penum, &text, &snd);
1597 penum = pevent_find (ofs, &i);
1598 continue;
1599 } else if (strcmp (buf, "event_text") == 0)
1601 if (text)
1602 free (text);
1604 #if 0
1605 /* This allows updating of old strings. We don't use new defaults
1606 if the user has customized the strings (.e.g a text theme).
1607 Hash of the old default is enough to identify and replace it.
1608 This only works in English. */
1610 switch (g_str_hash (ofs))
1612 case 0x526743a4:
1613 /* %C08,02 Hostmask PRIV NOTI CHAN CTCP INVI UNIG %O */
1614 text = strdup (te[XP_TE_IGNOREHEADER].def);
1615 break;
1617 case 0xe91bc9c2:
1618 /* %C08,02 %O */
1619 text = strdup (te[XP_TE_IGNOREFOOTER].def);
1620 break;
1622 case 0x1fbfdf22:
1623 /* -%C10-%C11-%O$tDCC RECV: Cannot open $1 for writing - aborting. */
1624 text = strdup (te[XP_TE_DCCFILEERR].def);
1625 break;
1627 default:
1628 text = strdup (ofs);
1630 #else
1631 text = strdup (ofs);
1632 #endif
1634 continue;
1635 }/* else if (strcmp (buf, "event_sound") == 0)
1637 if (snd)
1638 free (snd);
1639 snd = strdup (ofs);
1640 continue;
1643 continue;
1646 pevent_trigger_load (&penum, &text, &snd);
1647 free (ibuf);
1648 return 0;
1651 static void
1652 pevent_check_all_loaded ()
1654 int i;
1656 for (i = 0; i < NUM_XP; i++)
1658 if (pntevts_text[i] == NULL)
1660 /*printf ("%s\n", te[i].name);
1661 snprintf(out, sizeof(out), "The data for event %s failed to load. Reverting to defaults.\nThis may be because a new version of XChat is loading an old config file.\n\nCheck all print event texts are correct", evtnames[i]);
1662 gtkutil_simpledialog(out); */
1663 /* make-te.c sets this 128 flag (DON'T call gettext() flag) */
1664 if (te[i].num_args & 128)
1665 pntevts_text[i] = strdup (te[i].def);
1666 else
1667 pntevts_text[i] = strdup (_(te[i].def));
1672 void
1673 load_text_events ()
1675 memset (&pntevts_text, 0, sizeof (char *) * (NUM_XP));
1676 memset (&pntevts, 0, sizeof (char *) * (NUM_XP));
1678 if (pevent_load (NULL))
1679 pevent_load_defaults ();
1680 pevent_check_all_loaded ();
1681 pevent_make_pntevts ();
1685 CL: format_event now handles filtering of arguments:
1686 1) if prefs.stripcolor is set, filter all style control codes from arguments
1687 2) always strip \010 (ATTR_HIDDEN) from arguments: it is only for use in the format string itself
1689 #define ARG_FLAG(argn) (1 << (argn))
1691 void
1692 format_event (session *sess, int index, char **args, char *o, int sizeofo, unsigned int stripcolor_args)
1694 int len, oi, ii, numargs;
1695 char *i, *ar, d, a, done_all = FALSE;
1697 i = pntevts[index];
1698 numargs = te[index].num_args & 0x7f;
1700 oi = ii = len = d = a = 0;
1701 o[0] = 0;
1703 if (i == NULL)
1704 return;
1706 while (done_all == FALSE)
1708 d = i[ii++];
1709 switch (d)
1711 case 0:
1712 memcpy (&len, &(i[ii]), sizeof (int));
1713 ii += sizeof (int);
1714 if (oi + len > sizeofo)
1716 printf ("Overflow in display_event (%s)\n", i);
1717 o[0] = 0;
1718 return;
1720 memcpy (&(o[oi]), &(i[ii]), len);
1721 oi += len;
1722 ii += len;
1723 break;
1724 case 1:
1725 a = i[ii++];
1726 if (a > numargs)
1728 fprintf (stderr,
1729 "XChat DEBUG: display_event: arg > numargs (%d %d %s)\n",
1730 a, numargs, i);
1731 break;
1733 ar = args[(int) a + 1];
1734 if (ar == NULL)
1736 printf ("arg[%d] is NULL in print event\n", a + 1);
1737 } else
1739 if (stripcolor_args & ARG_FLAG(a + 1)) len = strip_color2 (ar, -1, &o[oi], STRIP_ALL);
1740 else len = strip_hidden_attribute (ar, &o[oi]);
1741 oi += len;
1743 break;
1744 case 2:
1745 o[oi++] = '\n';
1746 o[oi++] = 0;
1747 done_all = TRUE;
1748 continue;
1749 case 3:
1750 /* if (sess->type == SESS_DIALOG)
1752 if (prefs.dialog_indent_nicks)
1753 o[oi++] = '\t';
1754 else
1755 o[oi++] = ' ';
1756 } else
1758 if (prefs.indent_nicks)
1759 o[oi++] = '\t';
1760 else
1761 o[oi++] = ' ';
1762 /*}*/
1763 break;
1766 o[oi] = 0;
1767 if (*o == '\n')
1768 o[0] = 0;
1771 static void
1772 display_event (session *sess, int event, char **args, unsigned int stripcolor_args)
1774 char o[4096];
1775 format_event (sess, event, args, o, sizeof (o), stripcolor_args);
1776 if (o[0])
1777 PrintText (sess, o);
1781 pevt_build_string (const char *input, char **output, int *max_arg)
1783 struct pevt_stage1 *s = NULL, *base = NULL, *last = NULL, *next;
1784 int clen;
1785 char o[4096], d, *obuf, *i;
1786 int oi, ii, max = -1, len, x;
1788 len = strlen (input);
1789 i = malloc (len + 1);
1790 memcpy (i, input, len + 1);
1791 check_special_chars (i, TRUE);
1793 len = strlen (i);
1795 clen = oi = ii = 0;
1797 for (;;)
1799 if (ii == len)
1800 break;
1801 d = i[ii++];
1802 if (d != '$')
1804 o[oi++] = d;
1805 continue;
1807 if (i[ii] == '$')
1809 o[oi++] = '$';
1810 continue;
1812 if (oi > 0)
1814 s = (struct pevt_stage1 *) malloc (sizeof (struct pevt_stage1));
1815 if (base == NULL)
1816 base = s;
1817 if (last != NULL)
1818 last->next = s;
1819 last = s;
1820 s->next = NULL;
1821 s->data = malloc (oi + sizeof (int) + 1);
1822 s->len = oi + sizeof (int) + 1;
1823 clen += oi + sizeof (int) + 1;
1824 s->data[0] = 0;
1825 memcpy (&(s->data[1]), &oi, sizeof (int));
1826 memcpy (&(s->data[1 + sizeof (int)]), o, oi);
1827 oi = 0;
1829 if (ii == len)
1831 fe_message ("String ends with a $", FE_MSG_WARN);
1832 return 1;
1834 d = i[ii++];
1835 if (d == 'a')
1836 { /* Hex value */
1837 x = 0;
1838 if (ii == len)
1839 goto a_len_error;
1840 d = i[ii++];
1841 d -= '0';
1842 x = d * 100;
1843 if (ii == len)
1844 goto a_len_error;
1845 d = i[ii++];
1846 d -= '0';
1847 x += d * 10;
1848 if (ii == len)
1849 goto a_len_error;
1850 d = i[ii++];
1851 d -= '0';
1852 x += d;
1853 if (x > 255)
1854 goto a_range_error;
1855 o[oi++] = x;
1856 continue;
1858 a_len_error:
1859 fe_message ("String ends in $a", FE_MSG_WARN);
1860 return 1;
1861 a_range_error:
1862 fe_message ("$a value is greater than 255", FE_MSG_WARN);
1863 return 1;
1865 if (d == 't')
1867 /* Tab - if tabnicks is set then write '\t' else ' ' */
1868 s = (struct pevt_stage1 *) malloc (sizeof (struct pevt_stage1));
1869 if (base == NULL)
1870 base = s;
1871 if (last != NULL)
1872 last->next = s;
1873 last = s;
1874 s->next = NULL;
1875 s->data = malloc (1);
1876 s->len = 1;
1877 clen += 1;
1878 s->data[0] = 3;
1880 continue;
1882 if (d < '1' || d > '9')
1884 snprintf (o, sizeof (o), "Error, invalid argument $%c\n", d);
1885 fe_message (o, FE_MSG_WARN);
1886 return 1;
1888 d -= '0';
1889 if (max < d)
1890 max = d;
1891 s = (struct pevt_stage1 *) malloc (sizeof (struct pevt_stage1));
1892 if (base == NULL)
1893 base = s;
1894 if (last != NULL)
1895 last->next = s;
1896 last = s;
1897 s->next = NULL;
1898 s->data = malloc (2);
1899 s->len = 2;
1900 clen += 2;
1901 s->data[0] = 1;
1902 s->data[1] = d - 1;
1904 if (oi > 0)
1906 s = (struct pevt_stage1 *) malloc (sizeof (struct pevt_stage1));
1907 if (base == NULL)
1908 base = s;
1909 if (last != NULL)
1910 last->next = s;
1911 last = s;
1912 s->next = NULL;
1913 s->data = malloc (oi + sizeof (int) + 1);
1914 s->len = oi + sizeof (int) + 1;
1915 clen += oi + sizeof (int) + 1;
1916 s->data[0] = 0;
1917 memcpy (&(s->data[1]), &oi, sizeof (int));
1918 memcpy (&(s->data[1 + sizeof (int)]), o, oi);
1919 oi = 0;
1921 s = (struct pevt_stage1 *) malloc (sizeof (struct pevt_stage1));
1922 if (base == NULL)
1923 base = s;
1924 if (last != NULL)
1925 last->next = s;
1926 last = s;
1927 s->next = NULL;
1928 s->data = malloc (1);
1929 s->len = 1;
1930 clen += 1;
1931 s->data[0] = 2;
1933 oi = 0;
1934 s = base;
1935 obuf = malloc (clen);
1936 while (s)
1938 next = s->next;
1939 memcpy (&obuf[oi], s->data, s->len);
1940 oi += s->len;
1941 free (s->data);
1942 free (s);
1943 s = next;
1946 free (i);
1948 if (max_arg)
1949 *max_arg = max;
1950 if (output)
1951 *output = obuf;
1953 return 0;
1957 /* black n white(0/1) are bad colors for nicks, and we'll use color 2 for us */
1958 /* also light/dark gray (14/15) */
1959 /* 5,7,8 are all shades of yellow which happen to look dman near the same */
1961 static char rcolors[] = { 19, 20, 22, 24, 25, 26, 27, 28, 29 };
1963 static int
1964 color_of (char *name)
1966 int i = 0, sum = 0;
1968 while (name[i])
1969 sum += name[i++];
1970 sum %= sizeof (rcolors) / sizeof (char);
1971 return rcolors[sum];
1975 /* called by EMIT_SIGNAL macro */
1976 #include <stdio.h>
1977 void
1978 text_emit (int index, session *sess, char *a, char *b, char *c, char *d, char* file, int lineno)
1980 char *word[PDIWORDS];
1981 int i;
1982 unsigned int stripcolor_args = (prefs.stripcolor ? 0xFFFFFFFF : 0);
1983 char tbuf[NICKLEN + 4];
1985 if(getenv("XCHATDEBUG"))
1986 dprintf(2, "%s:%d %s %s %s %s\n", file, lineno, a ? a : "", b ? b : "", c ? c : "", d ? d : "");
1988 if (prefs.colorednicks && (index == XP_TE_CHANACTION || index == XP_TE_CHANMSG))
1990 snprintf (tbuf, sizeof (tbuf), "\003%d%s", color_of (a), a);
1991 a = tbuf;
1992 stripcolor_args &= ~ARG_FLAG(1); /* don't strip color from this argument */
1995 word[0] = te[index].name;
1996 word[1] = (a ? a : "\000");
1997 word[2] = (b ? b : "\000");
1998 word[3] = (c ? c : "\000");
1999 word[4] = (d ? d : "\000");
2000 for (i = 5; i < PDIWORDS; i++)
2001 word[i] = "\000";
2003 if (plugin_emit_print (sess, word))
2004 return;
2006 /* If a plugin's callback executes "/close", 'sess' may be invalid */
2007 if (!is_session (sess))
2008 return;
2010 switch (index)
2012 case XP_TE_JOIN:
2013 case XP_TE_PART:
2014 case XP_TE_PARTREASON:
2015 case XP_TE_QUIT:
2016 /* implement ConfMode / Hide Join and Part Messages */
2017 if (chanopt_is_set (prefs.confmode, sess->text_hidejoinpart))
2018 return;
2019 break;
2021 /* ===Private message=== */
2022 case XP_TE_PRIVMSG:
2023 case XP_TE_DPRIVMSG:
2024 case XP_TE_PRIVACTION:
2025 case XP_TE_DPRIVACTION:
2026 if (chanopt_is_set_a (prefs.input_beep_priv, sess->alert_beep))
2027 sound_beep (sess);
2028 if (chanopt_is_set_a (prefs.input_flash_priv, sess->alert_taskbar))
2029 fe_flash_window (sess);
2030 /* why is this one different? because of plugin-tray.c's hooks! ugly */
2031 if (sess->alert_tray == SET_ON)
2032 fe_tray_set_icon (FE_ICON_MESSAGE);
2033 break;
2035 /* ===Highlighted message=== */
2036 case XP_TE_HCHANACTION:
2037 case XP_TE_HCHANMSG:
2038 if (chanopt_is_set_a (prefs.input_beep_hilight, sess->alert_beep))
2039 sound_beep (sess);
2040 if (chanopt_is_set_a (prefs.input_flash_hilight, sess->alert_taskbar))
2041 fe_flash_window (sess);
2042 if (sess->alert_tray == SET_ON)
2043 fe_tray_set_icon (FE_ICON_MESSAGE);
2044 break;
2046 /* ===Channel message=== */
2047 case XP_TE_CHANACTION:
2048 case XP_TE_CHANMSG:
2049 if (chanopt_is_set_a (prefs.input_beep_chans, sess->alert_beep))
2050 sound_beep (sess);
2051 if (chanopt_is_set_a (prefs.input_flash_chans, sess->alert_taskbar))
2052 fe_flash_window (sess);
2053 if (sess->alert_tray == SET_ON)
2054 fe_tray_set_icon (FE_ICON_MESSAGE);
2055 break;
2058 sound_play_event (index);
2059 display_event (sess, index, word, stripcolor_args);
2062 char *
2063 text_find_format_string (char *name)
2065 int i = 0;
2067 i = pevent_find (name, &i);
2068 if (i >= 0)
2069 return pntevts_text[i];
2071 return NULL;
2075 text_emit_by_name (char *name, session *sess, char *a, char *b, char *c, char *d)
2077 int i = 0;
2079 i = pevent_find (name, &i);
2080 if (i >= 0)
2082 text_emit (i, sess, a, b, c, d, "", 0);
2083 return 1;
2086 return 0;
2089 void
2090 pevent_save (char *fn)
2092 int fd, i;
2093 char buf[1024];
2095 if (!fn)
2096 fd = xchat_open_file ("pevents.conf", O_CREAT | O_TRUNC | O_WRONLY,
2097 0x180, XOF_DOMODE);
2098 else
2099 fd = xchat_open_file (fn, O_CREAT | O_TRUNC | O_WRONLY, 0x180,
2100 XOF_FULLPATH | XOF_DOMODE);
2101 if (fd == -1)
2104 fe_message ("Error opening config file\n", FALSE);
2105 If we get here when X-Chat is closing the fe-message causes a nice & hard crash
2106 so we have to use perror which doesn't rely on GTK
2109 perror ("Error opening config file\n");
2110 return;
2113 for (i = 0; i < NUM_XP; i++)
2115 write (fd, buf, snprintf (buf, sizeof (buf),
2116 "event_name=%s\n", te[i].name));
2117 write (fd, buf, snprintf (buf, sizeof (buf),
2118 "event_text=%s\n\n", pntevts_text[i]));
2121 close (fd);
2124 /* =========================== */
2125 /* ========== SOUND ========== */
2126 /* =========================== */
2128 char *sound_files[NUM_XP];
2130 void
2131 sound_beep (session *sess)
2133 if (sound_files[XP_TE_BEEP] && sound_files[XP_TE_BEEP][0])
2134 /* user defined beep _file_ */
2135 sound_play_event (XP_TE_BEEP);
2136 else
2137 /* system beep */
2138 fe_beep ();
2141 static char *
2142 sound_find_command (void)
2144 /* some sensible unix players. You're bound to have one of them */
2145 static const char * const progs[] = {"aplay", "esdplay", "soxplay", "artsplay", NULL};
2146 char *cmd;
2147 int i = 0;
2149 if (prefs.soundcmd[0])
2150 return g_strdup (prefs.soundcmd);
2152 while (progs[i])
2154 cmd = g_find_program_in_path (progs[i]);
2155 if (cmd)
2156 return cmd;
2157 i++;
2160 return NULL;
2163 void
2164 sound_play (const char *file, gboolean quiet)
2166 char buf[512];
2167 char wavfile[512];
2168 char *file_fs;
2169 char *cmd;
2171 /* the pevents GUI editor triggers this after removing a soundfile */
2172 if (!file[0])
2173 return;
2175 #ifdef WIN32
2176 /* check for fullpath, windows style */
2177 if (strlen (file) > 3 &&
2178 file[1] == ':' && (file[2] == '\\' || file[2] == '/') )
2180 strncpy (wavfile, file, sizeof (wavfile));
2181 } else
2182 #endif
2183 if (file[0] != '/')
2185 snprintf (wavfile, sizeof (wavfile), "%s/%s", prefs.sounddir, file);
2186 } else
2188 strncpy (wavfile, file, sizeof (wavfile));
2190 wavfile[sizeof (wavfile) - 1] = 0; /* ensure termination */
2192 file_fs = xchat_filename_from_utf8 (wavfile, -1, 0, 0, 0);
2193 if (!file_fs)
2194 return;
2196 if (access (file_fs, R_OK) == 0)
2198 cmd = sound_find_command ();
2200 #ifdef WIN32
2201 if (cmd == NULL || strcmp (cmd, "esdplay") == 0)
2203 PlaySound (file_fs, NULL, SND_NODEFAULT|SND_FILENAME|SND_ASYNC);
2204 } else
2205 #endif
2207 if (cmd)
2209 if (strchr (file_fs, ' '))
2210 snprintf (buf, sizeof (buf), "%s \"%s\"", cmd, file_fs);
2211 else
2212 snprintf (buf, sizeof (buf), "%s %s", cmd, file_fs);
2213 buf[sizeof (buf) - 1] = '\0';
2214 xchat_exec (buf);
2218 if (cmd)
2219 g_free (cmd);
2221 } else
2223 if (!quiet)
2225 snprintf (buf, sizeof (buf), _("Cannot read sound file:\n%s"), wavfile);
2226 fe_message (buf, FE_MSG_ERROR);
2230 g_free (file_fs);
2233 void
2234 sound_play_event (int i)
2236 if (sound_files[i])
2237 sound_play (sound_files[i], FALSE);
2240 static void
2241 sound_load_event (char *evt, char *file)
2243 int i = 0;
2245 if (file[0] && pevent_find (evt, &i) != -1)
2247 if (sound_files[i])
2248 free (sound_files[i]);
2249 sound_files[i] = strdup (file);
2253 void
2254 sound_load ()
2256 int fd;
2257 char buf[512];
2258 char evt[128];
2260 memset (&sound_files, 0, sizeof (char *) * (NUM_XP));
2262 fd = xchat_open_file ("sound.conf", O_RDONLY, 0, 0);
2263 if (fd == -1)
2264 return;
2266 evt[0] = 0;
2267 while (waitline (fd, buf, sizeof buf, FALSE) != -1)
2269 if (strncmp (buf, "event=", 6) == 0)
2271 safe_strcpy (evt, buf + 6, sizeof (evt));
2273 else if (strncmp (buf, "sound=", 6) == 0)
2275 if (evt[0] != 0)
2277 sound_load_event (evt, buf + 6);
2278 evt[0] = 0;
2283 close (fd);
2286 void
2287 sound_save ()
2289 int fd, i;
2290 char buf[512];
2292 fd = xchat_open_file ("sound.conf", O_CREAT | O_TRUNC | O_WRONLY, 0x180,
2293 XOF_DOMODE);
2294 if (fd == -1)
2295 return;
2297 for (i = 0; i < NUM_XP; i++)
2299 if (sound_files[i] && sound_files[i][0])
2301 write (fd, buf, snprintf (buf, sizeof (buf),
2302 "event=%s\n", te[i].name));
2303 write (fd, buf, snprintf (buf, sizeof (buf),
2304 "sound=%s\n\n", sound_files[i]));
2308 close (fd);