diff options
Diffstat (limited to 'src/fe-common')
107 files changed, 30651 insertions, 0 deletions
diff --git a/src/fe-common/Makefile.am b/src/fe-common/Makefile.am new file mode 100644 index 0000000..f4b78b3 --- /dev/null +++ b/src/fe-common/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = core irc + +EXTRA_DIST = meson.build diff --git a/src/fe-common/Makefile.in b/src/fe-common/Makefile.in new file mode 100644 index 0000000..d1025a5 --- /dev/null +++ b/src/fe-common/Makefile.in @@ -0,0 +1,671 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/fe-common +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/glib-2.0.m4 \ + $(top_srcdir)/m4/glibtests.m4 $(top_srcdir)/m4/libgcrypt.m4 \ + $(top_srcdir)/m4/libotr.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/irssi-config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CHAT_MODULES = @CHAT_MODULES@ +COMMON_LIBS = @COMMON_LIBS@ +COMMON_NOUI_LIBS = @COMMON_NOUI_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FUZZER_LIBS = @FUZZER_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GOBJECT_QUERY = @GOBJECT_QUERY@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBOTR_CFLAGS = @LIBOTR_CFLAGS@ +LIBOTR_LIBS = @LIBOTR_LIBS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +OTR_CFLAGS = @OTR_CFLAGS@ +OTR_LDFLAGS = @OTR_LDFLAGS@ +OTR_LINK_FLAGS = @OTR_LINK_FLAGS@ +OTR_LINK_LIBS = @OTR_LINK_LIBS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL_CFLAGS = @PERL_CFLAGS@ +PERL_EXTRA_OPTS = @PERL_EXTRA_OPTS@ +PERL_FE_LINK_LIBS = @PERL_FE_LINK_LIBS@ +PERL_LDFLAGS = @PERL_LDFLAGS@ +PERL_LINK_FLAGS = @PERL_LINK_FLAGS@ +PERL_LINK_LIBS = @PERL_LINK_LIBS@ +PERL_MM_OPT = @PERL_MM_OPT@ +PERL_MM_PARAMS = @PERL_MM_PARAMS@ +PERL_STATIC_LIBS = @PERL_STATIC_LIBS@ +PERL_USE_LIB = @PERL_USE_LIB@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROG_LIBS = @PROG_LIBS@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TEXTUI_LIBS = @TEXTUI_LIBS@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +installed_test_metadir = @installed_test_metadir@ +installed_testdir = @installed_testdir@ +irc_MODULES = @irc_MODULES@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +otr_module_lib = @otr_module_lib@ +otr_static_lib = @otr_static_lib@ +pdfdir = @pdfdir@ +perl_module_fe_lib = @perl_module_fe_lib@ +perl_module_lib = @perl_module_lib@ +perl_static_fe_lib = @perl_static_fe_lib@ +perl_static_lib = @perl_static_lib@ +perlpath = @perlpath@ +pkgconfigdir = @pkgconfigdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sedpath = @sedpath@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = core irc +EXTRA_DIST = meson.build +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/fe-common/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/fe-common/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/fe-common/core/Makefile.am b/src/fe-common/core/Makefile.am new file mode 100644 index 0000000..01e934e --- /dev/null +++ b/src/fe-common/core/Makefile.am @@ -0,0 +1,73 @@ +noinst_LIBRARIES = libfe_common_core.a + +AM_CPPFLAGS = \ + -I$(top_builddir) \ + $(GLIB_CFLAGS) \ + -DHELPDIR=\""$(datadir)/irssi/help"\" \ + -DTHEMESDIR=\""$(datadir)/irssi/themes"\" + +libfe_common_core_a_SOURCES = \ + chat-completion.c \ + command-history.c \ + completion.c \ + fe-channels.c \ + fe-common-core.c \ + fe-core-commands.c \ + fe-exec.c \ + fe-expandos.c \ + fe-help.c \ + fe-ignore.c \ + fe-ignore-messages.c \ + fe-log.c \ + fe-messages.c \ + fe-modules.c \ + fe-queries.c \ + fe-server.c \ + fe-settings.c \ + fe-tls.c \ + formats.c \ + hilight-text.c \ + keyboard.c \ + module-formats.c \ + printtext.c \ + fe-recode.c \ + themes.c \ + window-activity.c \ + window-commands.c \ + window-items.c \ + windows-layout.c \ + fe-windows.c + +if HAVE_CAPSICUM +libfe_common_core_a_SOURCES += \ + fe-capsicum.c +endif + +pkginc_fe_common_coredir=$(pkgincludedir)/src/fe-common/core +pkginc_fe_common_core_HEADERS = \ + command-history.h \ + chat-completion.h \ + completion.h \ + fe-capsicum.h \ + fe-channels.h \ + fe-common-core.h \ + fe-core-commands.h \ + fe-exec.h \ + fe-messages.h \ + fe-queries.h \ + fe-settings.h \ + fe-tls.h \ + formats.h \ + hilight-text.h \ + keyboard.h \ + module-formats.h \ + module.h \ + printtext.h \ + fe-recode.h \ + themes.h \ + window-activity.h \ + window-items.h \ + windows-layout.h \ + fe-windows.h + +EXTRA_DIST = meson.build diff --git a/src/fe-common/core/Makefile.in b/src/fe-common/core/Makefile.in new file mode 100644 index 0000000..c247da9 --- /dev/null +++ b/src/fe-common/core/Makefile.in @@ -0,0 +1,876 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@HAVE_CAPSICUM_TRUE@am__append_1 = \ +@HAVE_CAPSICUM_TRUE@ fe-capsicum.c + +subdir = src/fe-common/core +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/glib-2.0.m4 \ + $(top_srcdir)/m4/glibtests.m4 $(top_srcdir)/m4/libgcrypt.m4 \ + $(top_srcdir)/m4/libotr.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_fe_common_core_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/irssi-config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +am__v_AR_1 = +libfe_common_core_a_AR = $(AR) $(ARFLAGS) +libfe_common_core_a_LIBADD = +am__libfe_common_core_a_SOURCES_DIST = chat-completion.c \ + command-history.c completion.c fe-channels.c fe-common-core.c \ + fe-core-commands.c fe-exec.c fe-expandos.c fe-help.c \ + fe-ignore.c fe-ignore-messages.c fe-log.c fe-messages.c \ + fe-modules.c fe-queries.c fe-server.c fe-settings.c fe-tls.c \ + formats.c hilight-text.c keyboard.c module-formats.c \ + printtext.c fe-recode.c themes.c window-activity.c \ + window-commands.c window-items.c windows-layout.c fe-windows.c \ + fe-capsicum.c +@HAVE_CAPSICUM_TRUE@am__objects_1 = fe-capsicum.$(OBJEXT) +am_libfe_common_core_a_OBJECTS = chat-completion.$(OBJEXT) \ + command-history.$(OBJEXT) completion.$(OBJEXT) \ + fe-channels.$(OBJEXT) fe-common-core.$(OBJEXT) \ + fe-core-commands.$(OBJEXT) fe-exec.$(OBJEXT) \ + fe-expandos.$(OBJEXT) fe-help.$(OBJEXT) fe-ignore.$(OBJEXT) \ + fe-ignore-messages.$(OBJEXT) fe-log.$(OBJEXT) \ + fe-messages.$(OBJEXT) fe-modules.$(OBJEXT) \ + fe-queries.$(OBJEXT) fe-server.$(OBJEXT) fe-settings.$(OBJEXT) \ + fe-tls.$(OBJEXT) formats.$(OBJEXT) hilight-text.$(OBJEXT) \ + keyboard.$(OBJEXT) module-formats.$(OBJEXT) \ + printtext.$(OBJEXT) fe-recode.$(OBJEXT) themes.$(OBJEXT) \ + window-activity.$(OBJEXT) window-commands.$(OBJEXT) \ + window-items.$(OBJEXT) windows-layout.$(OBJEXT) \ + fe-windows.$(OBJEXT) $(am__objects_1) +libfe_common_core_a_OBJECTS = $(am_libfe_common_core_a_OBJECTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/chat-completion.Po \ + ./$(DEPDIR)/command-history.Po ./$(DEPDIR)/completion.Po \ + ./$(DEPDIR)/fe-capsicum.Po ./$(DEPDIR)/fe-channels.Po \ + ./$(DEPDIR)/fe-common-core.Po ./$(DEPDIR)/fe-core-commands.Po \ + ./$(DEPDIR)/fe-exec.Po ./$(DEPDIR)/fe-expandos.Po \ + ./$(DEPDIR)/fe-help.Po ./$(DEPDIR)/fe-ignore-messages.Po \ + ./$(DEPDIR)/fe-ignore.Po ./$(DEPDIR)/fe-log.Po \ + ./$(DEPDIR)/fe-messages.Po ./$(DEPDIR)/fe-modules.Po \ + ./$(DEPDIR)/fe-queries.Po ./$(DEPDIR)/fe-recode.Po \ + ./$(DEPDIR)/fe-server.Po ./$(DEPDIR)/fe-settings.Po \ + ./$(DEPDIR)/fe-tls.Po ./$(DEPDIR)/fe-windows.Po \ + ./$(DEPDIR)/formats.Po ./$(DEPDIR)/hilight-text.Po \ + ./$(DEPDIR)/keyboard.Po ./$(DEPDIR)/module-formats.Po \ + ./$(DEPDIR)/printtext.Po ./$(DEPDIR)/themes.Po \ + ./$(DEPDIR)/window-activity.Po ./$(DEPDIR)/window-commands.Po \ + ./$(DEPDIR)/window-items.Po ./$(DEPDIR)/windows-layout.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libfe_common_core_a_SOURCES) +DIST_SOURCES = $(am__libfe_common_core_a_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkginc_fe_common_coredir)" +HEADERS = $(pkginc_fe_common_core_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(top_srcdir)/build-aux/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CHAT_MODULES = @CHAT_MODULES@ +COMMON_LIBS = @COMMON_LIBS@ +COMMON_NOUI_LIBS = @COMMON_NOUI_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FUZZER_LIBS = @FUZZER_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GOBJECT_QUERY = @GOBJECT_QUERY@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBOTR_CFLAGS = @LIBOTR_CFLAGS@ +LIBOTR_LIBS = @LIBOTR_LIBS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +OTR_CFLAGS = @OTR_CFLAGS@ +OTR_LDFLAGS = @OTR_LDFLAGS@ +OTR_LINK_FLAGS = @OTR_LINK_FLAGS@ +OTR_LINK_LIBS = @OTR_LINK_LIBS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL_CFLAGS = @PERL_CFLAGS@ +PERL_EXTRA_OPTS = @PERL_EXTRA_OPTS@ +PERL_FE_LINK_LIBS = @PERL_FE_LINK_LIBS@ +PERL_LDFLAGS = @PERL_LDFLAGS@ +PERL_LINK_FLAGS = @PERL_LINK_FLAGS@ +PERL_LINK_LIBS = @PERL_LINK_LIBS@ +PERL_MM_OPT = @PERL_MM_OPT@ +PERL_MM_PARAMS = @PERL_MM_PARAMS@ +PERL_STATIC_LIBS = @PERL_STATIC_LIBS@ +PERL_USE_LIB = @PERL_USE_LIB@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROG_LIBS = @PROG_LIBS@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TEXTUI_LIBS = @TEXTUI_LIBS@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +installed_test_metadir = @installed_test_metadir@ +installed_testdir = @installed_testdir@ +irc_MODULES = @irc_MODULES@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +otr_module_lib = @otr_module_lib@ +otr_static_lib = @otr_static_lib@ +pdfdir = @pdfdir@ +perl_module_fe_lib = @perl_module_fe_lib@ +perl_module_lib = @perl_module_lib@ +perl_static_fe_lib = @perl_static_fe_lib@ +perl_static_lib = @perl_static_lib@ +perlpath = @perlpath@ +pkgconfigdir = @pkgconfigdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sedpath = @sedpath@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +noinst_LIBRARIES = libfe_common_core.a +AM_CPPFLAGS = \ + -I$(top_builddir) \ + $(GLIB_CFLAGS) \ + -DHELPDIR=\""$(datadir)/irssi/help"\" \ + -DTHEMESDIR=\""$(datadir)/irssi/themes"\" + +libfe_common_core_a_SOURCES = chat-completion.c command-history.c \ + completion.c fe-channels.c fe-common-core.c fe-core-commands.c \ + fe-exec.c fe-expandos.c fe-help.c fe-ignore.c \ + fe-ignore-messages.c fe-log.c fe-messages.c fe-modules.c \ + fe-queries.c fe-server.c fe-settings.c fe-tls.c formats.c \ + hilight-text.c keyboard.c module-formats.c printtext.c \ + fe-recode.c themes.c window-activity.c window-commands.c \ + window-items.c windows-layout.c fe-windows.c $(am__append_1) +pkginc_fe_common_coredir = $(pkgincludedir)/src/fe-common/core +pkginc_fe_common_core_HEADERS = \ + command-history.h \ + chat-completion.h \ + completion.h \ + fe-capsicum.h \ + fe-channels.h \ + fe-common-core.h \ + fe-core-commands.h \ + fe-exec.h \ + fe-messages.h \ + fe-queries.h \ + fe-settings.h \ + fe-tls.h \ + formats.h \ + hilight-text.h \ + keyboard.h \ + module-formats.h \ + module.h \ + printtext.h \ + fe-recode.h \ + themes.h \ + window-activity.h \ + window-items.h \ + windows-layout.h \ + fe-windows.h + +EXTRA_DIST = meson.build +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/fe-common/core/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/fe-common/core/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +libfe_common_core.a: $(libfe_common_core_a_OBJECTS) $(libfe_common_core_a_DEPENDENCIES) $(EXTRA_libfe_common_core_a_DEPENDENCIES) + $(AM_V_at)-rm -f libfe_common_core.a + $(AM_V_AR)$(libfe_common_core_a_AR) libfe_common_core.a $(libfe_common_core_a_OBJECTS) $(libfe_common_core_a_LIBADD) + $(AM_V_at)$(RANLIB) libfe_common_core.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chat-completion.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/command-history.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/completion.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-capsicum.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-channels.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-common-core.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-core-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-exec.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-expandos.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-help.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-ignore-messages.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-ignore.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-log.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-messages.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-modules.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-queries.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-recode.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-server.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-settings.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-tls.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-windows.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/formats.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hilight-text.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keyboard.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/module-formats.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/printtext.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/themes.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/window-activity.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/window-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/window-items.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/windows-layout.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkginc_fe_common_coreHEADERS: $(pkginc_fe_common_core_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginc_fe_common_core_HEADERS)'; test -n "$(pkginc_fe_common_coredir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_fe_common_coredir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkginc_fe_common_coredir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_fe_common_coredir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_fe_common_coredir)" || exit $$?; \ + done + +uninstall-pkginc_fe_common_coreHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginc_fe_common_core_HEADERS)'; test -n "$(pkginc_fe_common_coredir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkginc_fe_common_coredir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(pkginc_fe_common_coredir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/chat-completion.Po + -rm -f ./$(DEPDIR)/command-history.Po + -rm -f ./$(DEPDIR)/completion.Po + -rm -f ./$(DEPDIR)/fe-capsicum.Po + -rm -f ./$(DEPDIR)/fe-channels.Po + -rm -f ./$(DEPDIR)/fe-common-core.Po + -rm -f ./$(DEPDIR)/fe-core-commands.Po + -rm -f ./$(DEPDIR)/fe-exec.Po + -rm -f ./$(DEPDIR)/fe-expandos.Po + -rm -f ./$(DEPDIR)/fe-help.Po + -rm -f ./$(DEPDIR)/fe-ignore-messages.Po + -rm -f ./$(DEPDIR)/fe-ignore.Po + -rm -f ./$(DEPDIR)/fe-log.Po + -rm -f ./$(DEPDIR)/fe-messages.Po + -rm -f ./$(DEPDIR)/fe-modules.Po + -rm -f ./$(DEPDIR)/fe-queries.Po + -rm -f ./$(DEPDIR)/fe-recode.Po + -rm -f ./$(DEPDIR)/fe-server.Po + -rm -f ./$(DEPDIR)/fe-settings.Po + -rm -f ./$(DEPDIR)/fe-tls.Po + -rm -f ./$(DEPDIR)/fe-windows.Po + -rm -f ./$(DEPDIR)/formats.Po + -rm -f ./$(DEPDIR)/hilight-text.Po + -rm -f ./$(DEPDIR)/keyboard.Po + -rm -f ./$(DEPDIR)/module-formats.Po + -rm -f ./$(DEPDIR)/printtext.Po + -rm -f ./$(DEPDIR)/themes.Po + -rm -f ./$(DEPDIR)/window-activity.Po + -rm -f ./$(DEPDIR)/window-commands.Po + -rm -f ./$(DEPDIR)/window-items.Po + -rm -f ./$(DEPDIR)/windows-layout.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pkginc_fe_common_coreHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/chat-completion.Po + -rm -f ./$(DEPDIR)/command-history.Po + -rm -f ./$(DEPDIR)/completion.Po + -rm -f ./$(DEPDIR)/fe-capsicum.Po + -rm -f ./$(DEPDIR)/fe-channels.Po + -rm -f ./$(DEPDIR)/fe-common-core.Po + -rm -f ./$(DEPDIR)/fe-core-commands.Po + -rm -f ./$(DEPDIR)/fe-exec.Po + -rm -f ./$(DEPDIR)/fe-expandos.Po + -rm -f ./$(DEPDIR)/fe-help.Po + -rm -f ./$(DEPDIR)/fe-ignore-messages.Po + -rm -f ./$(DEPDIR)/fe-ignore.Po + -rm -f ./$(DEPDIR)/fe-log.Po + -rm -f ./$(DEPDIR)/fe-messages.Po + -rm -f ./$(DEPDIR)/fe-modules.Po + -rm -f ./$(DEPDIR)/fe-queries.Po + -rm -f ./$(DEPDIR)/fe-recode.Po + -rm -f ./$(DEPDIR)/fe-server.Po + -rm -f ./$(DEPDIR)/fe-settings.Po + -rm -f ./$(DEPDIR)/fe-tls.Po + -rm -f ./$(DEPDIR)/fe-windows.Po + -rm -f ./$(DEPDIR)/formats.Po + -rm -f ./$(DEPDIR)/hilight-text.Po + -rm -f ./$(DEPDIR)/keyboard.Po + -rm -f ./$(DEPDIR)/module-formats.Po + -rm -f ./$(DEPDIR)/printtext.Po + -rm -f ./$(DEPDIR)/themes.Po + -rm -f ./$(DEPDIR)/window-activity.Po + -rm -f ./$(DEPDIR)/window-commands.Po + -rm -f ./$(DEPDIR)/window-items.Po + -rm -f ./$(DEPDIR)/windows-layout.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pkginc_fe_common_coreHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-pkginc_fe_common_coreHEADERS install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-pkginc_fe_common_coreHEADERS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/fe-common/core/chat-completion.c b/src/fe-common/core/chat-completion.c new file mode 100644 index 0000000..be16db7 --- /dev/null +++ b/src/fe-common/core/chat-completion.c @@ -0,0 +1,1330 @@ +/* + chat-completion.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/lib-config/iconfig.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/core/chatnets.h> +#include <irssi/src/core/servers.h> +#include <irssi/src/core/servers-setup.h> +#include <irssi/src/core/channels.h> +#include <irssi/src/core/channels-setup.h> +#include <irssi/src/core/queries.h> +#include <irssi/src/core/nicklist.h> + +#include <irssi/src/fe-common/core/completion.h> +#include <irssi/src/fe-common/core/chat-completion.h> +#include <irssi/src/fe-common/core/window-items.h> + +enum { + COMPLETE_MCASE_NEVER = 0, + COMPLETE_MCASE_ALWAYS, + COMPLETE_MCASE_AUTO, +}; + +static int keep_privates_count, keep_publics_count; +static int completion_lowercase; +static char *completion_char, *cmdchars; +static GSList *global_lastmsgs; +static int completion_auto, completion_strict, completion_empty_line; +static int completion_match_case; + +#define SERVER_LAST_MSG_ADD(server, nick) \ + last_msg_add(&((MODULE_SERVER_REC *) MODULE_DATA(server))->lastmsgs, \ + nick, TRUE, keep_privates_count) + +#define CHANNEL_LAST_MSG_ADD(channel, nick, own) \ + last_msg_add(&((MODULE_CHANNEL_REC *) MODULE_DATA(channel))->lastmsgs, \ + nick, own, keep_publics_count) + +static gboolean contains_uppercase(const char *s1) +{ + const char *ch; + + for (ch = s1; *ch != '\0'; ch++) { + if (g_ascii_isupper(*ch)) + return TRUE; + } + + return FALSE; +} + +static LAST_MSG_REC *last_msg_find(GSList *list, const char *nick) +{ + while (list != NULL) { + LAST_MSG_REC *rec = list->data; + + if (g_ascii_strcasecmp(rec->nick, nick) == 0) + return rec; + list = list->next; + } + + return NULL; +} + +static void last_msg_dec_owns(GSList *list) +{ + LAST_MSG_REC *rec; + + while (list != NULL) { + rec = list->data; + if (rec->own) rec->own--; + + list = list->next; + } +} + +static void last_msg_destroy(GSList **list, LAST_MSG_REC *rec) +{ + *list = g_slist_remove(*list, rec); + + g_free(rec->nick); + g_free(rec); +} + +static void last_msg_add(GSList **list, const char *nick, int own, int max) +{ + LAST_MSG_REC *rec; + + if (max <= 0) + return; + + rec = last_msg_find(*list, nick); + if (rec != NULL) { + /* msg already exists, update it */ + *list = g_slist_remove(*list, rec); + if (own) + rec->own = max; + else if (rec->own) + rec->own--; + } else { + rec = g_new(LAST_MSG_REC, 1); + rec->nick = g_strdup(nick); + + while ((int)g_slist_length(*list) >= max) { + last_msg_destroy(list, g_slist_last(*list)->data); + } + + rec->own = own ? max : 0; + } + rec->time = time(NULL); + + last_msg_dec_owns(*list); + + *list = g_slist_prepend(*list, rec); +} + +void completion_last_message_add(const char *nick) +{ + g_return_if_fail(nick != NULL); + + last_msg_add(&global_lastmsgs, nick, TRUE, keep_privates_count); +} + +void completion_last_message_remove(const char *nick) +{ + LAST_MSG_REC *rec; + + g_return_if_fail(nick != NULL); + + rec = last_msg_find(global_lastmsgs, nick); + if (rec != NULL) last_msg_destroy(&global_lastmsgs, rec); +} + +void completion_last_message_rename(const char *oldnick, const char *newnick) +{ + LAST_MSG_REC *rec; + + g_return_if_fail(oldnick != NULL); + g_return_if_fail(newnick != NULL); + + rec = last_msg_find(global_lastmsgs, oldnick); + if (rec != NULL) { + g_free(rec->nick); + rec->nick = g_strdup(newnick); + } +} + +static void sig_message_public(SERVER_REC *server, const char *msg, + const char *nick, const char *address, + const char *target) +{ + CHANNEL_REC *channel; + int own; + g_return_if_fail(nick != NULL); + + channel = channel_find(server, target); + if (channel != NULL) { + own = nick_match_msg(channel, msg, server->nick); + CHANNEL_LAST_MSG_ADD(channel, nick, own); + } +} + +static void sig_message_join(SERVER_REC *server, const char *channel, + const char *nick, const char *address) +{ + CHANNEL_REC *chanrec; + g_return_if_fail(nick != NULL); + + chanrec = channel_find(server, channel); + if (chanrec != NULL) + CHANNEL_LAST_MSG_ADD(chanrec, nick, FALSE); +} + +static void sig_message_private(SERVER_REC *server, const char *msg, + const char *nick, const char *address) +{ + g_return_if_fail(server != NULL); + g_return_if_fail(nick != NULL); + + SERVER_LAST_MSG_ADD(server, nick); +} + +static void sig_message_own_public(SERVER_REC *server, const char *msg, + const char *target, const char *origtarget) +{ + CHANNEL_REC *channel; + NICK_REC *nick; + char *p, *msgnick; + + g_return_if_fail(server != NULL); + g_return_if_fail(msg != NULL); + if (target == NULL) return; + + channel = channel_find(server, target); + if (channel == NULL) + return; + + /* channel msg - if first word in line is nick, + add it to lastmsgs */ + p = strchr(msg, ' '); + if (p != NULL && p != msg) { + msgnick = g_strndup(msg, (int) (p-msg)); + nick = nicklist_find(channel, msgnick); + if (nick == NULL && msgnick[1] != '\0') { + /* probably ':' or ',' or some other + char after nick, try without it */ + msgnick[strlen(msgnick)-1] = '\0'; + nick = nicklist_find(channel, msgnick); + } + g_free(msgnick); + if (nick != NULL && nick != channel->ownnick) + CHANNEL_LAST_MSG_ADD(channel, nick->nick, TRUE); + } +} + +static void sig_message_own_private(SERVER_REC *server, const char *msg, + const char *target, const char *origtarget) +{ + g_return_if_fail(server != NULL); + + if (target != NULL && query_find(server, target) == NULL) + SERVER_LAST_MSG_ADD(server, target); +} + +static void sig_nick_removed(CHANNEL_REC *channel, NICK_REC *nick) +{ + MODULE_CHANNEL_REC *mchannel; + LAST_MSG_REC *rec; + + mchannel = MODULE_DATA(channel); + rec = last_msg_find(mchannel->lastmsgs, nick->nick); + if (rec != NULL) last_msg_destroy(&mchannel->lastmsgs, rec); +} + +static void sig_nick_changed(CHANNEL_REC *channel, NICK_REC *nick, + const char *oldnick) +{ + MODULE_CHANNEL_REC *mchannel; + LAST_MSG_REC *rec; + + mchannel = MODULE_DATA(channel); + rec = last_msg_find(mchannel->lastmsgs, oldnick); + if (rec != NULL) { + g_free(rec->nick); + rec->nick = g_strdup(nick->nick); + } +} + +static int last_msg_cmp(LAST_MSG_REC *m1, LAST_MSG_REC *m2) +{ + return m1->time < m2->time ? 1 : -1; +} + +/* Complete /MSG from specified server, or from + global_lastmsgs if server is NULL */ +static void completion_msg_server(GSList **list, SERVER_REC *server, + const char *nick, const char *prefix) +{ + LAST_MSG_REC *msg; + GSList *tmp; + int len; + + g_return_if_fail(nick != NULL); + + len = strlen(nick); + tmp = server == NULL ? global_lastmsgs : + ((MODULE_SERVER_REC *) MODULE_DATA(server))->lastmsgs; + for (; tmp != NULL; tmp = tmp->next) { + LAST_MSG_REC *rec = tmp->data; + + if (len != 0 && g_ascii_strncasecmp(rec->nick, nick, len) != 0) + continue; + + msg = g_new(LAST_MSG_REC, 1); + msg->time = rec->time; + msg->nick = prefix == NULL || *prefix == '\0' ? + g_strdup(rec->nick) : + g_strconcat(prefix, " ", rec->nick, NULL); + *list = g_slist_insert_sorted(*list, msg, + (GCompareFunc) last_msg_cmp); + } +} + +/* convert list of LAST_MSG_REC's to list of char* nicks. */ +static GList *convert_msglist(GSList *msglist) +{ + GList *list; + + list = NULL; + while (msglist != NULL) { + LAST_MSG_REC *rec = msglist->data; + + list = g_list_append(list, rec->nick); + msglist = g_slist_remove(msglist, rec); + g_free(rec); + } + + return list; +} + +/* Complete /MSG - if `find_server' is NULL, complete nicks from all servers */ +GList *completion_msg(SERVER_REC *win_server, + SERVER_REC *find_server, + const char *nick, const char *prefix) +{ + GSList *tmp, *list; + char *newprefix; + + g_return_val_if_fail(nick != NULL, NULL); + if (servers == NULL) return NULL; + + list = NULL; + if (find_server != NULL) { + completion_msg_server(&list, find_server, nick, prefix); + return convert_msglist(list); + } + + completion_msg_server(&list, NULL, nick, prefix); + for (tmp = servers; tmp != NULL; tmp = tmp->next) { + SERVER_REC *rec = tmp->data; + + if (servers->next == NULL && rec == win_server) + newprefix = g_strdup(prefix); + else { + newprefix = prefix == NULL ? + g_strdup_printf("-%s", rec->tag) : + g_strdup_printf("%s -%s", prefix, rec->tag); + } + + completion_msg_server(&list, rec, nick, newprefix); + g_free_not_null(newprefix); + } + + return convert_msglist(list); +} + +static void complete_from_nicklist(GList **outlist, CHANNEL_REC *channel, + const char *nick, const char *suffix, + const int match_case) +{ + MODULE_CHANNEL_REC *mchannel; + GSList *tmp; + GList *ownlist; + char *str; + int len; + + /* go through the last x nicks who have said something in the channel. + nicks of all the "own messages" are placed before others */ + ownlist = NULL; + len = strlen(nick); + mchannel = MODULE_DATA(channel); + for (tmp = mchannel->lastmsgs; tmp != NULL; tmp = tmp->next) { + LAST_MSG_REC *rec = tmp->data; + + if ((match_case ? strncmp(rec->nick, nick, len) : + g_ascii_strncasecmp(rec->nick, nick, len)) == 0 && + (match_case ? i_list_find_string(*outlist, rec->nick) : + i_list_find_icase_string(*outlist, rec->nick)) == NULL) { + str = g_strconcat(rec->nick, suffix, NULL); + if (completion_lowercase) ascii_strdown(str); + if (rec->own) + ownlist = g_list_append(ownlist, str); + else + *outlist = g_list_append(*outlist, str); + } + } + + *outlist = g_list_concat(ownlist, *outlist); +} + +static GList *completion_nicks_nonstrict(CHANNEL_REC *channel, + const char *nick, + const char *suffix, + const int match_case) +{ + GSList *nicks, *tmp; + GList *list; + char *tnick, *str, *in, *out; + int len, str_len, tmplen; + + g_return_val_if_fail(channel != NULL, NULL); + + list = NULL; + + /* get all nicks from current channel, strip non alnum chars, + compare again and add to completion list on matching */ + len = strlen(nick); + nicks = nicklist_getnicks(channel); + + str_len = 80; str = g_malloc(str_len+1); + for (tmp = nicks; tmp != NULL; tmp = tmp->next) { + NICK_REC *rec = tmp->data; + + tmplen = strlen(rec->nick); + if (tmplen > str_len) { + str_len = tmplen*2; + str = g_realloc(str, str_len+1); + } + + /* remove non alnum chars from nick */ + in = rec->nick; out = str; + while (*in != '\0') { + if (i_isalnum(*in)) + *out++ = *in; + in++; + } + *out = '\0'; + + /* add to list if 'cleaned' nick matches */ + if ((match_case? strncmp(str, nick, len) + : g_ascii_strncasecmp(str, nick, len)) == 0) { + tnick = g_strconcat(rec->nick, suffix, NULL); + if (completion_lowercase) + ascii_strdown(tnick); + + if (i_list_find_icase_string(list, tnick) == NULL) + list = g_list_append(list, tnick); + else + g_free(tnick); + } + + } + g_free(str); + g_slist_free(nicks); + + return list; +} + +static GList *completion_channel_nicks(CHANNEL_REC *channel, const char *nick, + const char *suffix) +{ + GSList *nicks, *tmp; + GList *list; + char *str; + int len, match_case; + + g_return_val_if_fail(channel != NULL, NULL); + g_return_val_if_fail(nick != NULL, NULL); + if (*nick == '\0') return NULL; + + if (suffix != NULL && *suffix == '\0') + suffix = NULL; + + match_case = completion_match_case == COMPLETE_MCASE_ALWAYS || + (completion_match_case == COMPLETE_MCASE_AUTO && contains_uppercase(nick)); + + /* put first the nicks who have recently said something */ + list = NULL; + complete_from_nicklist(&list, channel, nick, suffix, match_case); + + /* and add the rest of the nicks too */ + len = strlen(nick); + nicks = nicklist_getnicks(channel); + for (tmp = nicks; tmp != NULL; tmp = tmp->next) { + NICK_REC *rec = tmp->data; + + if ((match_case? strncmp(rec->nick, nick, len) + : g_ascii_strncasecmp(rec->nick, nick, len)) == 0 && + rec != channel->ownnick) { + str = g_strconcat(rec->nick, suffix, NULL); + if (completion_lowercase) + ascii_strdown(str); + if (i_list_find_icase_string(list, str) == NULL) + list = g_list_append(list, str); + else + g_free(str); + } + } + g_slist_free(nicks); + + /* remove non alphanum chars from nick and search again in case + list is still NULL ("foo<tab>" would match "_foo_" f.e.) */ + if (!completion_strict) + list = g_list_concat(list, completion_nicks_nonstrict(channel, nick, suffix, match_case)); + return list; +} + +/* append all strings in list2 to list1 that already aren't there and + free list2 */ +static GList *completion_joinlist(GList *list1, GList *list2) +{ + GList *old; + + old = list2; + while (list2 != NULL) { + if (!i_list_find_icase_string(list1, list2->data)) + list1 = g_list_append(list1, list2->data); + else + g_free(list2->data); + + list2 = list2->next; + } + + g_list_free(old); + return list1; +} + +GList *completion_get_servertags(const char *word) +{ + GList *list; + GSList *tmp; + int len; + + g_return_val_if_fail(word != NULL, NULL); + + len = strlen(word); + list = NULL; + + for (tmp = servers; tmp != NULL; tmp = tmp->next) { + SERVER_REC *rec = tmp->data; + + if (g_ascii_strncasecmp(rec->tag, word, len) == 0) { + if (rec == active_win->active_server) + list = g_list_prepend(list, g_strdup(rec->tag)); + else + list = g_list_append(list, g_strdup(rec->tag)); + } + + } + + return list; +} + +GList *completion_get_channels(SERVER_REC *server, const char *word) +{ + GList *list; + GSList *tmp; + int len; + + g_return_val_if_fail(word != NULL, NULL); + + len = strlen(word); + list = NULL; + + /* first get the joined channels */ + tmp = server == NULL ? NULL : server->channels; + for (; tmp != NULL; tmp = tmp->next) { + CHANNEL_REC *rec = tmp->data; + + if (g_ascii_strncasecmp(rec->visible_name, word, len) == 0) + list = g_list_append(list, g_strdup(rec->visible_name)); + else if (g_ascii_strncasecmp(rec->name, word, len) == 0) + list = g_list_append(list, g_strdup(rec->name)); + } + + /* get channels from setup */ + for (tmp = setupchannels; tmp != NULL; tmp = tmp->next) { + CHANNEL_SETUP_REC *rec = tmp->data; + + if (g_ascii_strncasecmp(rec->name, word, len) == 0 && + i_list_find_icase_string(list, rec->name) == NULL) + list = g_list_append(list, g_strdup(rec->name)); + + } + + return list; +} + +GList *completion_get_aliases(const char *word) +{ + CONFIG_NODE *node; + GList *list; + GSList *tmp; + int len; + + g_return_val_if_fail(word != NULL, NULL); + + len = strlen(word); + list = NULL; + + /* get the list of all aliases */ + node = iconfig_node_traverse("aliases", FALSE); + tmp = node == NULL ? NULL : config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) { + node = tmp->data; + + if (node->type != NODE_TYPE_KEY) + continue; + + if (len != 0 && g_ascii_strncasecmp(node->key, word, len) != 0) + continue; + + list = g_list_append(list, g_strdup(node->key)); + } + + return list; +} + +static void complete_window_nicks(GList **list, WINDOW_REC *window, + const char *word, const char *nicksuffix) +{ + CHANNEL_REC *channel; + GList *tmplist; + GSList *tmp; + + channel = CHANNEL(window->active); + + /* first the active channel */ + if (channel != NULL) { + tmplist = completion_channel_nicks(channel, word, nicksuffix); + *list = completion_joinlist(*list, tmplist); + } + + if (nicksuffix != NULL) { + /* completing nick at the start of line - probably answering + to some other nick, don't even try to complete from + non-active channels */ + return; + } + + /* then the rest */ + for (tmp = window->items; tmp != NULL; tmp = tmp->next) { + channel = CHANNEL(tmp->data); + if (channel != NULL && tmp->data != window->active) { + tmplist = completion_channel_nicks(channel, word, + nicksuffix); + *list = completion_joinlist(*list, tmplist); + } + } +} + +/* Checks if a line is only nicks from autocompletion. + This lets us insert colons only at the beginning of a list + of nicks */ +static int only_nicks(const char *linestart) +{ + int i = 1; + char prev; + + // at the beginning of the line + if (*linestart == '\0') { + return TRUE; + } + + /* completion_char being a whole string introduces loads of edge cases + and can't be handled generally. Skip this case; we handled the + "beginning of line" case already */ + if (completion_char[1] != '\0') + return FALSE; + + /* This would make the completion char get inserted everywhere otherwise */ + if (*completion_char == ' ') + return FALSE; + + /* First ensure that the line is of the format "foo: bar: baz" + we check this by ensuring each space is preceded by a colon or + another space */ + while (linestart[i] != '\0') { + if (linestart[i] == ' ') { + prev = linestart[i - 1]; + if (prev != *completion_char && prev != ' ') + return FALSE; + } + i += 1; + } + + /* There's an edge case here, if we're completing something + like `foo: bar ba<tab>`, then the `linestart` line will end + at "bar", and we'll miss the space. Ensure that the end + of the line is a colon followed by an optional series of spaces */ + i -= 1; + while (i >= 0) { + if (linestart[i] == ' ') { + i--; + continue; + } else if (linestart[i] == *completion_char) { + return TRUE; + } else { + break; + } + } + return FALSE; +} + +static void sig_complete_word(GList **list, WINDOW_REC *window, + const char *word, const char *linestart, + int *want_space) +{ + SERVER_REC *server; + CHANNEL_REC *channel; + QUERY_REC *query; + char *prefix; + + g_return_if_fail(list != NULL); + g_return_if_fail(window != NULL); + g_return_if_fail(word != NULL); + g_return_if_fail(linestart != NULL); + + server = window->active_server; + if (server == NULL && servers != NULL) + server = servers->data; + + if (server != NULL && server_ischannel(server, word)) { + /* probably completing a channel name */ + *list = completion_get_channels(window->active_server, word); + if (*list != NULL) signal_stop(); + return; + } + + server = window->active_server; + if (server == NULL || !server->connected) + return; + + if (*linestart == '\0' && *word == '\0') { + if (!completion_empty_line) + return; + /* pressed TAB at the start of line - add /MSG */ + prefix = g_strdup_printf("%cmsg", *cmdchars); + *list = completion_msg(server, NULL, "", prefix); + if (*list == NULL) + *list = g_list_append(*list, g_strdup(prefix)); + g_free(prefix); + + signal_stop(); + return; + } + + channel = CHANNEL(window->active); + query = QUERY(window->active); + if (channel == NULL && query != NULL && + g_ascii_strncasecmp(word, query->name, strlen(word)) == 0) { + /* completion in query */ + *list = g_list_append(*list, g_strdup(query->name)); + } else if (channel != NULL) { + /* nick completion .. we could also be completing a nick + after /MSG from nicks in channel */ + const char *suffix = only_nicks(linestart) ? completion_char : NULL; + complete_window_nicks(list, window, word, suffix); + } else if (window->level & MSGLEVEL_MSGS) { + /* msgs window, complete /MSG nicks */ + *list = g_list_concat(completion_msg(server, NULL, word, NULL), *list); + } + + if (*list != NULL) signal_stop(); +} + +static SERVER_REC *line_get_server(const char *line) +{ + SERVER_REC *server; + char *tag, *ptr; + + g_return_val_if_fail(line != NULL, NULL); + if (*line != '-') return NULL; + + /* -option found - should be server tag */ + tag = g_strdup(line+1); + ptr = strchr(tag, ' '); + if (ptr != NULL) *ptr = '\0'; + + server = server_find_tag(tag); + + g_free(tag); + return server; +} + +static void sig_complete_msg(GList **list, WINDOW_REC *window, + const char *word, const char *line, + int *want_space) +{ + SERVER_REC *server, *msgserver; + + g_return_if_fail(list != NULL); + g_return_if_fail(word != NULL); + g_return_if_fail(line != NULL); + + server = window->active_server; + if (server == NULL || !server->connected) + return; + + msgserver = line_get_server(line); + *list = completion_msg(server, msgserver, word, NULL); + if (CHANNEL(window->active) != NULL) + complete_window_nicks(list, window, word, NULL); + if (*list != NULL) signal_stop(); +} + +static void sig_erase_complete_msg(WINDOW_REC *window, const char *word, + const char *line) +{ + SERVER_REC *server; + MODULE_SERVER_REC *mserver; + GSList *tmp; + + server = line_get_server(line); + if (server == NULL){ + server = window->active_server; + if (server == NULL) + return; + } + + if (*word == '\0') + return; + + /* check from global list */ + completion_last_message_remove(word); + + /* check from server specific list */ + if (server != NULL) { + mserver = MODULE_DATA(server); + for (tmp = mserver->lastmsgs; tmp != NULL; tmp = tmp->next) { + LAST_MSG_REC *rec = tmp->data; + + if (g_ascii_strcasecmp(rec->nick, word) == 0) { + last_msg_destroy(&mserver->lastmsgs, rec); + break; + } + } + + } +} + +GList *completion_get_chatnets(const char *word) +{ + GList *list; + GSList *tmp; + int len; + + g_return_val_if_fail(word != NULL, NULL); + + len = strlen(word); + list = NULL; + + for (tmp = chatnets; tmp != NULL; tmp = tmp->next) { + CHATNET_REC *rec = tmp->data; + + if (g_ascii_strncasecmp(rec->name, word, len) == 0) + list = g_list_append(list, g_strdup(rec->name)); + } + + return list; +} + +GList *completion_get_servers(const char *word) +{ + GList *list; + GSList *tmp; + int len; + + g_return_val_if_fail(word != NULL, NULL); + + len = strlen(word); + list = NULL; + + for (tmp = setupservers; tmp != NULL; tmp = tmp->next) { + SERVER_SETUP_REC *rec = tmp->data; + + if (g_ascii_strncasecmp(rec->address, word, len) == 0) + list = g_list_append(list, g_strdup(rec->address)); + } + + return list; +} + +GList *completion_get_targets(const char *word) +{ + CONFIG_NODE *node; + GList *list; + GSList *tmp; + int len; + + g_return_val_if_fail(word != NULL, NULL); + + len = strlen(word); + list = NULL; + + /* get the list of all conversion targets */ + node = iconfig_node_traverse("conversions", FALSE); + tmp = node == NULL ? NULL : config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) { + node = tmp->data; + + if (node->type != NODE_TYPE_KEY) + continue; + + if (len != 0 && g_ascii_strncasecmp(node->key, word, len) != 0) + continue; + + list = g_list_append(list, g_strdup(node->key)); + } + + return list; +} + +static void sig_complete_connect(GList **list, WINDOW_REC *window, + const char *word, const char *line, + int *want_space) +{ + g_return_if_fail(list != NULL); + g_return_if_fail(word != NULL); + + *list = completion_get_chatnets(word); + *list = g_list_concat(*list, completion_get_servers(word)); + if (*list != NULL) signal_stop(); +} + +static void sig_complete_tag(GList **list, WINDOW_REC *window, + const char *word, const char *line, + int *want_space) +{ + g_return_if_fail(list != NULL); + g_return_if_fail(word != NULL); + + *list = completion_get_servertags(word); + if (*list != NULL) signal_stop(); +} + +static void sig_complete_topic(GList **list, WINDOW_REC *window, + const char *word, const char *line, + int *want_space) +{ + const char *topic; + + g_return_if_fail(list != NULL); + g_return_if_fail(word != NULL); + + if (*word == '\0' && IS_CHANNEL(window->active)) { + topic = CHANNEL(window->active)->topic; + if (topic != NULL) { + *list = g_list_append(NULL, g_strdup(topic)); + signal_stop(); + } + } +} + +static void sig_complete_away(GList **list, WINDOW_REC *window, + const char *word, const char *line, + int *want_space) +{ + const char *reason; + + g_return_if_fail(list != NULL); + g_return_if_fail(word != NULL); + + *want_space = FALSE; + + if (*word == '\0' && window->active_server != NULL) { + reason = SERVER(window->active_server)->away_reason; + if (reason != NULL) { + *list = g_list_append(NULL, g_strdup(reason)); + signal_stop(); + } + } +} + +static void sig_complete_unalias(GList **list, WINDOW_REC *window, + const char *word, const char *line, + int *want_space) +{ + g_return_if_fail(list != NULL); + g_return_if_fail(word != NULL); + + *list = completion_get_aliases(word); + if (*list != NULL) signal_stop(); +} + +static void sig_complete_alias(GList **list, WINDOW_REC *window, + const char *word, const char *line, + int *want_space) +{ + const char *definition; + + g_return_if_fail(list != NULL); + g_return_if_fail(word != NULL); + g_return_if_fail(line != NULL); + + if (*line != '\0') { + if ((definition = alias_find(line)) != NULL) { + *list = g_list_append(NULL, g_strdup(definition)); + signal_stop(); + } + } else { + *list = completion_get_aliases(word); + if (*list != NULL) signal_stop(); + } +} + +static void sig_complete_window(GList **list, WINDOW_REC *window, + const char *word, const char *linestart, + int *want_space) +{ + WINDOW_REC *win; + WI_ITEM_REC *item; + GSList *tmp; + int len; + + g_return_if_fail(list != NULL); + g_return_if_fail(word != NULL); + + len = strlen(word); + + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + win = tmp->data; + item = win->active; + + if (win->name != NULL && g_ascii_strncasecmp(win->name, word, len) == 0) + *list = g_list_append(*list, g_strdup(win->name)); + if (item != NULL && g_ascii_strncasecmp(item->visible_name, word, len) == 0) + *list = g_list_append(*list, g_strdup(item->visible_name)); + } + + if (*list != NULL) signal_stop(); +} + +static void sig_complete_channel(GList **list, WINDOW_REC *window, + const char *word, const char *line, + int *want_space) +{ + g_return_if_fail(list != NULL); + g_return_if_fail(word != NULL); + + *list = completion_get_channels(NULL, word); + if (*list != NULL) signal_stop(); +} + +static void sig_complete_server(GList **list, WINDOW_REC *window, + const char *word, const char *line, + int *want_space) +{ + g_return_if_fail(list != NULL); + g_return_if_fail(word != NULL); + + *list = completion_get_servers(word); + if (*list != NULL) signal_stop(); +} + +static void sig_complete_target(GList **list, WINDOW_REC *window, + const char *word, const char *line, + int *want_space) +{ + const char *definition; + + g_return_if_fail(list != NULL); + g_return_if_fail(word != NULL); + g_return_if_fail(line != NULL); + + if (*line != '\0') { + if ((definition = iconfig_get_str("conversions", line ,NULL)) != NULL) { + *list = g_list_append(NULL, g_strdup(definition)); + signal_stop(); + } + } else { + *list = completion_get_targets(word); + if (*list != NULL) signal_stop(); + } +} + +static void event_text(const char *data, SERVER_REC *server, WI_ITEM_REC *item); + +/* expand \n, \t and \\ */ +static char *expand_escapes(const char *line, SERVER_REC *server, + WI_ITEM_REC *item) +{ + char *ptr, *ret; + const char *prev; + int chr; + + prev = line; + ret = ptr = g_malloc(strlen(line)+1); + for (; *line != '\0'; line++) { + if (*line != '\\') { + *ptr++ = *line; + continue; + } + + line++; + if (*line == '\0') { + *ptr++ = '\\'; + break; + } + + chr = expand_escape(&line); + if (chr == '\r' || chr == '\n') { + /* newline .. we need to send another "send text" + event to handle it (or actually the text before + the newline..) */ + if (prev != line) { + char *prev_line = g_strndup(prev, (line - prev) - 1); + event_text(prev_line, server, item); + g_free(prev_line); + prev = line + 1; + ptr = ret; + } + } else if (chr != -1) { + /* escaping went ok */ + *ptr++ = chr; + } else { + /* unknown escape, add it as-is */ + *ptr++ = '\\'; + *ptr++ = *line; + } + } + + *ptr = '\0'; + return ret; +} + +static char *auto_complete(CHANNEL_REC *channel, const char *line) +{ + GList *comp; + const char *p; + char *nick, *ret; + + p = strstr(line, completion_char); + if (p == NULL) + return NULL; + + nick = g_strndup(line, (int) (p-line)); + + ret = NULL; + if (nicklist_find(channel, nick) == NULL) { + /* not an exact match, use the first possible completion */ + comp = completion_channel_nicks(channel, nick, NULL); + if (comp != NULL) { + ret = g_strconcat(comp->data, p, NULL); + g_list_foreach(comp, (GFunc) g_free, NULL); + g_list_free(comp); + } + } + + g_free(nick); + + return ret; +} + +static void event_text(const char *data, SERVER_REC *server, WI_ITEM_REC *item) +{ + char *line, *str, *target; + + g_return_if_fail(data != NULL); + + if (item == NULL) + return; + + if (*data == '\0') { + /* empty line, forget it. */ + signal_stop(); + return; + } + + line = settings_get_bool("expand_escapes") ? + expand_escapes(data, server, item) : g_strdup(data); + + /* check for automatic nick completion */ + if (completion_auto && IS_CHANNEL(item)) { + str = auto_complete(CHANNEL(item), line); + if (str != NULL) { + g_free(line); + line = str; + } + } + + /* the nick is quoted in case it contains '-' character. also + spaces should work too now :) The nick is also escaped in case + it contains '\' characters */ + target = escape_string(window_item_get_target(item)); + str = g_strdup_printf(IS_CHANNEL(item) ? "-channel \"%s\" %s" : + IS_QUERY(item) ? "-nick \"%s\" %s" : "%s %s", + target, line); + g_free(target); + + signal_emit("command msg", 3, str, server, item); + + g_free(str); + g_free(line); + + signal_stop(); +} + +static void sig_server_disconnected(SERVER_REC *server) +{ + MODULE_SERVER_REC *mserver; + + g_return_if_fail(server != NULL); + + mserver = MODULE_DATA(server); + if (mserver == NULL) + return; + + while (mserver->lastmsgs) + last_msg_destroy(&mserver->lastmsgs, mserver->lastmsgs->data); +} + +static void sig_channel_destroyed(CHANNEL_REC *channel) +{ + MODULE_CHANNEL_REC *mchannel; + + g_return_if_fail(channel != NULL); + + mchannel = MODULE_DATA(channel); + while (mchannel->lastmsgs != NULL) { + last_msg_destroy(&mchannel->lastmsgs, + mchannel->lastmsgs->data); + } +} + +static void read_settings(void) +{ + keep_privates_count = settings_get_int("completion_keep_privates"); + keep_publics_count = settings_get_int("completion_keep_publics"); + completion_lowercase = settings_get_bool("completion_nicks_lowercase"); + + completion_auto = settings_get_bool("completion_auto"); + completion_strict = settings_get_bool("completion_strict"); + completion_empty_line = settings_get_bool("completion_empty_line"); + + completion_match_case = settings_get_choice("completion_nicks_match_case"); + + g_free_not_null(completion_char); + completion_char = g_strdup(settings_get_str("completion_char")); + + g_free_not_null(cmdchars); + cmdchars = g_strdup(settings_get_str("cmdchars")); + + if (*completion_char == '\0') { + /* this would break.. */ + completion_auto = FALSE; + } +} + +void chat_completion_init(void) +{ + settings_add_str("completion", "completion_char", ":"); + settings_add_bool("completion", "completion_auto", FALSE); + settings_add_int("completion", "completion_keep_publics", 50); + settings_add_int("completion", "completion_keep_privates", 10); + settings_add_bool("completion", "completion_nicks_lowercase", FALSE); + settings_add_bool("completion", "completion_strict", FALSE); + settings_add_bool("completion", "completion_empty_line", TRUE); + settings_add_choice("completion", "completion_nicks_match_case", COMPLETE_MCASE_AUTO, "never;always;auto"); + + settings_add_bool("lookandfeel", "expand_escapes", FALSE); + + read_settings(); + signal_add("complete word", (SIGNAL_FUNC) sig_complete_word); + signal_add("complete command msg", (SIGNAL_FUNC) sig_complete_msg); + signal_add("complete command query", (SIGNAL_FUNC) sig_complete_msg); + signal_add("complete command action", (SIGNAL_FUNC) sig_complete_msg); + signal_add("complete erase command msg", (SIGNAL_FUNC) sig_erase_complete_msg); + signal_add("complete erase command query", (SIGNAL_FUNC) sig_erase_complete_msg); + signal_add("complete erase command action", (SIGNAL_FUNC) sig_erase_complete_msg); + signal_add("complete command connect", (SIGNAL_FUNC) sig_complete_connect); + signal_add("complete command server", (SIGNAL_FUNC) sig_complete_connect); + signal_add("complete command disconnect", (SIGNAL_FUNC) sig_complete_tag); + signal_add("complete command reconnect", (SIGNAL_FUNC) sig_complete_tag); + signal_add("complete command window server", (SIGNAL_FUNC) sig_complete_tag); + signal_add("complete command topic", (SIGNAL_FUNC) sig_complete_topic); + signal_add("complete command away", (SIGNAL_FUNC) sig_complete_away); + signal_add("complete command unalias", (SIGNAL_FUNC) sig_complete_unalias); + signal_add("complete command alias", (SIGNAL_FUNC) sig_complete_alias); + signal_add("complete command window goto", (SIGNAL_FUNC) sig_complete_window); + signal_add("complete command window item move", (SIGNAL_FUNC) sig_complete_channel); + signal_add("complete command server add", (SIGNAL_FUNC) sig_complete_server); + signal_add("complete command server remove", (SIGNAL_FUNC) sig_complete_server); + signal_add("complete command recode remove", (SIGNAL_FUNC) sig_complete_target); + signal_add("message public", (SIGNAL_FUNC) sig_message_public); + signal_add("message join", (SIGNAL_FUNC) sig_message_join); + signal_add("message private", (SIGNAL_FUNC) sig_message_private); + signal_add("message own_public", (SIGNAL_FUNC) sig_message_own_public); + signal_add("message own_private", (SIGNAL_FUNC) sig_message_own_private); + signal_add("nicklist remove", (SIGNAL_FUNC) sig_nick_removed); + signal_add("nicklist changed", (SIGNAL_FUNC) sig_nick_changed); + signal_add("send text", (SIGNAL_FUNC) event_text); + signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected); + signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); +} + +void chat_completion_deinit(void) +{ + while (global_lastmsgs != NULL) + last_msg_destroy(&global_lastmsgs, global_lastmsgs->data); + + signal_remove("complete word", (SIGNAL_FUNC) sig_complete_word); + signal_remove("complete command msg", (SIGNAL_FUNC) sig_complete_msg); + signal_remove("complete command query", (SIGNAL_FUNC) sig_complete_msg); + signal_remove("complete command action", (SIGNAL_FUNC) sig_complete_msg); + signal_remove("complete erase command msg", (SIGNAL_FUNC) sig_erase_complete_msg); + signal_remove("complete erase command query", (SIGNAL_FUNC) sig_erase_complete_msg); + signal_remove("complete erase command action", (SIGNAL_FUNC) sig_erase_complete_msg); + signal_remove("complete command connect", (SIGNAL_FUNC) sig_complete_connect); + signal_remove("complete command server", (SIGNAL_FUNC) sig_complete_connect); + signal_remove("complete command disconnect", (SIGNAL_FUNC) sig_complete_tag); + signal_remove("complete command reconnect", (SIGNAL_FUNC) sig_complete_tag); + signal_remove("complete command window server", (SIGNAL_FUNC) sig_complete_tag); + signal_remove("complete command topic", (SIGNAL_FUNC) sig_complete_topic); + signal_remove("complete command away", (SIGNAL_FUNC) sig_complete_away); + signal_remove("complete command unalias", (SIGNAL_FUNC) sig_complete_unalias); + signal_remove("complete command alias", (SIGNAL_FUNC) sig_complete_alias); + signal_remove("complete command window goto", (SIGNAL_FUNC) sig_complete_window); + signal_remove("complete command window item move", (SIGNAL_FUNC) sig_complete_channel); + signal_remove("complete command server add", (SIGNAL_FUNC) sig_complete_server); + signal_remove("complete command server remove", (SIGNAL_FUNC) sig_complete_server); + signal_remove("complete command recode remove", (SIGNAL_FUNC) sig_complete_target); + signal_remove("message public", (SIGNAL_FUNC) sig_message_public); + signal_remove("message join", (SIGNAL_FUNC) sig_message_join); + signal_remove("message private", (SIGNAL_FUNC) sig_message_private); + signal_remove("message own_public", (SIGNAL_FUNC) sig_message_own_public); + signal_remove("message own_private", (SIGNAL_FUNC) sig_message_own_private); + signal_remove("nicklist remove", (SIGNAL_FUNC) sig_nick_removed); + signal_remove("nicklist changed", (SIGNAL_FUNC) sig_nick_changed); + signal_remove("send text", (SIGNAL_FUNC) event_text); + signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected); + signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); + + g_free_not_null(completion_char); + g_free_not_null(cmdchars); +} diff --git a/src/fe-common/core/chat-completion.h b/src/fe-common/core/chat-completion.h new file mode 100644 index 0000000..2976b96 --- /dev/null +++ b/src/fe-common/core/chat-completion.h @@ -0,0 +1,16 @@ +#ifndef IRSSI_FE_COMMON_CORE_CHAT_COMPLETION_H +#define IRSSI_FE_COMMON_CORE_CHAT_COMPLETION_H + +GList *completion_get_chatnets(const char *word); +GList *completion_get_servers(const char *word); +GList *completion_get_servertags(const char *word); +GList *completion_get_channels(SERVER_REC *server, const char *word); +GList *completion_get_aliases(const char *word); +GList *completion_msg(SERVER_REC *win_server, SERVER_REC *find_server, + const char *nick, const char *prefix); + +void completion_last_message_add(const char *nick); +void completion_last_message_remove(const char *nick); +void completion_last_message_rename(const char *oldnick, const char *newnick); + +#endif diff --git a/src/fe-common/core/command-history.c b/src/fe-common/core/command-history.c new file mode 100644 index 0000000..43d8635 --- /dev/null +++ b/src/fe-common/core/command-history.c @@ -0,0 +1,496 @@ +/* + command-history.c : irssi + + Copyright (C) 1999 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/signals.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/special-vars.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/core/window-items.h> + +#include <irssi/src/fe-common/core/command-history.h> + +/* command history */ +static GList *history_entries; +static HISTORY_REC *global_history; +static int window_history; +static GSList *histories; + +static HISTORY_ENTRY_REC *history_entry_new(HISTORY_REC *history, const char *text) +{ + HISTORY_ENTRY_REC *entry; + + entry = g_new0(HISTORY_ENTRY_REC, 1); + entry->text = g_strdup(text); + entry->history = history; + entry->time = time(NULL); + + return entry; +} + +static void history_entry_destroy(HISTORY_ENTRY_REC *entry) +{ + g_free((char *)entry->text); + g_free(entry); +} + +GList *command_history_list_last(HISTORY_REC *history) +{ + GList *link; + + link = g_list_last(history_entries); + while (link != NULL && history != NULL && ((HISTORY_ENTRY_REC *)link->data)->history != history) { + link = link->prev; + } + + return link; +} + +GList *command_history_list_first(HISTORY_REC *history) +{ + GList *link; + + link = history_entries; + while (link != NULL && history != NULL && ((HISTORY_ENTRY_REC *)link->data)->history != history) { + link = link->next; + } + + return link; +} + +GList *command_history_list_prev(HISTORY_REC *history, GList *pos) +{ + GList *link; + + link = pos != NULL ? pos->prev : NULL; + while (link != NULL && history != NULL && ((HISTORY_ENTRY_REC *)link->data)->history != history) { + link = link->prev; + } + + return link; +} + +GList *command_history_list_next(HISTORY_REC *history, GList *pos) +{ + GList *link; + + link = pos != NULL ? pos->next : NULL; + while (link != NULL && history != NULL && ((HISTORY_ENTRY_REC *)link->data)->history != history) { + link = link->next; + } + + return link; +} + +static void command_history_clear_pos_for_unlink_func(HISTORY_REC *history, GList* link) +{ + if (history->pos == link) { + history->pos = command_history_list_next(history, link); + history->redo = 1; + } +} + +static void history_list_delete_link_and_destroy(GList *link) +{ + g_slist_foreach(histories, + (GFunc) command_history_clear_pos_for_unlink_func, link); + history_entry_destroy(link->data); + history_entries = g_list_delete_link(history_entries, link); +} + +void command_history_add(HISTORY_REC *history, const char *text) +{ + GList *link; + + g_return_if_fail(history != NULL); + g_return_if_fail(text != NULL); + + link = command_history_list_last(history); + if (link != NULL && g_strcmp0(((HISTORY_ENTRY_REC *)link->data)->text, text) == 0) + return; /* same as previous entry */ + + if (settings_get_int("max_command_history") < 1 || + history->lines < settings_get_int("max_command_history")) + history->lines++; + else { + link = command_history_list_first(history); + history_list_delete_link_and_destroy(link); + } + + history_entries = g_list_append(history_entries, history_entry_new(history, text)); +} + +HISTORY_REC *command_history_find(HISTORY_REC *history) +{ + GSList *tmp; + tmp = g_slist_find(histories, history); + + if (tmp == NULL) + return NULL; + else + return tmp->data; +} + +HISTORY_REC *command_history_find_name(const char *name) +{ + GSList *tmp; + + if (name == NULL) + return NULL; + + for (tmp = histories; tmp != NULL; tmp = tmp->next) { + HISTORY_REC *rec = tmp->data; + + if (rec->name != NULL && + g_ascii_strcasecmp(rec->name, name) == 0) + return rec; + } + + return NULL; +} + +static int history_entry_after_time_sort(const HISTORY_ENTRY_REC *a, const HISTORY_ENTRY_REC *b) +{ + return a->time == b->time ? 1 : a->time - b->time; +} + +void command_history_load_entry(time_t history_time, HISTORY_REC *history, const char *text) +{ + HISTORY_ENTRY_REC *entry; + + g_return_if_fail(history != NULL); + g_return_if_fail(text != NULL); + + entry = g_new0(HISTORY_ENTRY_REC, 1); + entry->text = g_strdup(text); + entry->history = history; + entry->time = history_time; + + history->lines++; + + history_entries = g_list_insert_sorted(history_entries, entry, (GCompareFunc)history_entry_after_time_sort); +} + +static int history_entry_find_func(const HISTORY_ENTRY_REC *data, const HISTORY_ENTRY_REC *user_data) +{ + if ((user_data->time == -1 || (data->time == user_data->time)) && + (user_data->history == NULL || (data->history == user_data->history)) && + g_strcmp0(data->text, user_data->text) == 0) { + return 0; + } else { + return -1; + } +} + +gboolean command_history_delete_entry(time_t history_time, HISTORY_REC *history, const char *text) +{ + GList *link; + HISTORY_ENTRY_REC entry; + + g_return_val_if_fail(history != NULL, FALSE); + g_return_val_if_fail(text != NULL, FALSE); + + entry.text = text; + entry.history = history; + entry.time = history_time; + + link = g_list_find_custom(history_entries, &entry, (GCompareFunc)history_entry_find_func); + if (link != NULL) { + ((HISTORY_ENTRY_REC *)link->data)->history->lines--; + history_list_delete_link_and_destroy(link); + return TRUE; + } else { + return FALSE; + } +} + +HISTORY_REC *command_history_current(WINDOW_REC *window) +{ + HISTORY_REC *rec; + + if (window == NULL) + return global_history; + + rec = command_history_find_name(window->history_name); + if (rec != NULL) + return rec; + + if (window_history) + return window->history; + + return global_history; +} + +static const char *command_history_prev_int(WINDOW_REC *window, const char *text, gboolean global) +{ + HISTORY_REC *history; + GList *pos; + + history = command_history_current(window); + pos = history->pos; + history->redo = 0; + + if (pos != NULL) { + /* don't go past the first entry (no wrap around) */ + GList *prev = command_history_list_prev(global ? NULL : history, history->pos); + if (prev != NULL) + history->pos = prev; + } else { + history->pos = command_history_list_last(global ? NULL : history); + } + + if (*text != '\0' && + (pos == NULL || g_strcmp0(((HISTORY_ENTRY_REC *)pos->data)->text, text) != 0)) { + /* save the old entry to history */ + command_history_add(history, text); + } + + return history->pos == NULL ? text : ((HISTORY_ENTRY_REC *)history->pos->data)->text; +} + +const char *command_history_prev(WINDOW_REC *window, const char *text) +{ + return command_history_prev_int(window, text, FALSE); +} + +const char *command_global_history_prev(WINDOW_REC *window, const char *text) +{ + return command_history_prev_int(window, text, TRUE); +} + +static const char *command_history_next_int(WINDOW_REC *window, const char *text, gboolean global) +{ + HISTORY_REC *history; + GList *pos; + + history = command_history_current(window); + pos = history->pos; + + if (!(history->redo) && pos != NULL) + history->pos = command_history_list_next(global ? NULL : history, history->pos); + history->redo = 0; + + if (*text != '\0' && + (pos == NULL || g_strcmp0(((HISTORY_ENTRY_REC *)pos->data)->text, text) != 0)) { + /* save the old entry to history */ + command_history_add(history, text); + } + return history->pos == NULL ? "" : ((HISTORY_ENTRY_REC *)history->pos->data)->text; +} + +const char *command_history_next(WINDOW_REC *window, const char *text) +{ + return command_history_next_int(window, text, FALSE); +} + +const char *command_global_history_next(WINDOW_REC *window, const char *text) +{ + return command_history_next_int(window, text, TRUE); +} + +const char *command_history_delete_current(WINDOW_REC *window, const char *text) +{ + HISTORY_REC *history; + GList *pos; + + history = command_history_current(window); + pos = history->pos; + + if (pos != NULL && g_strcmp0(((HISTORY_ENTRY_REC *)pos->data)->text, text) == 0) { + ((HISTORY_ENTRY_REC *)pos->data)->history->lines--; + history_list_delete_link_and_destroy(pos); + } + + history->redo = 0; + return history->pos == NULL ? "" : ((HISTORY_ENTRY_REC *)history->pos->data)->text; +} + +void command_history_clear_pos_func(HISTORY_REC *history, gpointer user_data) +{ + history->pos = NULL; +} + +void command_history_clear_pos(WINDOW_REC *window) +{ + g_slist_foreach(histories, + (GFunc) command_history_clear_pos_func, NULL); +} + +HISTORY_REC *command_history_create(const char *name) +{ + HISTORY_REC *rec; + + rec = g_new0(HISTORY_REC, 1); + + if (name != NULL) + rec->name = g_strdup(name); + + histories = g_slist_append(histories, rec); + + return rec; +} + +void command_history_clear(HISTORY_REC *history) +{ + GList *link, *next; + + g_return_if_fail(history != NULL); + + command_history_clear_pos_func(history, NULL); + link = command_history_list_first(history); + while (link != NULL) { + next = command_history_list_next(history, link); + history_list_delete_link_and_destroy(link); + link = next; + } + history->lines = 0; +} + +void command_history_destroy(HISTORY_REC *history) +{ + g_return_if_fail(history != NULL); + + /* history->refcount should be 0 here, or somthing is wrong... */ + g_return_if_fail(history->refcount == 0); + + histories = g_slist_remove(histories, history); + command_history_clear(history); + + g_free_not_null(history->name); + g_free(history); +} + +void command_history_link(const char *name) +{ + HISTORY_REC *rec; + rec = command_history_find_name(name); + + if (rec == NULL) + rec = command_history_create(name); + + rec->refcount++; +} + +void command_history_unlink(const char *name) +{ + HISTORY_REC *rec; + rec = command_history_find_name(name); + + if (rec == NULL) + return; + + if (--(rec->refcount) <= 0) + command_history_destroy(rec); +} + +static void sig_window_created(WINDOW_REC *window, int automatic) +{ + window->history = command_history_create(NULL); +} + +static void sig_window_destroyed(WINDOW_REC *window) +{ + command_history_unlink(window->history_name); + command_history_destroy(window->history); + g_free_not_null(window->history_name); +} + +static void sig_window_history_cleared(WINDOW_REC *window, const char *name) { + HISTORY_REC *history; + + if (name == NULL || *name == '\0') { + history = command_history_current(window); + } else { + history = command_history_find_name(name); + } + + command_history_clear(history); +} + +static void sig_window_history_changed(WINDOW_REC *window, const char *oldname) +{ + command_history_link(window->history_name); + command_history_unlink(oldname); +} + +static char *special_history_func(const char *text, void *item, int *free_ret) +{ + WINDOW_REC *window; + HISTORY_REC *history; + GList *tmp; + char *findtext, *ret; + + window = item == NULL ? active_win : window_item_window(item); + + findtext = g_strdup_printf("*%s*", text); + ret = NULL; + + history = command_history_current(window); + for (tmp = command_history_list_first(history); tmp != NULL; tmp = command_history_list_next(history, tmp)) { + const char *line = ((HISTORY_ENTRY_REC *)tmp->data)->text; + + if (match_wildcards(findtext, line)) { + *free_ret = TRUE; + ret = g_strdup(line); + } + } + g_free(findtext); + + return ret; +} + +static void read_settings(void) +{ + window_history = settings_get_bool("window_history"); +} + +void command_history_init(void) +{ + settings_add_int("history", "max_command_history", 100); + settings_add_bool("history", "window_history", FALSE); + + special_history_func_set(special_history_func); + + history_entries = NULL; + + global_history = command_history_create(NULL); + + read_settings(); + signal_add("window created", (SIGNAL_FUNC) sig_window_created); + signal_add("window destroyed", (SIGNAL_FUNC) sig_window_destroyed); + signal_add("window history changed", (SIGNAL_FUNC) sig_window_history_changed); + signal_add_last("window history cleared", (SIGNAL_FUNC) sig_window_history_cleared); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); +} + +void command_history_deinit(void) +{ + signal_remove("window created", (SIGNAL_FUNC) sig_window_created); + signal_remove("window destroyed", (SIGNAL_FUNC) sig_window_destroyed); + signal_remove("window history changed", (SIGNAL_FUNC) sig_window_history_changed); + signal_remove("window history cleared", (SIGNAL_FUNC) sig_window_history_cleared); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); + + command_history_destroy(global_history); + + g_list_free_full(history_entries, (GDestroyNotify) history_entry_destroy); +} diff --git a/src/fe-common/core/command-history.h b/src/fe-common/core/command-history.h new file mode 100644 index 0000000..f404264 --- /dev/null +++ b/src/fe-common/core/command-history.h @@ -0,0 +1,53 @@ +#ifndef IRSSI_FE_COMMON_CORE_COMMAND_HISTORY_H +#define IRSSI_FE_COMMON_CORE_COMMAND_HISTORY_H + +#include <irssi/src/common.h> + +typedef struct { + char *name; + + GList *pos; + int lines; + + int refcount; + unsigned int redo:1; +} HISTORY_REC; + +typedef struct { + const char *text; + HISTORY_REC *history; + time_t time; +} HISTORY_ENTRY_REC; + +HISTORY_REC *command_history_find(HISTORY_REC *history); +HISTORY_REC *command_history_find_name(const char *name); + +HISTORY_REC *command_history_current(WINDOW_REC *window); + +void command_history_init(void); +void command_history_deinit(void); + +void command_history_add(HISTORY_REC *history, const char *text); +void command_history_load_entry(time_t time, HISTORY_REC *history, const char *text); +gboolean command_history_delete_entry(time_t history_time, HISTORY_REC *history, const char *text); + +GList *command_history_list_last(HISTORY_REC *history); +GList *command_history_list_first(HISTORY_REC *history); +GList *command_history_list_prev(HISTORY_REC *history, GList *pos); +GList *command_history_list_next(HISTORY_REC *history, GList *pos); + +const char *command_history_prev(WINDOW_REC *window, const char *text); +const char *command_history_next(WINDOW_REC *window, const char *text); +const char *command_global_history_prev(WINDOW_REC *window, const char *text); +const char *command_global_history_next(WINDOW_REC *window, const char *text); +const char *command_history_delete_current(WINDOW_REC *window, const char *text); + +void command_history_clear_pos(WINDOW_REC *window); + +HISTORY_REC *command_history_create(const char *name); +void command_history_clear(HISTORY_REC *history); +void command_history_destroy(HISTORY_REC *history); +void command_history_link(const char *name); +void command_history_unlink(const char *name); + +#endif diff --git a/src/fe-common/core/completion.c b/src/fe-common/core/completion.c new file mode 100644 index 0000000..e2c4d2a --- /dev/null +++ b/src/fe-common/core/completion.c @@ -0,0 +1,957 @@ +/* + completion.c : irssi + + Copyright (C) 2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/lib-config/iconfig.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/fe-common/core/completion.h> +#include <irssi/src/fe-common/core/printtext.h> + +static GList *complist; /* list of commands we're currently completing */ +static char *last_line; +static int last_want_space, last_line_pos; + +#define isseparator_notspace(c) \ + ((c) == ',') + +#define isseparator_space(c) \ + ((c) == ' ') + +#define isseparator(c) \ + (isseparator_space(c) || isseparator_notspace(c)) + +void chat_completion_init(void); +void chat_completion_deinit(void); + +static const char *completion_find(const char *key, int automatic) +{ + CONFIG_NODE *node; + + node = iconfig_node_traverse("completions", FALSE); + if (node == NULL || node->type != NODE_TYPE_BLOCK) + return NULL; + + node = iconfig_node_section(node, key, -1); + if (node == NULL) + return NULL; + + if (automatic && !config_node_get_bool(node, "auto", FALSE)) + return NULL; + + return config_node_get_str(node, "value", NULL); +} + +/* Return whole word at specified position in string */ +static char *get_word_at(const char *str, int pos, char **startpos) +{ + const char *start, *end; + + g_return_val_if_fail(str != NULL, NULL); + g_return_val_if_fail(pos >= 0, NULL); + + /* get previous word if char at `pos' is space */ + start = str+pos; + while (start > str && isseparator(start[-1])) start--; + + end = start; + while (start > str && !isseparator(start[-1])) start--; + while (*end != '\0' && !isseparator(*end)) end++; + while (*end != '\0' && isseparator_notspace(*end)) end++; + + *startpos = (char *) start; + return g_strndup(start, (int) (end-start)); +} + +/* automatic word completion - called when space/enter is pressed */ +char *auto_word_complete(const char *line, int *pos) +{ + GString *result; + const char *replace; + char *word, *wordstart, *ret; + int startpos; + + g_return_val_if_fail(line != NULL, NULL); + g_return_val_if_fail(pos != NULL, NULL); + + word = get_word_at(line, *pos, &wordstart); + startpos = (int) (wordstart-line); + + result = g_string_new(line); + g_string_erase(result, startpos, strlen(word)); + + /* check for words in autocompletion list */ + replace = completion_find(word, TRUE); + if (replace == NULL || (!g_strcmp0(replace, word))) { + ret = NULL; + g_string_free(result, TRUE); + } else { + *pos = startpos+strlen(replace); + + g_string_insert(result, startpos, replace); + ret = result->str; + g_string_free(result, FALSE); + } + + g_free(word); + return ret; +} + +static void free_completions(void) +{ + complist = g_list_first(complist); + + g_list_foreach(complist, (GFunc) g_free, NULL); + g_list_free(complist); + complist = NULL; + + g_free_and_null(last_line); +} + +/* manual word completion - called when TAB is pressed */ +char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase, int backward) +{ + static int startpos = 0, wordlen = 0; + int old_startpos, old_wordlen; + + GString *result; + const char *cmdchars; + char *word, *wordstart, *linestart, *ret, *data; + int continue_complete, want_space, expand_escapes; + + g_return_val_if_fail(line != NULL, NULL); + g_return_val_if_fail(pos != NULL, NULL); + + continue_complete = complist != NULL && *pos == last_line_pos && + g_strcmp0(line, last_line) == 0; + + if (erase && !continue_complete) + return NULL; + + old_startpos = startpos; + old_wordlen = wordlen; + + if (!erase && continue_complete) { + word = NULL; + linestart = NULL; + } else { + char* old_wordstart; + + /* get the word we want to complete */ + word = get_word_at(line, *pos, &wordstart); + old_wordstart = wordstart; + + startpos = (int) (wordstart-line); + wordlen = strlen(word); + + /* remove trailing spaces from linestart */ + while (wordstart > line && isseparator_space(wordstart[-1])) + wordstart--; + + /* unless everything was spaces */ + if (old_wordstart > line && wordstart == line) + wordstart = old_wordstart - 1; + + linestart = g_strndup(line, (int) (wordstart-line)); + + /* completions usually add space after the word, that makes + things a bit harder. When continuing a completion + "/msg nick1 "<tab> we have to cycle to nick2, etc. + BUT if we start completion with "/msg "<tab>, we don't + want to complete the /msg word, but instead complete empty + word with /msg being in linestart. */ + if (!erase && *pos > 0 && isseparator_space(line[*pos-1]) && + (*linestart == '\0' || !isseparator_space(wordstart[-1]))) { + char *old; + + old = linestart; + /* we want to move word into linestart */ + if (*linestart == '\0') { + linestart = g_strdup(word); + } else { + GString *str = g_string_new(linestart); + if (old_wordstart[-1] != str->str[str->len - 1]) { + /* do not accidentally duplicate the word separator */ + g_string_append_c(str, old_wordstart[-1]); + } + g_string_append(str, word); + linestart = g_string_free(str, FALSE); + } + g_free(old); + + g_free(word); + word = g_strdup(""); + + startpos = *linestart == '\0' ? 0 : + strlen(linestart)+1; + wordlen = 0; + } + + } + + if (erase) { + signal_emit("complete erase", 3, window, word, linestart); + + /* jump to next completion */ + startpos = old_startpos; + wordlen = old_wordlen; + } + + if (continue_complete) { + /* complete from old list */ + if (backward) + complist = complist->prev != NULL ? complist->prev : + g_list_last(complist); + else + complist = complist->next != NULL ? complist->next : + g_list_first(complist); + want_space = last_want_space; + } else { + int keep_word = settings_get_bool("completion_keep_word"); + /* get new completion list */ + free_completions(); + + want_space = TRUE; + signal_emit("complete word", 5, &complist, window, word, linestart, &want_space); + last_want_space = want_space; + + if (complist != NULL) { + /* Remove all nulls (from the signal) before doing further processing */ + complist = g_list_remove_all(g_list_first(complist), NULL); + + if (keep_word) { + complist = g_list_append(complist, g_strdup(word)); + } + + if (backward) { + complist = g_list_last(complist); + if (keep_word) { + complist = complist->prev; + } + } + } + } + + g_free(linestart); + g_free(word); + + if (complist == NULL) + return NULL; + + /* get the cmd char */ + cmdchars = settings_get_str("cmdchars"); + + /* get the expand_escapes setting */ + expand_escapes = settings_get_bool("expand_escapes"); + + /* escape if the word doesn't begin with '/' and expand_escapes are turned on */ + data = strchr(cmdchars, *line) == NULL && expand_escapes ? + escape_string_backslashes(complist->data) : g_strdup(complist->data); + + /* word completed */ + *pos = startpos + strlen(data); + + /* replace the word in line - we need to return + a full new line */ + result = g_string_new(line); + g_string_erase(result, startpos, wordlen); + g_string_insert(result, startpos, data); + + if (want_space) { + if (!isseparator(result->str[*pos])) + g_string_insert_c(result, *pos, ' '); + (*pos)++; + } + + wordlen = strlen(data); + last_line_pos = *pos; + g_free_not_null(last_line); + last_line = g_strdup(result->str); + + ret = result->str; + g_string_free(result, FALSE); + + /* free the data */ + g_free(data); + + return ret; +} + +#define IS_CURRENT_DIR(dir) \ + ((dir)[0] == '.' && ((dir)[1] == '\0' || (dir)[1] == G_DIR_SEPARATOR)) + +#define USE_DEFAULT_PATH(path, default_path) \ + ((!g_path_is_absolute(path) || IS_CURRENT_DIR(path)) && \ + default_path != NULL) + +static GList *list_add_file(GList *list, const char *name, const char *default_path) +{ + struct stat statbuf; + char *fname; + + g_return_val_if_fail(name != NULL, NULL); + + fname = convert_home(name); + if (USE_DEFAULT_PATH(fname, default_path)) { + g_free(fname); + fname = g_strconcat(default_path, G_DIR_SEPARATOR_S, + name, NULL); + } + if (stat(fname, &statbuf) == 0) { + list = g_list_append(list, !S_ISDIR(statbuf.st_mode) ? g_strdup(name) : + g_strconcat(name, G_DIR_SEPARATOR_S, NULL)); + } + + g_free(fname); + return list; +} + +GList *filename_complete(const char *path, const char *default_path) +{ + GList *list; + DIR *dirp; + struct dirent *dp; + char *basename; + char *realpath, *dir, *name; + size_t len; + + g_return_val_if_fail(path != NULL, NULL); + + if (path[0] == '\0') { + return NULL; + } + + list = NULL; + + /* get directory part of the path - expand ~/ */ + realpath = convert_home(path); + if (USE_DEFAULT_PATH(realpath, default_path)) { + g_free(realpath); + realpath = g_strconcat(default_path, G_DIR_SEPARATOR_S, + path, NULL); + } + + /* open directory for reading */ + dir = g_path_get_dirname(realpath); + dirp = opendir(dir); + g_free(dir); + g_free(realpath); + + if (dirp == NULL) + return NULL; + + dir = g_path_get_dirname(path); + if (*dir == G_DIR_SEPARATOR && dir[1] == '\0') { + /* completing file in root directory */ + *dir = '\0'; + } else if (IS_CURRENT_DIR(dir) && !IS_CURRENT_DIR(path)) { + /* completing file in default_path + (path not set, and leave it that way) */ + g_free_and_null(dir); + } + + len = strlen(path); + /* g_path_get_basename() returns the component before the last slash if + * the path ends with a directory separator, that's not what we want */ + if (len > 0 && path[len - 1] == G_DIR_SEPARATOR) { + basename = g_strdup(""); + } else { + basename = g_path_get_basename(path); + } + len = strlen(basename); + + /* add all files in directory to completion list */ + while ((dp = readdir(dirp)) != NULL) { + if (dp->d_name[0] == '.') { + if (dp->d_name[1] == '\0' || + (dp->d_name[1] == '.' && dp->d_name[2] == '\0')) + continue; /* skip . and .. */ + + /* Skip the dotfiles unless the user explicitly asked us + * to do so. Basename might be './', beware of that */ + if (basename[0] != '.' || basename[1] == '\0') + continue; + } + + if (len == 0 || strncmp(dp->d_name, basename, len) == 0) { + name = dir == NULL ? g_strdup(dp->d_name) : + g_strdup_printf("%s"G_DIR_SEPARATOR_S"%s", dir, dp->d_name); + list = list_add_file(list, name, default_path); + g_free(name); + } + } + closedir(dirp); + g_free(basename); + + g_free_not_null(dir); + return list; +} + +static GList *completion_get_settings(const char *key, SettingType type) +{ + GList *complist; + GSList *tmp, *sets; + int len; + + g_return_val_if_fail(key != NULL, NULL); + + sets = settings_get_sorted(); + + len = strlen(key); + complist = NULL; + for (tmp = sets; tmp != NULL; tmp = tmp->next) { + SETTINGS_REC *rec = tmp->data; + + if ((type == SETTING_TYPE_ANY || rec->type == type) && g_ascii_strncasecmp(rec->key, key, len) == 0) + complist = g_list_insert_sorted(complist, g_strdup(rec->key), + (GCompareFunc) i_istr_cmp); + } + g_slist_free(sets); + return complist; +} + +static GList *completion_get_aliases(const char *alias, char cmdchar) +{ + CONFIG_NODE *node; + GList *complist; + GSList *tmp; + char *word; + int len; + + g_return_val_if_fail(alias != NULL, NULL); + + /* get list of aliases from mainconfig */ + node = iconfig_node_traverse("aliases", FALSE); + tmp = node == NULL ? NULL : config_node_first(node->value); + + len = strlen(alias); + complist = NULL; + for (; tmp != NULL; tmp = config_node_next(tmp)) { + CONFIG_NODE *node = tmp->data; + + if (node->type != NODE_TYPE_KEY) + continue; + + if (g_ascii_strncasecmp(node->key, alias, len) == 0) { + word = cmdchar == '\0' ? g_strdup(node->key) : + g_strdup_printf("%c%s", cmdchar, node->key); + /* add matching alias to completion list, aliases will + be appended after command completions and kept in + uppercase to show it's an alias */ + if (i_list_find_icase_string(complist, word) == NULL) + complist = + g_list_insert_sorted(complist, word, (GCompareFunc) i_istr_cmp); + else + g_free(word); + } + } + return complist; +} + +static GList *completion_get_commands(const char *cmd, char cmdchar) +{ + GList *complist; + GSList *tmp; + char *word; + int len; + + g_return_val_if_fail(cmd != NULL, NULL); + + len = strlen(cmd); + complist = NULL; + for (tmp = commands; tmp != NULL; tmp = tmp->next) { + COMMAND_REC *rec = tmp->data; + + if (strchr(rec->cmd, ' ') != NULL) + continue; + + if (g_ascii_strncasecmp(rec->cmd, cmd, len) == 0) { + word = cmdchar == '\0' ? g_strdup(rec->cmd) : + g_strdup_printf("%c%s", cmdchar, rec->cmd); + if (i_list_find_icase_string(complist, word) == NULL) + complist = + g_list_insert_sorted(complist, word, (GCompareFunc) i_istr_cmp); + else + g_free(word); + } + } + return complist; +} + +static GList *completion_get_subcommands(const char *cmd) +{ + GList *complist; + GSList *tmp; + char *spacepos; + int len, skip; + + g_return_val_if_fail(cmd != NULL, NULL); + + /* get the number of chars to skip at the start of command. */ + spacepos = strrchr(cmd, ' '); + skip = spacepos == NULL ? strlen(cmd)+1 : + ((int) (spacepos-cmd) + 1); + + len = strlen(cmd); + complist = NULL; + for (tmp = commands; tmp != NULL; tmp = tmp->next) { + COMMAND_REC *rec = tmp->data; + + if ((int)strlen(rec->cmd) < len) + continue; + + if (strchr(rec->cmd+len, ' ') != NULL) + continue; + + if (g_ascii_strncasecmp(rec->cmd, cmd, len) == 0) + complist = g_list_insert_sorted(complist, g_strdup(rec->cmd + skip), + (GCompareFunc) i_istr_cmp); + } + return complist; +} + +static GList *completion_get_options(const char *cmd, const char *option) +{ + COMMAND_REC *rec; + GList *list; + char **tmp; + int len; + + g_return_val_if_fail(cmd != NULL, NULL); + g_return_val_if_fail(option != NULL, NULL); + + rec = command_find(cmd); + if (rec == NULL || rec->options == NULL) return NULL; + + list = NULL; + len = strlen(option); + for (tmp = rec->options; *tmp != NULL; tmp++) { + const char *optname; + if (**tmp == '~') + continue; /* deprecated or hidden option */ + + optname = *tmp + iscmdtype(**tmp); + + if (len == 0 || g_ascii_strncasecmp(optname, option, len) == 0) + list = g_list_append(list, g_strconcat("-", optname, NULL)); + } + + return list; +} + +/* split the line to command and arguments */ +static char *line_get_command(const char *line, char **args, int aliases) +{ + const char *ptr, *cmdargs; + char *cmd, *checkcmd; + + g_return_val_if_fail(line != NULL, NULL); + g_return_val_if_fail(args != NULL, NULL); + + cmd = checkcmd = NULL; *args = ""; + cmdargs = NULL; ptr = line; + + do { + ptr = strchr(ptr, ' '); + if (ptr == NULL) { + checkcmd = g_strdup(line); + cmdargs = ""; + } else { + checkcmd = g_strndup(line, (int) (ptr-line)); + + while (*ptr == ' ') ptr++; + cmdargs = ptr; + } + + if (aliases ? !alias_find(checkcmd) : + !command_find(checkcmd)) { + /* not found, use the previous */ + g_free(checkcmd); + break; + } + + /* found, check if it has subcommands */ + g_free_not_null(cmd); + if (!aliases) + cmd = checkcmd; + else { + cmd = g_strdup(alias_find(checkcmd)); + g_free(checkcmd); + } + *args = (char *) cmdargs; + } while (ptr != NULL); + + if (cmd != NULL) + ascii_strdown(cmd); + return cmd; +} + +static char *expand_aliases(const char *line) +{ + char *cmd, *args, *ret; + + g_return_val_if_fail(line != NULL, NULL); + + cmd = line_get_command(line, &args, TRUE); + if (cmd == NULL) return g_strdup(line); + if (*args == '\0') return cmd; + + ret = g_strconcat(cmd, " ", args, NULL); + g_free(cmd); + return ret; +} + +static void sig_complete_word(GList **list, WINDOW_REC *window, + const char *word, const char *linestart, + int *want_space) +{ + const char *newword, *cmdchars; + char *signal, *cmd, *args, *line; + + g_return_if_fail(list != NULL); + g_return_if_fail(word != NULL); + g_return_if_fail(linestart != NULL); + + /* check against "completion words" list */ + newword = completion_find(word, FALSE); + if (newword != NULL) { + *list = g_list_append(*list, g_strdup(newword)); + + signal_stop(); + return; + } + + if (*linestart != '\0' && (*word == '/' || *word == '~')) { + /* quite likely filename completion */ + *list = g_list_concat(*list, filename_complete(word, NULL)); + if (*list != NULL) { + *want_space = FALSE; + signal_stop(); + return; + } + } + + /* command completion? */ + cmdchars = settings_get_str("cmdchars"); + if (*word != '\0' && ((*linestart == '\0' && strchr(cmdchars, *word)) || + (*linestart != '\0' && linestart[1] == '\0' && + strchr(cmdchars, *linestart)))) { + gboolean skip = *linestart == '\0' ? TRUE : FALSE; + + /* complete /command */ + *list = completion_get_commands(word + (skip ? 1 : 0), + skip ? *word : '\0'); + + /* complete aliases, too */ + *list = g_list_concat(*list, + completion_get_aliases(word + (skip ? 1 : 0), + skip ? *word : '\0')); + + if (*list != NULL) signal_stop(); + return; + } + + /* check only for /command completions from now on */ + if (*linestart == '\0') + return; + + cmdchars = strchr(cmdchars, *linestart); + if (cmdchars == NULL) return; + + /* check if there's aliases */ + line = linestart[1] == *cmdchars ? g_strdup(linestart+2) : + expand_aliases(linestart+1); + + cmd = line_get_command(line, &args, FALSE); + if (cmd == NULL) { + g_free(line); + return; + } + + /* we're completing -option? */ + if (*word == '-') { + *list = completion_get_options(cmd, word+1); + if (*list != NULL) signal_stop(); + g_free(cmd); + g_free(line); + return; + } + + /* complete parameters */ + signal = g_strconcat("complete command ", cmd, NULL); + signal_emit(signal, 5, list, window, word, args, want_space); + + if (command_have_sub(line)) { + /* complete subcommand */ + g_free(cmd); + cmd = g_strconcat(line, " ", word, NULL); + *list = g_list_concat(completion_get_subcommands(cmd), *list); + } + + if (*list != NULL) signal_stop(); + g_free(signal); + g_free(cmd); + g_free(line); +} + +static void sig_complete_erase(WINDOW_REC *window, const char *word, + const char *linestart) +{ + const char *cmdchars; + char *line, *cmd, *args, *signal; + + if (*linestart == '\0') + return; + + /* we only want to check for commands */ + cmdchars = settings_get_str("cmdchars"); + cmdchars = strchr(cmdchars, *linestart); + if (cmdchars == NULL) + return; + + /* check if there's aliases */ + line = linestart[1] == *cmdchars ? g_strdup(linestart+2) : + expand_aliases(linestart+1); + + cmd = line_get_command(line, &args, FALSE); + if (cmd == NULL) { + g_free(line); + return; + } + + signal = g_strconcat("complete erase command ", cmd, NULL); + signal_emit(signal, 3, window, word, args); + + g_free(signal); + g_free(cmd); + g_free(line); +} + +static void sig_complete_set(GList **list, WINDOW_REC *window, + const char *word, const char *line, int *want_space) +{ + g_return_if_fail(list != NULL); + g_return_if_fail(word != NULL); + g_return_if_fail(line != NULL); + + if (*line == '\0' || + !g_strcmp0("-clear", line) || !g_strcmp0("-default", line)) + *list = completion_get_settings(word, SETTING_TYPE_ANY); + else if (*line != '\0' && *word == '\0') { + SETTINGS_REC *rec = settings_get_record(line); + if (rec != NULL) { + char *value = settings_get_print(rec); + + /* show the current option first */ + if (value != NULL) + *list = g_list_append(*list, value); + + /* show the whole list of valid options */ + if (rec->type == SETTING_TYPE_CHOICE) { + char **tmp; + + for (tmp = rec->choices; *tmp; tmp++) { + if (g_ascii_strcasecmp(*tmp, value) != 0) + *list = g_list_append(*list, g_strdup(*tmp)); + } + } + } + } + + if (*list != NULL) signal_stop(); +} + +static void sig_complete_toggle(GList **list, WINDOW_REC *window, + const char *word, const char *line, int *want_space) +{ + g_return_if_fail(list != NULL); + g_return_if_fail(word != NULL); + g_return_if_fail(line != NULL); + + if (*line != '\0') return; + + *list = completion_get_settings(word, SETTING_TYPE_BOOLEAN); + if (*list != NULL) signal_stop(); +} + +/* first argument of command is file name - complete it */ +static void sig_complete_filename(GList **list, WINDOW_REC *window, + const char *word, const char *line, int *want_space) +{ + g_return_if_fail(list != NULL); + g_return_if_fail(word != NULL); + g_return_if_fail(line != NULL); + + if (*line != '\0') return; + + *list = filename_complete(word, NULL); + if (*list != NULL) { + *want_space = FALSE; + signal_stop(); + } +} + +/* first argument of command is .. command :) (/HELP command) */ +static void sig_complete_command(GList **list, WINDOW_REC *window, + const char *word, const char *line, int *want_space) +{ + char *cmd; + + g_return_if_fail(list != NULL); + g_return_if_fail(word != NULL); + g_return_if_fail(line != NULL); + + if (*line == '\0') { + /* complete base command */ + *list = completion_get_commands(word, '\0'); + } else if (command_have_sub(line)) { + /* complete subcommand */ + cmd = g_strconcat(line, " ", word, NULL); + *list = completion_get_subcommands(cmd); + g_free(cmd); + } + + if (*list != NULL) signal_stop(); +} + +/* SYNTAX: COMPLETION [-auto] [-delete] <key> <value> */ +static void cmd_completion(const char *data) +{ + GHashTable *optlist; + CONFIG_NODE *node; + GSList *tmp; + char *key, *value; + void *free_arg; + int len; + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS | + PARAM_FLAG_GETREST, + "completion", &optlist, &key, &value)) + return; + + node = iconfig_node_traverse("completions", *value != '\0'); + if (node != NULL && node->type != NODE_TYPE_BLOCK) { + /* FIXME: remove after 0.8.5 */ + iconfig_node_remove(mainconfig->mainnode, node); + node = iconfig_node_traverse("completions", *value != '\0'); + } + + if (node == NULL || (node->value == NULL && *value == '\0')) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_NO_COMPLETIONS); + cmd_params_free(free_arg); + return; + } + + if (g_hash_table_lookup(optlist, "delete") != NULL && *key != '\0') { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_COMPLETION_REMOVED, key); + + iconfig_set_str("completions", key, NULL); + signal_emit("completion removed", 1, key); + } else if (*key != '\0' && *value != '\0') { + int automatic = g_hash_table_lookup(optlist, "auto") != NULL; + + node = iconfig_node_section(node, key, NODE_TYPE_BLOCK); + iconfig_node_set_str(node, "value", value); + if (automatic) + iconfig_node_set_bool(node, "auto", TRUE); + else + iconfig_node_set_str(node, "auto", NULL); + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, + TXT_COMPLETION_LINE, + key, value, automatic ? "yes" : "no"); + + signal_emit("completion added", 1, key); + } else { + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, + TXT_COMPLETION_HEADER); + + len = strlen(key); + for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + node = tmp->data; + + if (len == 0 || + g_ascii_strncasecmp(node->key, key, len) == 0) { + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, + TXT_COMPLETION_LINE, node->key, + config_node_get_str(node, "value", ""), + config_node_get_bool(node, "auto", FALSE) ? "yes" : "no"); + } + } + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, + TXT_COMPLETION_FOOTER); + } + + cmd_params_free(free_arg); +} + +void completion_init(void) +{ + complist = NULL; + last_line = NULL; last_line_pos = -1; + + chat_completion_init(); + + settings_add_bool("completion", "completion_keep_word", TRUE); + command_bind("completion", NULL, (SIGNAL_FUNC) cmd_completion); + + signal_add_first("complete word", (SIGNAL_FUNC) sig_complete_word); + signal_add_first("complete erase", (SIGNAL_FUNC) sig_complete_erase); + signal_add("complete command set", (SIGNAL_FUNC) sig_complete_set); + signal_add("complete command toggle", (SIGNAL_FUNC) sig_complete_toggle); + signal_add("complete command load", (SIGNAL_FUNC) sig_complete_filename); + signal_add("complete command cat", (SIGNAL_FUNC) sig_complete_filename); + signal_add("complete command save", (SIGNAL_FUNC) sig_complete_filename); + signal_add("complete command reload", (SIGNAL_FUNC) sig_complete_filename); + signal_add("complete command rawlog open", (SIGNAL_FUNC) sig_complete_filename); + signal_add("complete command rawlog save", (SIGNAL_FUNC) sig_complete_filename); + signal_add("complete command help", (SIGNAL_FUNC) sig_complete_command); + + command_set_options("completion", "auto delete"); +} + +void completion_deinit(void) +{ + free_completions(); + + chat_completion_deinit(); + + command_unbind("completion", (SIGNAL_FUNC) cmd_completion); + + signal_remove("complete word", (SIGNAL_FUNC) sig_complete_word); + signal_remove("complete erase", (SIGNAL_FUNC) sig_complete_erase); + signal_remove("complete command set", (SIGNAL_FUNC) sig_complete_set); + signal_remove("complete command toggle", (SIGNAL_FUNC) sig_complete_toggle); + signal_remove("complete command load", (SIGNAL_FUNC) sig_complete_filename); + signal_remove("complete command cat", (SIGNAL_FUNC) sig_complete_filename); + signal_remove("complete command save", (SIGNAL_FUNC) sig_complete_filename); + signal_remove("complete command reload", (SIGNAL_FUNC) sig_complete_filename); + signal_remove("complete command rawlog open", (SIGNAL_FUNC) sig_complete_filename); + signal_remove("complete command rawlog save", (SIGNAL_FUNC) sig_complete_filename); + signal_remove("complete command help", (SIGNAL_FUNC) sig_complete_command); +} diff --git a/src/fe-common/core/completion.h b/src/fe-common/core/completion.h new file mode 100644 index 0000000..d505aa8 --- /dev/null +++ b/src/fe-common/core/completion.h @@ -0,0 +1,18 @@ +#ifndef IRSSI_FE_COMMON_CORE_COMPLETION_H +#define IRSSI_FE_COMMON_CORE_COMPLETION_H + +#include <irssi/src/fe-common/core/window-items.h> + +/* automatic word completion - called when space/enter is pressed */ +char *auto_word_complete(const char *line, int *pos); +/* manual word completion - called when TAB is pressed. if erase is TRUE, + the word is removed from completion list entirely (if possible) and + next completion is used */ +char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase, int backward); + +GList *filename_complete(const char *path, const char *default_path); + +void completion_init(void); +void completion_deinit(void); + +#endif diff --git a/src/fe-common/core/fe-capsicum.c b/src/fe-common/core/fe-capsicum.c new file mode 100644 index 0000000..d7ea95e --- /dev/null +++ b/src/fe-common/core/fe-capsicum.c @@ -0,0 +1,63 @@ +/* + fe-capsicum.c : irssi + + Copyright (C) 2017 Edward Tomasz Napierala <trasz@FreeBSD.org> + + This software was developed by SRI International and the University of + Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 + ("CTSRD"), as part of the DARPA CRASH research programme. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/core/fe-capsicum.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/fe-common/core/printtext.h> +#include <irssi/src/core/signals.h> + +static void capability_mode_enabled(void) +{ + + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CAPSICUM_ENABLED); +} + +static void capability_mode_disabled(void) +{ + + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CAPSICUM_DISABLED); +} + +static void capability_mode_failed(gchar *msg) +{ + + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_CAPSICUM_FAILED, msg); +} + +void fe_capsicum_init(void) +{ + + signal_add("capability mode enabled", (SIGNAL_FUNC) capability_mode_enabled); + signal_add("capability mode disabled", (SIGNAL_FUNC) capability_mode_disabled); + signal_add("capability mode failed", (SIGNAL_FUNC) capability_mode_failed); +} + +void fe_capsicum_deinit(void) +{ + signal_remove("capability mode enabled", (SIGNAL_FUNC) capability_mode_enabled); + signal_remove("capability mode disabled", (SIGNAL_FUNC) capability_mode_disabled); + signal_remove("capability mode failed", (SIGNAL_FUNC) capability_mode_failed); +} diff --git a/src/fe-common/core/fe-capsicum.h b/src/fe-common/core/fe-capsicum.h new file mode 100644 index 0000000..c047129 --- /dev/null +++ b/src/fe-common/core/fe-capsicum.h @@ -0,0 +1,7 @@ +#ifndef IRSSI_FE_COMMON_CORE_FE_CAPSICUM_H +#define IRSSI_FE_COMMON_CORE_FE_CAPSICUM_H + +void fe_capsicum_init(void); +void fe_capsicum_deinit(void); + +#endif diff --git a/src/fe-common/core/fe-channels.c b/src/fe-common/core/fe-channels.c new file mode 100644 index 0000000..2d4e9ec --- /dev/null +++ b/src/fe-common/core/fe-channels.c @@ -0,0 +1,674 @@ +/* + fe-channels.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/core/modules.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/settings.h> +#include <irssi/src/core/special-vars.h> +#include <irssi/src/core/utf8.h> + +#include <irssi/src/core/chat-protocols.h> +#include <irssi/src/core/chatnets.h> +#include <irssi/src/core/servers.h> +#include <irssi/src/core/channels.h> +#include <irssi/src/core/channels-setup.h> +#include <irssi/src/core/nicklist.h> + +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/core/fe-channels.h> +#include <irssi/src/fe-common/core/window-items.h> +#include <irssi/src/fe-common/core/printtext.h> + +static void signal_channel_created(CHANNEL_REC *channel, void *automatic) +{ + if (window_item_window(channel) == NULL) { + window_item_create((WI_ITEM_REC *) channel, + GPOINTER_TO_INT(automatic)); + } +} + +static void signal_channel_created_curwin(CHANNEL_REC *channel) +{ + g_return_if_fail(channel != NULL); + + window_item_add(active_win, (WI_ITEM_REC *) channel, FALSE); +} + +static void signal_channel_destroyed(CHANNEL_REC *channel) +{ + WINDOW_REC *window; + + g_return_if_fail(channel != NULL); + + window = window_item_window((WI_ITEM_REC *) channel); + if (window == NULL) + return; + + window_item_destroy((WI_ITEM_REC *) channel); + + if (channel->joined && !channel->left && + !channel->server->disconnected) { + /* kicked out from channel */ + window_bind_add(window, channel->server->tag, + channel->visible_name); + } else if (!channel->joined || channel->left) + window_auto_destroy(window); +} + +static void sig_disconnected(SERVER_REC *server) +{ + WINDOW_REC *window; + GSList *tmp; + + g_return_if_fail(IS_SERVER(server)); + + for (tmp = server->channels; tmp != NULL; tmp = tmp->next) { + CHANNEL_REC *channel = tmp->data; + + window = window_item_window((WI_ITEM_REC *) channel); + window_bind_add(window, server->tag, channel->name); + } +} + +static void signal_window_item_changed(WINDOW_REC *window, WI_ITEM_REC *item) +{ + g_return_if_fail(window != NULL); + if (item == NULL) return; + + if (g_slist_length(window->items) > 1 && IS_CHANNEL(item)) { + printformat(item->server, item->visible_name, + MSGLEVEL_CLIENTNOTICE, + TXT_TALKING_IN, item->visible_name); + signal_stop(); + } +} + +static void sig_channel_joined(CHANNEL_REC *channel) +{ + if (settings_get_bool("show_names_on_join") && !channel->session_rejoin) { + int limit = settings_get_int("show_names_on_join_limit"); + int flags = CHANNEL_NICKLIST_FLAG_ALL; + if (limit > 0 && g_hash_table_size(channel->nicks) > limit) { + flags |= CHANNEL_NICKLIST_FLAG_COUNT; + } + fe_channels_nicklist(channel, flags); + } +} + +/* SYNTAX: JOIN [-window] [-invite] [-<server tag>] <channels> [<keys>] */ +static void cmd_join(const char *data, SERVER_REC *server) +{ + WINDOW_REC *window; + CHANNEL_REC *channel; + GHashTable *optlist; + char *pdata; + int invite; + int samewindow; + void *free_arg; + + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS | + PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST | + PARAM_FLAG_STRIP_TRAILING_WS, + "join", &optlist, &pdata)) + return; + + invite = g_hash_table_lookup(optlist, "invite") != NULL; + samewindow = g_hash_table_lookup(optlist, "window") != NULL; + if (!invite && *pdata == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + /* -<server tag> */ + server = cmd_options_get_server("join", optlist, server); + + channel = channel_find(server, pdata); + if (channel != NULL) { + /* already joined to channel, set it active */ + window = window_item_window(channel); + if (window != active_win) + window_set_active(window); + + window_item_set_active(active_win, (WI_ITEM_REC *) channel); + } + else { + if (server == NULL || !server->connected) + cmd_param_error(CMDERR_NOT_CONNECTED); + if (invite) { + if (server->last_invite == NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_NOT_INVITED); + signal_stop(); + cmd_params_free(free_arg); + return; + } + pdata = server->last_invite; + } + if (samewindow) + signal_add("channel created", + (SIGNAL_FUNC) signal_channel_created_curwin); + server->channels_join(server, pdata, FALSE); + if (samewindow) + signal_remove("channel created", + (SIGNAL_FUNC) signal_channel_created_curwin); + } + cmd_params_free(free_arg); +} + +static void cmd_channel_list_joined(void) +{ + CHANNEL_REC *channel; + GString *nicks; + GSList *nicklist, *tmp, *ntmp; + + if (channels == NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_NOT_IN_CHANNELS); + return; + } + + /* print active channel */ + channel = CHANNEL(active_win->active); + if (channel != NULL) + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_CURRENT_CHANNEL, channel->visible_name); + + /* print list of all channels, their modes, server tags and nicks */ + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_CHANLIST_HEADER); + for (tmp = channels; tmp != NULL; tmp = tmp->next) { + channel = tmp->data; + + nicklist = nicklist_getnicks(channel); + nicks = g_string_new(NULL); + for (ntmp = nicklist; ntmp != NULL; ntmp = ntmp->next) { + NICK_REC *rec = ntmp->data; + + g_string_append_printf(nicks, "%s ", rec->nick); + } + + if (nicks->len > 1) g_string_truncate(nicks, nicks->len-1); + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_CHANLIST_LINE, + channel->visible_name, channel->mode, + channel->server->tag, nicks->str); + + g_slist_free(nicklist); + g_string_free(nicks, TRUE); + } +} + +/* SYNTAX: CHANNEL LIST */ +static void cmd_channel_list(void) +{ + GString *str; + GSList *tmp; + + str = g_string_new(NULL); + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_CHANSETUP_HEADER); + for (tmp = setupchannels; tmp != NULL; tmp = tmp->next) { + CHANNEL_SETUP_REC *rec = tmp->data; + + g_string_truncate(str, 0); + if (rec->autojoin) + g_string_append(str, "autojoin, "); + if (rec->botmasks != NULL && *rec->botmasks != '\0') + g_string_append_printf(str, "bots: %s, ", rec->botmasks); + if (rec->autosendcmd != NULL && *rec->autosendcmd != '\0') + g_string_append_printf(str, "botcmd: %s, ", rec->autosendcmd); + + if (str->len > 2) g_string_truncate(str, str->len-2); + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_CHANSETUP_LINE, + rec->name, rec->chatnet == NULL ? "" : rec->chatnet, + rec->password == NULL ? "" : rec->password, str->str); + } + g_string_free(str, TRUE); + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_CHANSETUP_FOOTER); +} + +static void cmd_channel(const char *data, SERVER_REC *server, WI_ITEM_REC *item) +{ + if (*data == '\0') + cmd_channel_list_joined(); + else if (server != NULL && server_ischannel(server, data)) { + signal_emit("command join", 3, data, server, item); + } else { + command_runsub("channel", data, server, item); + } +} + +static void cmd_channel_add_modify(const char *data, gboolean add) +{ + GHashTable *optlist; + CHATNET_REC *chatnetrec; + CHANNEL_SETUP_REC *rec; + char *botarg, *botcmdarg, *chatnet, *channel, *password; + void *free_arg; + + if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS, + "channel add", &optlist, &channel, &chatnet, &password)) + return; + + if (*chatnet == '\0' || *channel == '\0') { + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + } + + chatnetrec = chatnet_find(chatnet); + if (chatnetrec == NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_UNKNOWN_CHATNET, chatnet); + cmd_params_free(free_arg); + return; + } + + botarg = g_hash_table_lookup(optlist, "bots"); + botcmdarg = g_hash_table_lookup(optlist, "botcmd"); + + rec = channel_setup_find(channel, chatnet); + if (rec == NULL) { + if (add == FALSE) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_CHANSETUP_NOT_FOUND, channel, chatnet); + cmd_params_free(free_arg); + return; + } + + rec = CHAT_PROTOCOL(chatnetrec)->create_channel_setup(); + rec->name = g_strdup(channel); + rec->chatnet = g_strdup(chatnet); + } else { + if (g_hash_table_lookup(optlist, "bots")) g_free_and_null(rec->botmasks); + if (g_hash_table_lookup(optlist, "botcmd")) g_free_and_null(rec->autosendcmd); + if (*password != '\0') g_free_and_null(rec->password); + } + if (g_hash_table_lookup(optlist, "auto")) rec->autojoin = TRUE; + if (g_hash_table_lookup(optlist, "noauto")) rec->autojoin = FALSE; + if (botarg != NULL && *botarg != '\0') rec->botmasks = g_strdup(botarg); + if (botcmdarg != NULL && *botcmdarg != '\0') rec->autosendcmd = g_strdup(botcmdarg); + if (*password != '\0' && g_strcmp0(password, "-") != 0) rec->password = g_strdup(password); + + signal_emit("channel add fill", 2, rec, optlist); + + channel_setup_create(rec); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_CHANSETUP_ADDED, channel, chatnet); + + cmd_params_free(free_arg); +} + +/* SYNTAX: CHANNEL ADD|MODIFY [-auto | -noauto] [-bots <masks>] [-botcmd <command>] + <channel> <network> [<password>] */ +static void cmd_channel_add(const char *data) +{ + cmd_channel_add_modify(data, TRUE); +} + +static void cmd_channel_modify(const char *data) +{ + cmd_channel_add_modify(data, FALSE); +} + +/* SYNTAX: CHANNEL REMOVE <channel> <network> */ +static void cmd_channel_remove(const char *data) +{ + CHANNEL_SETUP_REC *rec; + char *chatnet, *channel; + void *free_arg; + + if (!cmd_get_params(data, &free_arg, 2, &channel, &chatnet)) + return; + if (*chatnet == '\0' || *channel == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + rec = channel_setup_find(channel, chatnet); + if (rec == NULL) + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CHANSETUP_NOT_FOUND, channel, chatnet); + else { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CHANSETUP_REMOVED, channel, chatnet); + channel_setup_remove(rec); + } + cmd_params_free(free_arg); +} + +static int get_nick_length(void *data) +{ + return string_width(((NICK_REC *) data)->nick, -1); +} + +static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist) +{ + WINDOW_REC *window; + TEXT_DEST_REC dest; + GString *str; + GSList *tmp; + char *format, *stripped, *prefix_format; + char *aligned_nick, nickmode[2] = { 0, 0 }; + int *columns, cols, rows, last_col_rows, col, row, max_width; + int item_extra, formatnum; + + window = window_find_closest(channel->server, channel->visible_name, + MSGLEVEL_CLIENTCRAP); + max_width = window->width; + + /* get the length of item extra stuff ("[ ] ") */ + format = format_get_text(MODULE_NAME, NULL, + channel->server, channel->visible_name, + TXT_NAMES_NICK, " ", ""); + stripped = strip_codes(format); + item_extra = strlen(stripped); + g_free(stripped); + g_free(format); + + if (settings_get_int("names_max_width") > 0 && + settings_get_int("names_max_width") < max_width) + max_width = settings_get_int("names_max_width"); + + /* remove width of the timestamp from max_width */ + format_create_dest(&dest, channel->server, channel->visible_name, + MSGLEVEL_CLIENTCRAP, NULL); + format = format_get_line_start(current_theme, &dest, time(NULL)); + if (format != NULL) { + stripped = strip_codes(format); + max_width -= strlen(stripped); + g_free(stripped); + g_free(format); + } + + /* remove width of the prefix from max_width */ + prefix_format = format_get_text(MODULE_NAME, NULL, + channel->server, channel->visible_name, + TXT_NAMES_PREFIX, + channel->visible_name); + if (prefix_format != NULL) { + stripped = strip_codes(prefix_format); + max_width -= strlen(stripped); + g_free(stripped); + } + + if (max_width <= 0) { + /* we should always have at least some space .. if we + really don't, it won't show properly anyway. */ + max_width = 10; + } + + /* calculate columns */ + cols = get_max_column_count(nicklist, get_nick_length, max_width, + settings_get_int("names_max_columns"), + item_extra, 3, &columns, &rows); + nicklist = columns_sort_list(nicklist, rows); + + /* rows in last column */ + last_col_rows = rows-(cols*rows-g_slist_length(nicklist)); + if (last_col_rows == 0) + last_col_rows = rows; + + str = g_string_new(prefix_format); + + col = 0; row = 0; + for (tmp = nicklist; tmp != NULL; tmp = tmp->next) { + NICK_REC *rec = tmp->data; + + if (rec->prefixes[0]) + nickmode[0] = rec->prefixes[0]; + else + nickmode[0] = ' '; + + aligned_nick = get_alignment(rec->nick, + columns[col]-item_extra, + ALIGN_PAD, ' '); + + formatnum = rec->op ? TXT_NAMES_NICK_OP : + rec->halfop ? TXT_NAMES_NICK_HALFOP : + rec->voice ? TXT_NAMES_NICK_VOICE : + TXT_NAMES_NICK; + format = format_get_text(MODULE_NAME, NULL, + channel->server, + channel->visible_name, + formatnum, nickmode, aligned_nick); + g_string_append(str, format); + g_free(aligned_nick); + g_free(format); + + if (++col == cols) { + printtext(channel->server, channel->visible_name, + MSGLEVEL_CLIENTCRAP, "%s", str->str); + g_string_truncate(str, 0); + if (prefix_format != NULL) + g_string_assign(str, prefix_format); + col = 0; row++; + + if (row == last_col_rows) + cols--; + } + } + + if (prefix_format != NULL && str->len > strlen(prefix_format)) { + printtext(channel->server, channel->visible_name, + MSGLEVEL_CLIENTCRAP, "%s", str->str); + } + + g_slist_free(nicklist); + g_string_free(str, TRUE); + g_free_not_null(columns); + g_free_not_null(prefix_format); +} + +void fe_channels_nicklist(CHANNEL_REC *channel, int flags) +{ + NICK_REC *nick; + GSList *tmp, *nicklist, *sorted; + int nicks, normal, voices, halfops, ops; + const char *nick_flags; + + nicks = normal = voices = halfops = ops = 0; + nicklist = nicklist_getnicks(channel); + sorted = NULL; + nick_flags = channel->server->get_nick_flags(channel->server); + + /* filter (for flags) and count ops, halfops, voices */ + for (tmp = nicklist; tmp != NULL; tmp = tmp->next) { + nick = tmp->data; + + nicks++; + if (nick->op) { + ops++; + if ((flags & CHANNEL_NICKLIST_FLAG_OPS) == 0) + continue; + } else if (nick->halfop) { + halfops++; + if ((flags & CHANNEL_NICKLIST_FLAG_HALFOPS) == 0) + continue; + } else if (nick->voice) { + voices++; + if ((flags & CHANNEL_NICKLIST_FLAG_VOICES) == 0) + continue; + } else { + normal++; + if ((flags & CHANNEL_NICKLIST_FLAG_NORMAL) == 0) + continue; + } + + sorted = g_slist_prepend(sorted, nick); + } + g_slist_free(nicklist); + + /* sort the nicklist */ + sorted = g_slist_sort_with_data(sorted, (GCompareDataFunc) nicklist_compare, (void *)nick_flags); + + /* display the nicks */ + if ((flags & CHANNEL_NICKLIST_FLAG_COUNT) == 0) { + printformat(channel->server, channel->visible_name, + MSGLEVEL_CLIENTCRAP, TXT_NAMES, + channel->visible_name, + nicks, ops, halfops, voices, normal); + display_sorted_nicks(channel, sorted); + } + g_slist_free(sorted); + + printformat(channel->server, channel->visible_name, + MSGLEVEL_CLIENTNOTICE, TXT_ENDOFNAMES, + channel->visible_name, nicks, ops, halfops, voices, normal); +} + +/* SYNTAX: NAMES [-count | -ops -halfops -voices -normal] [<channels> | **] */ +static void cmd_names(const char *data, SERVER_REC *server, WI_ITEM_REC *item) +{ + CHANNEL_REC *chanrec; + GHashTable *optlist; + GString *unknowns; + char *channel, **channels, **tmp; + int flags; + void *free_arg; + + g_return_if_fail(data != NULL); + if (!IS_SERVER(server) || !server->connected) + cmd_return_error(CMDERR_NOT_CONNECTED); + + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS, + "names", &optlist, &channel)) + return; + + if (g_strcmp0(channel, "*") == 0 || *channel == '\0') { + if (!IS_CHANNEL(item)) + cmd_param_error(CMDERR_NOT_JOINED); + + channel = CHANNEL(item)->name; + } + + flags = 0; + if (g_hash_table_lookup(optlist, "ops") != NULL) + flags |= CHANNEL_NICKLIST_FLAG_OPS; + if (g_hash_table_lookup(optlist, "halfops") != NULL) + flags |= CHANNEL_NICKLIST_FLAG_HALFOPS; + if (g_hash_table_lookup(optlist, "voices") != NULL) + flags |= CHANNEL_NICKLIST_FLAG_VOICES; + if (g_hash_table_lookup(optlist, "normal") != NULL) + flags |= CHANNEL_NICKLIST_FLAG_NORMAL; + if (g_hash_table_lookup(optlist, "count") != NULL) + flags |= CHANNEL_NICKLIST_FLAG_COUNT; + + if (flags == 0) flags = CHANNEL_NICKLIST_FLAG_ALL; + + unknowns = g_string_new(NULL); + + channels = g_strsplit(channel, ",", -1); + for (tmp = channels; *tmp != NULL; tmp++) { + chanrec = channel_find(server, *tmp); + if (chanrec == NULL) + g_string_append_printf(unknowns, "%s,", *tmp); + else { + fe_channels_nicklist(chanrec, flags); + signal_stop(); + } + } + g_strfreev(channels); + + if (unknowns->len > 1) + g_string_truncate(unknowns, unknowns->len-1); + + if (unknowns->len > 0 && g_strcmp0(channel, unknowns->str) != 0) + signal_emit("command names", 3, unknowns->str, server, item); + g_string_free(unknowns, TRUE); + + cmd_params_free(free_arg); +} + +/* SYNTAX: CYCLE [<channel>] [<message>] */ +static void cmd_cycle(const char *data, SERVER_REC *server, WI_ITEM_REC *item) +{ + CHANNEL_REC *chanrec; + char *channame, *msg, *joindata; + void *free_arg; + + g_return_if_fail(data != NULL); + if (!IS_SERVER(server) || !server->connected) + cmd_return_error(CMDERR_NOT_CONNECTED); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN, + item, &channame, &msg)) + return; + if (*channame == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + chanrec = channel_find(server, channame); + if (chanrec == NULL) cmd_param_error(CMDERR_CHAN_NOT_FOUND); + + joindata = chanrec->get_join_data(chanrec); + window_bind_add(window_item_window(chanrec), + chanrec->server->tag, chanrec->name); + + /* FIXME: kludgy kludgy... */ + signal_emit("command part", 3, data, server, item); + + if (g_slist_find(channels, chanrec) != NULL) { + chanrec->left = TRUE; + channel_destroy(chanrec); + } + + server->channels_join(server, joindata, FALSE); + g_free(joindata); + + cmd_params_free(free_arg); +} + +void fe_channels_init(void) +{ + settings_add_bool("lookandfeel", "autoclose_windows", TRUE); + settings_add_bool("lookandfeel", "show_names_on_join", TRUE); + settings_add_int("lookandfeel", "show_names_on_join_limit", 18); + settings_add_int("lookandfeel", "names_max_columns", 6); + settings_add_int("lookandfeel", "names_max_width", 0); + + signal_add("channel created", (SIGNAL_FUNC) signal_channel_created); + signal_add("channel destroyed", (SIGNAL_FUNC) signal_channel_destroyed); + signal_add_last("window item changed", (SIGNAL_FUNC) signal_window_item_changed); + signal_add_last("server disconnected", (SIGNAL_FUNC) sig_disconnected); + signal_add_last("channel joined", (SIGNAL_FUNC) sig_channel_joined); + + command_bind("join", NULL, (SIGNAL_FUNC) cmd_join); + command_bind("channel", NULL, (SIGNAL_FUNC) cmd_channel); + command_bind("channel add", NULL, (SIGNAL_FUNC) cmd_channel_add); + command_bind("channel modify", NULL, (SIGNAL_FUNC) cmd_channel_modify); + command_bind("channel remove", NULL, (SIGNAL_FUNC) cmd_channel_remove); + command_bind("channel list", NULL, (SIGNAL_FUNC) cmd_channel_list); + command_bind("names", NULL, (SIGNAL_FUNC) cmd_names); + command_bind("cycle", NULL, (SIGNAL_FUNC) cmd_cycle); + + command_set_options("channel add", "auto noauto -bots -botcmd"); + command_set_options("channel modify", "auto noauto -bots -botcmd"); + command_set_options("names", "count ops halfops voices normal"); + command_set_options("join", "invite window"); +} + +void fe_channels_deinit(void) +{ + signal_remove("channel created", (SIGNAL_FUNC) signal_channel_created); + signal_remove("channel destroyed", (SIGNAL_FUNC) signal_channel_destroyed); + signal_remove("window item changed", (SIGNAL_FUNC) signal_window_item_changed); + signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected); + signal_remove("channel joined", (SIGNAL_FUNC) sig_channel_joined); + + command_unbind("join", (SIGNAL_FUNC) cmd_join); + command_unbind("channel", (SIGNAL_FUNC) cmd_channel); + command_unbind("channel add", (SIGNAL_FUNC) cmd_channel_add); + command_unbind("channel modify", (SIGNAL_FUNC) cmd_channel_modify); + command_unbind("channel remove", (SIGNAL_FUNC) cmd_channel_remove); + command_unbind("channel list", (SIGNAL_FUNC) cmd_channel_list); + command_unbind("names", (SIGNAL_FUNC) cmd_names); + command_unbind("cycle", (SIGNAL_FUNC) cmd_cycle); +} diff --git a/src/fe-common/core/fe-channels.h b/src/fe-common/core/fe-channels.h new file mode 100644 index 0000000..34b24ac --- /dev/null +++ b/src/fe-common/core/fe-channels.h @@ -0,0 +1,16 @@ +#ifndef IRSSI_FE_COMMON_CORE_FE_CHANNELS_H +#define IRSSI_FE_COMMON_CORE_FE_CHANNELS_H + +#define CHANNEL_NICKLIST_FLAG_OPS 0x01 +#define CHANNEL_NICKLIST_FLAG_HALFOPS 0x02 +#define CHANNEL_NICKLIST_FLAG_VOICES 0x04 +#define CHANNEL_NICKLIST_FLAG_NORMAL 0x08 +#define CHANNEL_NICKLIST_FLAG_ALL 0x0f +#define CHANNEL_NICKLIST_FLAG_COUNT 0x10 + +void fe_channels_nicklist(CHANNEL_REC *channel, int flags); + +void fe_channels_init(void); +void fe_channels_deinit(void); + +#endif diff --git a/src/fe-common/core/fe-common-core.c b/src/fe-common/core/fe-common-core.c new file mode 100644 index 0000000..9724354 --- /dev/null +++ b/src/fe-common/core/fe-common-core.c @@ -0,0 +1,584 @@ +/* + fe-common-core.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/core/args.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/core/servers.h> +#include <irssi/src/core/channels.h> +#include <irssi/src/core/servers-setup.h> + +#include <irssi/src/core/special-vars.h> +#include <irssi/src/fe-common/core/fe-core-commands.h> +#include <irssi/src/fe-common/core/fe-queries.h> +#ifdef HAVE_CAPSICUM +#include <irssi/src/fe-common/core/fe-capsicum.h> +#endif +#include <irssi/src/fe-common/core/hilight-text.h> +#include <irssi/src/fe-common/core/command-history.h> +#include <irssi/src/fe-common/core/completion.h> +#include <irssi/src/fe-common/core/keyboard.h> +#include <irssi/src/fe-common/core/printtext.h> +#include <irssi/src/fe-common/core/formats.h> +#include <irssi/src/fe-common/core/themes.h> +#include <irssi/src/fe-common/core/fe-channels.h> +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/core/window-activity.h> +#include <irssi/src/fe-common/core/window-items.h> +#include <irssi/src/fe-common/core/windows-layout.h> +#include <irssi/src/fe-common/core/fe-recode.h> + +#include <signal.h> + +static char *autocon_server; +static char *autocon_password; +static int autocon_port; +static int no_autoconnect; +static char *cmdline_nick; +static char *cmdline_hostname; +GLogFunc logger_old; + +void fe_core_log_init(void); +void fe_core_log_deinit(void); + +void fe_exec_init(void); +void fe_exec_deinit(void); + +void fe_expandos_init(void); +void fe_expandos_deinit(void); + +void fe_help_init(void); +void fe_help_deinit(void); + +void fe_ignore_init(void); +void fe_ignore_deinit(void); + +void fe_ignore_messages_init(void); +void fe_ignore_messages_deinit(void); + +void fe_log_init(void); +void fe_log_deinit(void); + +void fe_messages_init(void); +void fe_messages_deinit(void); + +void fe_modules_init(void); +void fe_modules_deinit(void); + +void fe_server_init(void); +void fe_server_deinit(void); + +void fe_settings_init(void); +void fe_settings_deinit(void); + +void fe_tls_init(void); +void fe_tls_deinit(void); + +void window_commands_init(void); +void window_commands_deinit(void); + +static void sig_setup_changed(void); + +static void sig_connected(SERVER_REC *server) +{ + MODULE_DATA_SET(server, g_new0(MODULE_SERVER_REC, 1)); +} + +static void sig_destroyed(SERVER_REC *server) +{ + void *data = MODULE_DATA(server); + g_free(data); + MODULE_DATA_UNSET(server); +} + +static void sig_channel_created(CHANNEL_REC *channel) +{ + MODULE_DATA_SET(channel, g_new0(MODULE_CHANNEL_REC, 1)); +} + +static void sig_channel_destroyed(CHANNEL_REC *channel) +{ + void *data = MODULE_DATA(channel); + + g_free(data); + MODULE_DATA_UNSET(channel); +} + +void fe_common_core_register_options(void) +{ + static GOptionEntry options[] = { + { "connect", 'c', 0, G_OPTION_ARG_STRING, &autocon_server, "Automatically connect to server/network", "SERVER" }, + { "password", 'w', 0, G_OPTION_ARG_STRING, &autocon_password, "Autoconnect password", "PASSWORD" }, + { "port", 'p', 0, G_OPTION_ARG_INT, &autocon_port, "Autoconnect port", "PORT" }, + { "noconnect", '!', 0, G_OPTION_ARG_NONE, &no_autoconnect, "Disable autoconnecting", NULL }, + { "nick", 'n', 0, G_OPTION_ARG_STRING, &cmdline_nick, "Specify nick to use", NULL }, + { "hostname", 'h', 0, G_OPTION_ARG_STRING, &cmdline_hostname, "Specify host name to use", NULL }, + { NULL } + }; + + autocon_server = NULL; + autocon_password = NULL; + autocon_port = 0; + no_autoconnect = FALSE; + cmdline_nick = NULL; + cmdline_hostname = NULL; + args_register(options); +} + +void fe_common_core_init(void) +{ + const char *str; + + settings_add_bool("lookandfeel", "timestamps", TRUE); + settings_add_level("lookandfeel", "timestamp_level", "ALL"); + settings_add_time("lookandfeel", "timestamp_timeout", "0"); + + settings_add_level("lookandfeel", "beep_msg_level", ""); + settings_add_bool("lookandfeel", "beep_when_window_active", TRUE); + settings_add_bool("lookandfeel", "beep_when_away", TRUE); + + settings_add_bool("lookandfeel", "hide_text_style", FALSE); + settings_add_bool("lookandfeel", "hide_colors", FALSE); + settings_add_bool("lookandfeel", "hide_server_tags", FALSE); + + settings_add_bool("lookandfeel", "use_status_window", TRUE); + settings_add_bool("lookandfeel", "use_msgs_window", FALSE); + g_get_charset(&str); + settings_add_str("lookandfeel", "term_charset", str); + settings_add_str("lookandfeel", "glib_log_domains", "all"); + themes_init(); + theme_register(fecommon_core_formats); + + command_history_init(); + completion_init(); + keyboard_init(); + printtext_init(); + formats_init(); + fe_exec_init(); + fe_expandos_init(); + fe_help_init(); + fe_ignore_init(); + fe_log_init(); + fe_modules_init(); + fe_server_init(); + fe_settings_init(); + fe_tls_init(); +#ifdef HAVE_CAPSICUM + fe_capsicum_init(); +#endif + windows_init(); + window_activity_init(); + window_commands_init(); + window_items_init(); + windows_layout_init(); + fe_core_commands_init(); + + fe_channels_init(); + fe_queries_init(); + + fe_messages_init(); + hilight_text_init(); + fe_ignore_messages_init(); + fe_recode_init(); + + settings_check(); + + signal_add_first("server connected", (SIGNAL_FUNC) sig_connected); + signal_add_last("server destroyed", (SIGNAL_FUNC) sig_destroyed); + signal_add_first("channel created", (SIGNAL_FUNC) sig_channel_created); + signal_add_last("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed); + + module_register("core", "fe"); +} + +void fe_common_core_deinit(void) +{ + hilight_text_deinit(); + command_history_deinit(); + completion_deinit(); + keyboard_deinit(); + printtext_deinit(); + formats_deinit(); + fe_exec_deinit(); + fe_expandos_deinit(); + fe_help_deinit(); + fe_ignore_deinit(); + fe_log_deinit(); + fe_modules_deinit(); + fe_server_deinit(); + fe_settings_deinit(); + fe_tls_deinit(); +#ifdef HAVE_CAPSICUM + fe_capsicum_deinit(); +#endif + windows_deinit(); + window_activity_deinit(); + window_commands_deinit(); + window_items_deinit(); + windows_layout_deinit(); + fe_core_commands_deinit(); + + fe_channels_deinit(); + fe_queries_deinit(); + + fe_messages_deinit(); + fe_ignore_messages_deinit(); + fe_recode_deinit(); + + theme_unregister(); + themes_deinit(); + + signal_remove("setup changed", (SIGNAL_FUNC) sig_setup_changed); + signal_remove("server connected", (SIGNAL_FUNC) sig_connected); + signal_remove("server destroyed", (SIGNAL_FUNC) sig_destroyed); + signal_remove("channel created", (SIGNAL_FUNC) sig_channel_created); + signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed); + + g_log_set_default_handler(logger_old, NULL); +} + +static gboolean glib_domain_wanted(const char *domain) +{ + const char *domains; + char *c, *cur; + int len = 0; + int print_it = 0; /* -1 for exclude, 0 for undecided, 1 for include */ + int incl; + + /* Go through each item in glib_log_domains setting to determine whether + * or not we want to print message from this domain */ + domains = settings_get_str("glib_log_domains"); + c = cur = (char *) domains; + + do { + /* Advance through the string until we hit a space or the end */ + while (*cur != '\0' && *cur != ' ') { + cur++; + len++; + } + + /* Handle '-' prefix */ + incl = 1; + if (*c == '-') { + incl = -1; + c++; + len--; + } + + /* If we got a valid item, process it */ + if (len > 0 && (!strncmp(domain, c, len) || !strncasecmp("all", c, len) || + !strncmp("*", c, len))) + print_it = incl; + + /* Go past any spaces towards the next item */ + while (*cur == ' ') + cur++; + + /* Move on beyond the item we just handled */ + c = cur; + len = 0; + } while (*c != '\0' && print_it != -1); + + return (print_it == 1); +} + +static void i_log_func(const char *log_domain, GLogLevelFlags log_level, const char *message) +{ + const char *reason, *domain; + + switch (log_level) { + case G_LOG_LEVEL_WARNING: + reason = "warning"; + break; + case G_LOG_LEVEL_CRITICAL: + reason = "critical"; + break; + case G_LOG_LEVEL_DEBUG: + reason = "debug"; + break; + case G_LOG_LEVEL_MESSAGE: + reason = "message"; + break; + case G_LOG_LEVEL_INFO: + reason = "info"; + break; + default: + reason = "error"; + break; + } + + /* If log_domain parameter is NULL, GLib means to tell us that this is + * meant to be some nebulous "default" log domain name. */ + domain = (log_domain ? log_domain : "default"); + + /* Only print the message if we decided to */ + if (!glib_domain_wanted(domain)) + return; + + if (windows == NULL) + fprintf(stderr, "GLib (%s) %s: %s\n", domain, reason, message); + else { + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_GLIB_ERROR, domain, reason, + message); + } +} + +#define MSGS_WINDOW_LEVELS (MSGLEVEL_MSGS|MSGLEVEL_ACTIONS|MSGLEVEL_DCCMSGS) + +static void create_windows(void) +{ + WINDOW_REC *window; + int have_status = settings_get_bool("use_status_window"); + + window = window_find_name("(status)"); + if (have_status) { + if (window == NULL) { + window = window_create(NULL, TRUE); + window_set_refnum(window, 1); + window_set_name(window, "(status)"); + window_set_level(window, MSGLEVEL_ALL ^ + (settings_get_bool("use_msgs_window") ? + MSGS_WINDOW_LEVELS : 0)); + window_set_immortal(window, TRUE); + } + } else { + if (window != NULL) { + window_set_name(window, NULL); + window_set_level(window, 0); + window_set_immortal(window, FALSE); + } + } + + window = window_find_name("(msgs)"); + if (settings_get_bool("use_msgs_window")) { + if (window == NULL) { + window = window_create(NULL, TRUE); + window_set_refnum(window, have_status ? 2 : 1); + window_set_name(window, "(msgs)"); + window_set_level(window, MSGS_WINDOW_LEVELS); + window_set_immortal(window, TRUE); + } + } else { + if (window != NULL) { + window_set_name(window, NULL); + window_set_level(window, 0); + window_set_immortal(window, FALSE); + } + } + + if (windows == NULL) { + /* we have to have at least one window.. */ + window = window_create(NULL, TRUE); + } +} + +static void autoconnect_servers(void) +{ + GSList *tmp, *chatnets; + char *str; + + if (autocon_server != NULL) { + /* connect to specified server */ + if (autocon_password == NULL) + str = g_strdup_printf("%s %d", autocon_server, autocon_port); + else + str = g_strdup_printf("%s %d %s", autocon_server, autocon_port, autocon_password); + + signal_emit("command connect", 1, str); + g_free(str); + return; + } + + if (no_autoconnect) { + /* don't autoconnect */ + return; + } + + /* connect to autoconnect servers */ + chatnets = NULL; + for (tmp = setupservers; tmp != NULL; tmp = tmp->next) { + SERVER_SETUP_REC *rec = tmp->data; + + if (rec->autoconnect && + (rec->chatnet == NULL || + i_slist_find_icase_string(chatnets, rec->chatnet) == NULL)) { + if (rec->chatnet != NULL) { + chatnets = g_slist_append(chatnets, rec->chatnet); + str = g_strdup_printf("-network %s %s %d", rec->chatnet, rec->address, rec->port); + } else { + str = g_strdup_printf("%s %d", rec->address, rec->port); + } + + signal_emit("command connect", 1, str); + g_free(str); + } + } + + g_slist_free(chatnets); +} + +static void sig_setup_changed(void) +{ + static int firsttime = TRUE; + static int status_window = FALSE, msgs_window = FALSE; + int changed = FALSE; + + if (settings_get_bool("use_status_window") != status_window) { + status_window = !status_window; + changed = TRUE; + } + if (settings_get_bool("use_msgs_window") != msgs_window) { + msgs_window = !msgs_window; + changed = TRUE; + } + + if (firsttime) { + firsttime = FALSE; + changed = TRUE; + + windows_layout_restore(); + if (windows != NULL) + return; + } + + if (changed) + create_windows(); +} + +static void autorun_startup(void) +{ + char *path; + GIOChannel *handle; + GString *buf; + gsize tpos; + + /* open ~/.irssi/startup and run all commands in it */ + path = g_strdup_printf("%s/startup", get_irssi_dir()); + handle = g_io_channel_new_file(path, "r", NULL); + g_free(path); + if (handle == NULL) { + /* file not found */ + return; + } + + g_io_channel_set_encoding(handle, NULL, NULL); + buf = g_string_sized_new(512); + while (g_io_channel_read_line_string(handle, buf, &tpos, NULL) == G_IO_STATUS_NORMAL) { + buf->str[tpos] = '\0'; + if (buf->str[0] != '#') { + eval_special_string(buf->str, "", + active_win->active_server, + active_win->active); + } + } + g_string_free(buf, TRUE); + + g_io_channel_unref(handle); +} + +void fe_common_core_finish_init(void) +{ + int setup_changed; + + signal_emit("irssi init read settings", 0); + +#ifdef SIGPIPE + signal(SIGPIPE, SIG_IGN); +#endif + + setup_changed = FALSE; + if (cmdline_nick != NULL && *cmdline_nick != '\0') { + /* override nick found from setup */ + settings_set_str("nick", cmdline_nick); + setup_changed = TRUE; + } + + if (cmdline_hostname != NULL) { + /* override host name found from setup */ + settings_set_str("hostname", cmdline_hostname); + setup_changed = TRUE; + } + + sig_setup_changed(); + signal_add_first("setup changed", (SIGNAL_FUNC) sig_setup_changed); + + /* _after_ windows are created.. */ + logger_old = g_log_set_default_handler((GLogFunc) i_log_func, NULL); + + if (setup_changed) + signal_emit("setup changed", 0); + + autorun_startup(); + signal_emit("module autoload", 0); + autoconnect_servers(); +} + +gboolean strarray_find_dest(char **array, const TEXT_DEST_REC *dest) +{ + WI_ITEM_REC *item; + int server_tag_len, channel_type, query_type; + char **tmp; + + channel_type = module_get_uniq_id_str("WINDOW ITEM TYPE", "CHANNEL"); + query_type = module_get_uniq_id_str("WINDOW ITEM TYPE", "QUERY"); + + g_return_val_if_fail(array != NULL, FALSE); + g_return_val_if_fail(dest != NULL, FALSE); + g_return_val_if_fail(dest->window != NULL, FALSE); + + if (dest->target == NULL) + return dest->window->name != NULL && + strarray_find(array, dest->window->name) != -1 ? TRUE : FALSE; + + item = window_item_find_window(dest->window, dest->server, dest->target); + + server_tag_len = dest->server_tag != NULL ? strlen(dest->server_tag) : 0; + for (tmp = array; *tmp != NULL; tmp++) { + char *str = *tmp; + if (*str == '\0') { + continue; + } + + if (server_tag_len && + g_ascii_strncasecmp(str, dest->server_tag, server_tag_len) == 0 && + str[server_tag_len] == '/') { + str += server_tag_len + 1; + } + + if (g_strcmp0(str, "*") == 0 || g_strcmp0(str, "::all") == 0) { + return TRUE; + } else if (g_ascii_strcasecmp(str, dest->target) == 0) { + return TRUE; + } else if (item != NULL && item->type == query_type && + g_strcmp0(str, dest->target[0] == '=' ? "::dccqueries" : + "::queries") == 0) { + return TRUE; + } else if (item != NULL && item->type == channel_type && + g_strcmp0(str, "::channels") == 0) { + return TRUE; + } + } + + return FALSE; +} diff --git a/src/fe-common/core/fe-common-core.h b/src/fe-common/core/fe-common-core.h new file mode 100644 index 0000000..a9a74b0 --- /dev/null +++ b/src/fe-common/core/fe-common-core.h @@ -0,0 +1,13 @@ +#ifndef IRSSI_FE_COMMON_CORE_FE_COMMON_CORE_H +#define IRSSI_FE_COMMON_CORE_FE_COMMON_CORE_H + +void fe_common_core_register_options(void); +void fe_common_core_init(void); +void fe_common_core_deinit(void); +void fe_common_core_finish_init(void); + +/* Returns TRUE if "dest->target" or "dest->server_tag/dest->target" is found in + * array, otherwise FALSE. */ +gboolean strarray_find_dest(char **array, const TEXT_DEST_REC *dest); + +#endif diff --git a/src/fe-common/core/fe-core-commands.c b/src/fe-common/core/fe-core-commands.c new file mode 100644 index 0000000..207bcff --- /dev/null +++ b/src/fe-common/core/fe-core-commands.c @@ -0,0 +1,376 @@ +/* + fe-core-commands.c : irssi + + Copyright (C) 1999-2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include <irssi/src/core/core.h> +#include "module.h" +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/settings.h> +#include <irssi/irssi-version.h> +#include <irssi/src/core/servers.h> +#ifdef HAVE_CAPSICUM +#include <irssi/src/core/capsicum.h> +#endif + +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/core/printtext.h> + +#define PASTE_CHECK_SPEED 200 /* 0.2 sec */ + +static int ret_texts[] = { + TXT_OPTION_UNKNOWN, + TXT_OPTION_AMBIGUOUS, + TXT_OPTION_MISSING_ARG, + TXT_COMMAND_UNKNOWN, + TXT_COMMAND_AMBIGUOUS, + -1, + TXT_NOT_ENOUGH_PARAMS, + TXT_NOT_CONNECTED, + TXT_NOT_JOINED, + TXT_CHAN_NOT_FOUND, + TXT_CHAN_NOT_SYNCED, + TXT_ILLEGAL_PROTO, + TXT_NOT_GOOD_IDEA, + TXT_INVALID_TIME, + TXT_INVALID_CHARSET, + TXT_EVAL_MAX_RECURSE, + TXT_PROGRAM_NOT_FOUND, + TXT_NO_SERVER_DEFINED, +}; + +int command_hide_output; + +/* keep the whole command line here temporarily. we need it in + "default command" event handler, but there we don't know if the start of + the line had one or two command chars, and which one.. */ +static const char *current_cmdline; + +static gint64 time_command_last, time_command_now; +static int last_command_cmd, command_cmd; + +/* SYNTAX: ECHO [-window <name>] [-level <level>] <text> */ +static void cmd_echo(const char *data, void *server, WI_ITEM_REC *item) +{ + WINDOW_REC *window; + GHashTable *optlist; + char *msg, *levelstr, *winname; + void *free_arg; + int level; + + g_return_if_fail(data != NULL); + + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS | + PARAM_FLAG_GETREST, "echo", &optlist, &msg)) + return; + + levelstr = g_hash_table_lookup(optlist, "level"); + level = levelstr == NULL ? 0 : + level2bits(g_hash_table_lookup(optlist, "level"), NULL); + if (level == 0) level = MSGLEVEL_CRAP; + + winname = g_hash_table_lookup(optlist, "window"); + window = winname == NULL ? NULL : + is_numeric(winname, '\0') ? + window_find_refnum(atoi(winname)) : + window_find_item(NULL, winname); + if (window == NULL) window = active_win; + + printtext_window(window, level, "%s", msg); + cmd_params_free(free_arg); +} + +/* SYNTAX: VERSION */ +static void cmd_version(char *data) +{ + char time[10]; + + g_return_if_fail(data != NULL); + + if (*data == '\0') { + g_snprintf(time, sizeof(time), "%04d", IRSSI_VERSION_TIME); + printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + "Client: "PACKAGE_TARNAME" " PACKAGE_VERSION" (%d %s)", + IRSSI_VERSION_DATE, time); + } +} + +/* SYNTAX: CAT [-window] <file> [<seek position>] */ +static void cmd_cat(const char *data, SERVER_REC *server, WI_ITEM_REC *item) +{ + char *fname, *fposstr; + gboolean target; + GHashTable *optlist; + void *free_arg; + int fpos; + GIOChannel *handle; + GString *buf; + gsize tpos; +#ifdef HAVE_CAPSICUM + int fd; +#endif + + g_return_if_fail(data != NULL); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS, + "cat", &optlist, &fname, &fposstr)) + return; + + fname = convert_home(fname); + fpos = atoi(fposstr); + +#ifdef HAVE_CAPSICUM + fd = capsicum_open_wrapper(fname, O_RDONLY, 0); + if (fd > 0) + handle = g_io_channel_unix_new(fd); + else + handle = NULL; +#else + handle = g_io_channel_new_file(fname, "r", NULL); +#endif + g_free(fname); + + if (handle == NULL) { + /* file not found */ + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, + "%s", g_strerror(errno)); + return; + } + + target = g_hash_table_lookup(optlist, "window") != NULL; + + g_io_channel_set_encoding(handle, NULL, NULL); + g_io_channel_seek_position(handle, fpos, G_SEEK_SET, NULL); + buf = g_string_sized_new(512); + while (g_io_channel_read_line_string(handle, buf, &tpos, NULL) == G_IO_STATUS_NORMAL) { + buf->str[tpos] = '\0'; + if (target) + printtext_window(active_win, MSGLEVEL_CLIENTCRAP | MSGLEVEL_NEVER, "%s", + buf->str); + else + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP | MSGLEVEL_NEVER, "%s", buf->str); + } + g_string_free(buf, TRUE); + cmd_params_free(free_arg); + + g_io_channel_unref(handle); +} + +/* SYNTAX: BEEP */ +static void cmd_beep(void) +{ + signal_emit("beep", 0); +} + +static void cmd_nick(const char *data, SERVER_REC *server) +{ + g_return_if_fail(data != NULL); + + if (*data != '\0') return; + if (server == NULL || !server->connected) + cmd_return_error(CMDERR_NOT_CONNECTED); + + /* display current nick */ + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_YOUR_NICK, server->nick); + signal_stop(); +} + +/* SYNTAX: UPTIME */ +static void cmd_uptime(char *data) +{ + long uptime; + + g_return_if_fail(data != NULL); + + if (*data == '\0') { + uptime = (long)difftime(time(NULL), client_start_time); + printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + "Uptime: %ldd %ldh %ldm %lds", + uptime/3600/24, uptime/3600%24, + uptime/60%60, uptime%60); + } +} + +static void sig_stop(void) +{ + signal_stop(); +} + +static void event_command(const char *data) +{ + const char *cmdchar; + + /* save current command line */ + current_cmdline = data; + + /* for detecting if we're pasting text */ + time_command_last = time_command_now; + last_command_cmd = command_cmd; + + time_command_now = g_get_real_time(); + command_cmd = *data != '\0' && + strchr(settings_get_str("cmdchars"), *data) != NULL; + + /* /^command hides the output of the command */ + cmdchar = *data == '\0' ? NULL : + strchr(settings_get_str("cmdchars"), *data); + if (cmdchar != NULL && (data[1] == '^' || + (data[1] == *cmdchar && data[2] == '^')) + && !command_hide_output++) { + signal_add_first("print starting", (SIGNAL_FUNC) sig_stop); + signal_add_first("print noformat", (SIGNAL_FUNC) sig_stop); + signal_add_first("print format", (SIGNAL_FUNC) sig_stop); + signal_add_first("print text", (SIGNAL_FUNC) sig_stop); + } +} + +static void event_command_last(const char *data) +{ + if (command_hide_output && !--command_hide_output) { + signal_remove("print starting", (SIGNAL_FUNC) sig_stop); + signal_remove("print noformat", (SIGNAL_FUNC) sig_stop); + signal_remove("print format", (SIGNAL_FUNC) sig_stop); + signal_remove("print text", (SIGNAL_FUNC) sig_stop); + } +} + +static void event_default_command(const char *data, void *server, + WI_ITEM_REC *item) +{ + const char *cmdchars, *ptr; + char *cmd, *p; + long diff; + + cmdchars = settings_get_str("cmdchars"); + signal_stop(); + + ptr = data; + while (*ptr != '\0' && *ptr != ' ') { + if (strchr(cmdchars, *ptr)) { + /* command character inside command .. we probably + want to send this text to channel. for example + when pasting a path /usr/bin/xxx. */ + signal_emit("send text", 3, current_cmdline, server, item); + return; + } + ptr++; + } + + /* maybe we're copy+pasting text? check how long it was since the + last line */ + diff = time_command_now - time_command_last; + if (item != NULL && !last_command_cmd && diff < PASTE_CHECK_SPEED) { + signal_emit("send text", 3, current_cmdline, active_win->active_server, active_win->active); + command_cmd = FALSE; + return; + } + + /* get the command part of the line, send "error command" signal */ + cmd = g_strdup(data); + p = strchr(cmd, ' '); + if (p != NULL) *p = '\0'; + + signal_emit("error command", 2, GINT_TO_POINTER(CMDERR_UNKNOWN), cmd); + + g_free(cmd); +} + +static void event_cmderror(void *errorp, const char *arg) +{ + int error; + + error = GPOINTER_TO_INT(errorp); + if (error == CMDERR_ERRNO) { + /* errno is special */ + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", g_strerror(errno)); + } else { + /* others */ + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, ret_texts[error + -CMDERR_OPTION_UNKNOWN], arg); + } +} + +static void event_list_subcommands(const char *command) +{ + GSList *tmp; + GString *str; + int len; + + str = g_string_new(NULL); + + len = strlen(command); + for (tmp = commands; tmp != NULL; tmp = tmp->next) { + COMMAND_REC *rec = tmp->data; + + if (g_ascii_strncasecmp(rec->cmd, command, len) == 0 && + rec->cmd[len] == ' ' && + strchr(rec->cmd+len+1, ' ') == NULL) { + g_string_append_printf(str, "%s ", rec->cmd+len+1); + } + } + + if (str->len != 0) { + g_string_truncate(str, str->len-1); + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str->str); + } + + g_string_free(str, TRUE); +} + +void fe_core_commands_init(void) +{ + command_hide_output = 0; + + command_cmd = FALSE; + time_command_now = 0; + + command_bind("echo", NULL, (SIGNAL_FUNC) cmd_echo); + command_bind("version", NULL, (SIGNAL_FUNC) cmd_version); + command_bind("cat", NULL, (SIGNAL_FUNC) cmd_cat); + command_bind("beep", NULL, (SIGNAL_FUNC) cmd_beep); + command_bind("uptime", NULL, (SIGNAL_FUNC) cmd_uptime); + command_bind_first("nick", NULL, (SIGNAL_FUNC) cmd_nick); + + signal_add("send command", (SIGNAL_FUNC) event_command); + signal_add_last("send command", (SIGNAL_FUNC) event_command_last); + signal_add_last("default command", (SIGNAL_FUNC) event_default_command); + signal_add("error command", (SIGNAL_FUNC) event_cmderror); + signal_add("list subcommands", (SIGNAL_FUNC) event_list_subcommands); + + command_set_options("echo", "+level +window"); + command_set_options("cat", "window"); +} + +void fe_core_commands_deinit(void) +{ + command_unbind("echo", (SIGNAL_FUNC) cmd_echo); + command_unbind("version", (SIGNAL_FUNC) cmd_version); + command_unbind("cat", (SIGNAL_FUNC) cmd_cat); + command_unbind("beep", (SIGNAL_FUNC) cmd_beep); + command_unbind("uptime", (SIGNAL_FUNC) cmd_uptime); + command_unbind("nick", (SIGNAL_FUNC) cmd_nick); + + signal_remove("send command", (SIGNAL_FUNC) event_command); + signal_remove("send command", (SIGNAL_FUNC) event_command_last); + signal_remove("default command", (SIGNAL_FUNC) event_default_command); + signal_remove("error command", (SIGNAL_FUNC) event_cmderror); + signal_remove("list subcommands", (SIGNAL_FUNC) event_list_subcommands); +} diff --git a/src/fe-common/core/fe-core-commands.h b/src/fe-common/core/fe-core-commands.h new file mode 100644 index 0000000..d711b5d --- /dev/null +++ b/src/fe-common/core/fe-core-commands.h @@ -0,0 +1,9 @@ +#ifndef IRSSI_FE_COMMON_CORE_FE_CORE_COMMANDS_H +#define IRSSI_FE_COMMON_CORE_FE_CORE_COMMANDS_H + +extern int command_hide_output; + +void fe_core_commands_init(void); +void fe_core_commands_deinit(void); + +#endif diff --git a/src/fe-common/core/fe-exec.c b/src/fe-common/core/fe-exec.c new file mode 100644 index 0000000..0e929ba --- /dev/null +++ b/src/fe-common/core/fe-exec.c @@ -0,0 +1,682 @@ +/* + fe-exec.c : irssi + + Copyright (C) 2000-2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/modules.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/pidwait.h> +#include <irssi/src/core/line-split.h> +#include <irssi/src/core/network.h> +#include <irssi/src/core/net-sendbuffer.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/levels.h> + +#include <irssi/src/core/servers.h> +#include <irssi/src/core/channels.h> +#include <irssi/src/core/queries.h> + +#include <irssi/src/fe-common/core/printtext.h> +#include <irssi/src/fe-common/core/fe-exec.h> +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/core/window-items.h> + +#include <signal.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <unistd.h> + +GSList *processes; +static int signal_exec_input; + +static void exec_wi_destroy(EXEC_WI_REC *rec) +{ + g_return_if_fail(rec != NULL); + + if (rec->destroying) return; + rec->destroying = TRUE; + + rec->process->target_item = NULL; + if (window_item_window((WI_ITEM_REC *) rec) != NULL) + window_item_destroy((WI_ITEM_REC *) rec); + + MODULE_DATA_DEINIT(rec); + g_free(rec->visible_name); + g_free(rec); +} + +static const char *exec_get_target(WI_ITEM_REC *item) +{ + return ((EXEC_WI_REC *) item)->visible_name; +} + +static EXEC_WI_REC *exec_wi_create(WINDOW_REC *window, PROCESS_REC *rec) +{ + EXEC_WI_REC *item; + + g_return_val_if_fail(window != NULL, NULL); + g_return_val_if_fail(rec != NULL, NULL); + + item = g_new0(EXEC_WI_REC, 1); + item->type = module_get_uniq_id_str("WINDOW ITEM TYPE", "EXEC"); + item->destroy = (void (*) (WI_ITEM_REC *)) exec_wi_destroy; + item->get_target = exec_get_target; + item->visible_name = rec->name != NULL ? g_strdup(rec->name) : + g_strdup_printf("%%%d", rec->id); + + item->createtime = time(NULL); + item->process = rec; + + MODULE_DATA_INIT(item); + window_item_add(window, (WI_ITEM_REC *) item, FALSE); + return item; +} + +static int process_get_new_id(void) +{ + PROCESS_REC *rec; + GSList *tmp; + int id; + + id = 0; + tmp = processes; + while (tmp != NULL) { + rec = tmp->data; + + if (id != rec->id) { + tmp = tmp->next; + continue; + } + + id++; + tmp = processes; + } + + return id; +} + +static PROCESS_REC *process_find_pid(int pid) +{ + GSList *tmp; + + g_return_val_if_fail(pid > 0, NULL); + + for (tmp = processes; tmp != NULL; tmp = tmp->next) { + PROCESS_REC *rec = tmp->data; + + if (rec->pid == pid) + return rec; + } + + return NULL; +} + +static PROCESS_REC *process_find_id(int id, int verbose) +{ + GSList *tmp; + + g_return_val_if_fail(id != -1, NULL); + + for (tmp = processes; tmp != NULL; tmp = tmp->next) { + PROCESS_REC *rec = tmp->data; + + if (rec->id == id) + return rec; + } + + if (verbose) { + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, + "Unknown process id: %d", id); + } + + return NULL; +} + +static PROCESS_REC *process_find(const char *name, int verbose) +{ + GSList *tmp; + + g_return_val_if_fail(name != NULL, NULL); + + if (*name == '%' && is_numeric(name+1, 0)) + return process_find_id(atoi(name+1), verbose); + + for (tmp = processes; tmp != NULL; tmp = tmp->next) { + PROCESS_REC *rec = tmp->data; + + if (rec->name != NULL && g_strcmp0(rec->name, name) == 0) + return rec; + } + + if (verbose) { + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, + "Unknown process name: %s", name); + } + + return NULL; +} + +static void process_destroy(PROCESS_REC *rec, int status) +{ + processes = g_slist_remove(processes, rec); + + signal_emit("exec remove", 2, rec, GINT_TO_POINTER(status)); + + if (rec->read_tag != -1) + g_source_remove(rec->read_tag); + if (rec->target_item != NULL) + exec_wi_destroy(rec->target_item); + + line_split_free(rec->databuf); + g_io_channel_shutdown(rec->in, TRUE, NULL); + g_io_channel_unref(rec->in); + net_sendbuffer_destroy(rec->out, TRUE); + + g_free_not_null(rec->name); + g_free_not_null(rec->target); + g_free_not_null(rec->target_server); + g_free(rec->args); + g_free(rec); +} + +static void processes_killall(int signum) +{ + GSList *tmp; + int kill_ret; + + for (tmp = processes; tmp != NULL; tmp = tmp->next) { + PROCESS_REC *rec = tmp->data; + + kill_ret = kill(-rec->pid, signum); + if (kill_ret != 0) + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, + "Error sending signal %d to pid %d: %s", + signum, rec->pid, g_strerror(errno)); + } +} + +static int signal_name_to_id(const char *name) +{ + /* check only the few most common signals, too much job to check + them all. if we sometimes want more, procps-sources/proc/sig.c + would be useful for copypasting */ + if (g_ascii_strcasecmp(name, "hup") == 0) + return SIGHUP; + if (g_ascii_strcasecmp(name, "int") == 0) + return SIGINT; + if (g_ascii_strcasecmp(name, "term") == 0) + return SIGTERM; + if (g_ascii_strcasecmp(name, "kill") == 0) + return SIGKILL; + if (g_ascii_strcasecmp(name, "usr1") == 0) + return SIGUSR1; + if (g_ascii_strcasecmp(name, "usr2") == 0) + return SIGUSR2; + return -1; +} + +/* `optlist' should contain only one unknown key - the server tag. + returns NULL if there was unknown -option */ +static int cmd_options_get_signal(const char *cmd, + GHashTable *optlist) +{ + GList *list; + char *signame; + int signum; + + /* get all the options, then remove the known ones. there should + be only one left - the signal */ + list = optlist_remove_known(cmd, optlist); + + if (list == NULL) + return -1; + + signame = list->data; + signum = -1; + + signum = is_numeric(signame, 0) ? atol(signame) : + signal_name_to_id(signame); + + if (signum == -1 || list->next != NULL) { + /* unknown option (not a signal) */ + signal_emit("error command", 2, + GINT_TO_POINTER(CMDERR_OPTION_UNKNOWN), + signum == -1 ? list->data : list->next->data); + signal_stop(); + return -2; + } + + g_list_free(list); + return signum; +} + +static void exec_show_list(void) +{ + GSList *tmp; + + for (tmp = processes; tmp != NULL; tmp = tmp->next) { + PROCESS_REC *rec = tmp->data; + + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, + "%d (%s): %s", rec->id, rec->name, rec->args); + } +} + +static void process_exec(PROCESS_REC *rec, const char *cmd) +{ + const char *shell_args[4] = { FHS_PREFIX "/bin/sh", "-c", NULL, NULL }; + char **args; + int in[2], out[2]; + int n; + + if (pipe(in) == -1) + return; + if (pipe(out) == -1) + return; + + shell_args[2] = cmd; + rec->pid = fork(); + if (rec->pid == -1) { + /* error */ + close(in[0]); close(in[1]); + close(out[0]); close(out[1]); + return; + } + + if (rec->pid != 0) { + /* parent process */ + GIOChannel *outio = i_io_channel_new(in[1]); + + rec->in = i_io_channel_new(out[0]); + rec->out = net_sendbuffer_create(outio, 0); + + close(out[1]); + close(in[0]); + pidwait_add(rec->pid); + return; + } + + /* child process, try to clean up everything */ + setsid(); + +#ifndef __ANDROID__ + if (setuid(getuid()) != 0) + _exit(EXIT_FAILURE); + + if (setgid(getgid()) != 0) + _exit(EXIT_FAILURE); +#endif + + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_DFL); + + putenv("TERM=tty"); + + /* set stdin, stdout and stderr */ + dup2(in[0], STDIN_FILENO); + dup2(out[1], STDOUT_FILENO); + dup2(out[1], STDERR_FILENO); + + /* don't let child see our files */ + for (n = 3; n < 256; n++) + close(n); + + if (rec->shell) { + execvp(shell_args[0], (char **) shell_args); + + fprintf(stderr, "Exec: " FHS_PREFIX "/bin/sh: %s\n", g_strerror(errno)); + } else { + args = g_strsplit(cmd, " ", -1); + execvp(args[0], args); + + fprintf(stderr, "Exec: %s: %s\n", args[0], g_strerror(errno)); + } + + _exit(-1); +} + +static void sig_exec_input_reader(PROCESS_REC *rec) +{ + char tmpbuf[512], *str; + int recvlen; + int ret; + + g_return_if_fail(rec != NULL); + + recvlen = net_receive(rec->in, tmpbuf, sizeof(tmpbuf)); + do { + ret = line_split(tmpbuf, recvlen, &str, &rec->databuf); + if (ret == -1) { + /* link to terminal closed? */ + g_source_remove(rec->read_tag); + rec->read_tag = -1; + break; + } + + if (ret > 0) { + signal_emit_id(signal_exec_input, 2, rec, str); + if (recvlen > 0) recvlen = 0; + } + } while (ret > 0); +} + +static void handle_exec(const char *args, GHashTable *optlist, + SERVER_REC *server, WI_ITEM_REC *item) +{ + PROCESS_REC *rec; + SERVER_REC *target_server; + char *target, *level; + int notice, signum, interactive, target_nick, target_channel, kill_ret; + + /* check that there's no unknown options. we allowed them + because signals can be used as options, but there should be + only one unknown option: the signal name/number. */ + signum = cmd_options_get_signal("exec", optlist); + if (signum == -2) + return; + + if (*args == '\0') { + exec_show_list(); + return; + } + + target = NULL; + target_server = NULL; + notice = FALSE; + + if (g_hash_table_lookup(optlist, "in") != NULL) { + rec = process_find(g_hash_table_lookup(optlist, "in"), TRUE); + if (rec != NULL) { + net_sendbuffer_send(rec->out, args, strlen(args)); + net_sendbuffer_send(rec->out, "\n", 1); + } + return; + } + + /* check if args is a process ID or name. if it's ID but not found, + complain about it and fail immediately */ + rec = process_find(args, *args == '%'); + if (*args == '%' && rec == NULL) + return; + + /* common options */ + target_channel = target_nick = FALSE; + if (g_hash_table_lookup(optlist, "out") != NULL) { + /* redirect output to active channel/query */ + if (item == NULL) + cmd_return_error(CMDERR_NOT_JOINED); + target = (char *) window_item_get_target(item); + target_server = item->server; + target_channel = IS_CHANNEL(item); + target_nick = IS_QUERY(item); + } else if (g_hash_table_lookup(optlist, "msg") != NULL) { + /* redirect output to /msg <nick> */ + target = g_hash_table_lookup(optlist, "msg"); + target_server = server; + } else if (g_hash_table_lookup(optlist, "notice") != NULL) { + target = g_hash_table_lookup(optlist, "notice"); + target_server = server; + notice = TRUE; + } + + /* options that require process ID/name as argument */ + if (rec == NULL && + (signum != -1 || g_hash_table_lookup(optlist, "close") != NULL)) { + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, + "Unknown process name: %s", args); + return; + } + if (g_hash_table_lookup(optlist, "close") != NULL) { + /* forcibly close the process */ + process_destroy(rec, -1); + return; + } + + if (signum != -1) { + /* send a signal to process group */ + kill_ret = kill(-rec->pid, signum); + if (kill_ret != 0) + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, + "Error sending signal %d to pid %d: %s", + signum, rec->pid, g_strerror(errno)); + return; + } + + interactive = g_hash_table_lookup(optlist, "interactive") != NULL; + if (*args == '%') { + /* do something to already existing process */ + char *name; + + if (target != NULL) { + /* redirect output to target */ + g_free_and_null(rec->target); + rec->target = g_strdup(target); + rec->target_server = target_server == NULL ? NULL : + g_strdup(target_server->tag); + rec->notice = notice; + } + + name = g_hash_table_lookup(optlist, "name"); + if (name != NULL) { + /* change window name */ + g_free_not_null(rec->name); + rec->name = *name == '\0' ? NULL : g_strdup(name); + } else if (target == NULL && + (rec->target_item == NULL || interactive)) { + /* no parameters given, + redirect output to the active window */ + g_free_and_null(rec->target); + rec->target_win = active_win; + + if (rec->target_item != NULL) + exec_wi_destroy(rec->target_item); + + if (interactive) { + rec->target_item = + exec_wi_create(active_win, rec); + } + } + return; + } + + /* starting a new process */ + rec = g_new0(PROCESS_REC, 1); + rec->pid = -1; + rec->shell = g_hash_table_lookup(optlist, "nosh") == NULL; + + process_exec(rec, args); + if (rec->pid == -1) { + /* pipe() or fork() failed */ + g_free(rec); + cmd_return_error(CMDERR_ERRNO); + } + + rec->id = process_get_new_id(); + rec->target = g_strdup(target); + rec->target_server = target_server == NULL ? NULL : + g_strdup(target_server->tag); + rec->target_win = active_win; + rec->target_channel = target_channel; + rec->target_nick = target_nick; + rec->args = g_strdup(args); + rec->notice = notice; + rec->silent = g_hash_table_lookup(optlist, "-") != NULL; + rec->quiet = g_hash_table_lookup(optlist, "quiet") != NULL; + rec->name = g_strdup(g_hash_table_lookup(optlist, "name")); + + level = g_hash_table_lookup(optlist, "level"); + rec->level = level == NULL ? MSGLEVEL_CLIENTCRAP : level2bits(level, NULL); + + rec->read_tag = + i_input_add(rec->in, I_INPUT_READ, (GInputFunction) sig_exec_input_reader, rec); + processes = g_slist_append(processes, rec); + + if (rec->target == NULL && interactive) + rec->target_item = exec_wi_create(active_win, rec); + + signal_emit("exec new", 1, rec); +} + +/* SYNTAX: EXEC [-] [-nosh] [-out | -msg <target> | -notice <target>] + [-name <name>] <cmd line> + EXEC -out | -window | -msg <target> | -notice <target> | + -close | -<signal> %<id> + EXEC -in %<id> <text to send to process> */ +static void cmd_exec(const char *data, SERVER_REC *server, WI_ITEM_REC *item) +{ + GHashTable *optlist; + char *args; + void *free_arg; + + g_return_if_fail(data != NULL); + + if (cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS | + PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST, + "exec", &optlist, &args)) { + handle_exec(args, optlist, server, item); + cmd_params_free(free_arg); + } +} + +static void sig_pidwait(void *pid, void *statusp) +{ + PROCESS_REC *rec; + char *str; + int status = GPOINTER_TO_INT(statusp); + + rec = process_find_pid(GPOINTER_TO_INT(pid)); + if (rec == NULL) return; + + /* process exited - print the last line if + there wasn't a newline at end. */ + if (line_split("\n", 1, &str, &rec->databuf) > 0 && *str != '\0') + signal_emit_id(signal_exec_input, 2, rec, str); + + if (!rec->silent) { + if (WIFSIGNALED(status)) { + status = WTERMSIG(status); + printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + "process %d (%s) terminated with signal %d (%s)", + rec->id, rec->args, + status, g_strsignal(status)); + } else { + status = WIFEXITED(status) ? WEXITSTATUS(status) : -1; + printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + "process %d (%s) terminated with return code %d", + rec->id, rec->args, status); + } + } + process_destroy(rec, status); +} + +static void sig_exec_input(PROCESS_REC *rec, const char *text) +{ + WI_ITEM_REC *item; + SERVER_REC *server; + char *str; + + if (rec->quiet) + return; + + item = NULL; + server = NULL; + + if (rec->target != NULL) { + if (rec->target_server != NULL) { + server = server_find_tag(rec->target_server); + if (server == NULL) { + /* disconnected - target is lost */ + return; + } + item = NULL; + } else { + item = window_item_find(NULL, rec->target); + server = item != NULL ? item->server : + active_win->active_server; + } + + str = g_strconcat(rec->target_nick ? "-nick " : + rec->target_channel ? "-channel " : "", + rec->target, " ", *text == '\0' ? " " : text, NULL); + signal_emit(rec->notice ? "command notice" : "command msg", + 3, str, server, item); + g_free(str); + } else if (rec->target_item != NULL) { + printtext(NULL, rec->target_item->visible_name, + rec->level, "%s", text); + } else { + printtext_window(rec->target_win, rec->level, "%s", text); + } +} + +static void sig_window_destroyed(WINDOW_REC *window) +{ + GSList *tmp; + + /* window is being closed, if there's any /exec targets for it, + change them to active window. */ + for (tmp = processes; tmp != NULL; tmp = tmp->next) { + PROCESS_REC *rec = tmp->data; + + if (rec->target_win == window) + rec->target_win = active_win; + } +} + +static void event_text(const char *data, SERVER_REC *server, EXEC_WI_REC *item) +{ + if (!IS_EXEC_WI(item)) + return; + + net_sendbuffer_send(item->process->out, data, strlen(data)); + net_sendbuffer_send(item->process->out, "\n", 1); + signal_stop(); +} + +void fe_exec_init(void) +{ + command_bind("exec", NULL, (SIGNAL_FUNC) cmd_exec); + command_set_options("exec", "!- interactive nosh +name out +msg +notice +in window close +level quiet"); + + signal_exec_input = signal_get_uniq_id("exec input"); + signal_add("pidwait", (SIGNAL_FUNC) sig_pidwait); + signal_add("exec input", (SIGNAL_FUNC) sig_exec_input); + signal_add("window destroyed", (SIGNAL_FUNC) sig_window_destroyed); + signal_add_first("send text", (SIGNAL_FUNC) event_text); +} + +void fe_exec_deinit(void) +{ + if (processes != NULL) { + processes_killall(SIGTERM); + sleep(1); + processes_killall(SIGKILL); + + while (processes != NULL) + process_destroy(processes->data, -1); + } + + command_unbind("exec", (SIGNAL_FUNC) cmd_exec); + + signal_remove("pidwait", (SIGNAL_FUNC) sig_pidwait); + signal_remove("exec input", (SIGNAL_FUNC) sig_exec_input); + signal_remove("window destroyed", (SIGNAL_FUNC) sig_window_destroyed); + signal_remove("send text", (SIGNAL_FUNC) event_text); +} diff --git a/src/fe-common/core/fe-exec.h b/src/fe-common/core/fe-exec.h new file mode 100644 index 0000000..824d3d8 --- /dev/null +++ b/src/fe-common/core/fe-exec.h @@ -0,0 +1,52 @@ +#ifndef IRSSI_FE_COMMON_CORE_FE_EXEC_H +#define IRSSI_FE_COMMON_CORE_FE_EXEC_H + +#include <irssi/src/fe-common/core/fe-windows.h> + +#define EXEC_WI(query) \ + MODULE_CHECK_CAST_MODULE(query, EXEC_WI_REC, type, \ + "WINDOW ITEM TYPE", "EXEC") + +#define IS_EXEC_WI(query) \ + (EXEC_WI(query) ? TRUE : FALSE) + +typedef struct PROCESS_REC PROCESS_REC; + +#define STRUCT_SERVER_REC void +typedef struct { +#include <irssi/src/core/window-item-rec.h> + PROCESS_REC *process; + unsigned int destroying:1; +} EXEC_WI_REC; + +struct PROCESS_REC { + int id; + char *name; + char *args; + + int pid; + GIOChannel *in; + NET_SENDBUF_REC *out; + LINEBUF_REC *databuf; + int read_tag; + + int level; /* what level to use when printing the text */ + char *target; /* send text with /msg <target> ... */ + char *target_server; + WINDOW_REC *target_win; /* print text to this window */ + EXEC_WI_REC *target_item; /* print text to this exec window item */ + + unsigned int shell:1; /* start the program via /bin/sh */ + unsigned int notice:1; /* send text with /notice, not /msg if target is set */ + unsigned int silent:1; /* don't print "process exited with level xx" */ + unsigned int quiet:1; /* don't print process output at all */ + unsigned int target_channel:1; /* target is a channel */ + unsigned int target_nick:1; /* target is a nick */ +}; + +extern GSList *processes; + +void fe_exec_init(void); +void fe_exec_deinit(void); + +#endif diff --git a/src/fe-common/core/fe-expandos.c b/src/fe-common/core/fe-expandos.c new file mode 100644 index 0000000..9a48b4b --- /dev/null +++ b/src/fe-common/core/fe-expandos.c @@ -0,0 +1,58 @@ +/* + fe-expandos.c : irssi + + Copyright (C) 2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/expandos.h> +#include <irssi/src/fe-common/core/fe-windows.h> + +/* Window ref# */ +static char *expando_winref(SERVER_REC *server, void *item, int *free_ret) +{ + if (active_win == NULL) + return ""; + + *free_ret = TRUE; + return g_strdup_printf("%d", active_win->refnum); +} + +/* Window name */ +static char *expando_winname(SERVER_REC *server, void *item, int *free_ret) +{ + if (active_win == NULL) + return ""; + + return active_win->name; +} + +void fe_expandos_init(void) +{ + expando_create("winref", expando_winref, + "window changed", EXPANDO_ARG_NONE, + "window refnum changed", EXPANDO_ARG_WINDOW, NULL); + expando_create("winname", expando_winname, + "window changed", EXPANDO_ARG_NONE, + "window name changed", EXPANDO_ARG_WINDOW, NULL); +} + +void fe_expandos_deinit(void) +{ + expando_destroy("winref", expando_winref); + expando_destroy("winname", expando_winname); +} diff --git a/src/fe-common/core/fe-help.c b/src/fe-common/core/fe-help.c new file mode 100644 index 0000000..9c42806 --- /dev/null +++ b/src/fe-common/core/fe-help.c @@ -0,0 +1,273 @@ +/* + fe-help.c : irssi + + Copyright (C) 1999-2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/fe-common/core/printtext.h> +#include <irssi/src/fe-common/core/formats.h> + +static int commands_equal(COMMAND_REC *rec, COMMAND_REC *rec2) +{ + int i; + + if (rec->category == NULL && rec2->category != NULL) + return -1; + if (rec2->category == NULL && rec->category != NULL) + return 1; + if (rec->category != NULL && rec2->category != NULL) { + i = g_strcmp0(rec->category, rec2->category); + if (i != 0) + return i; + } + + return g_strcmp0(rec->cmd, rec2->cmd); +} + +static int get_cmd_length(void *data) +{ + return strlen(((COMMAND_REC *) data)->cmd); +} + +static void help_category(GSList *cmdlist, int items) +{ + WINDOW_REC *window; + TEXT_DEST_REC dest; + GString *str; + GSList *tmp; + int *columns, cols, rows, col, row, last_col_rows, max_width; + char *linebuf, *format, *stripped; + + window = window_find_closest(NULL, NULL, MSGLEVEL_CLIENTCRAP); + max_width = window->width; + + /* remove width of timestamp from max_width */ + format_create_dest(&dest, NULL, NULL, MSGLEVEL_CLIENTCRAP, NULL); + format = format_get_line_start(current_theme, &dest, time(NULL)); + if (format != NULL) { + stripped = strip_codes(format); + max_width -= strlen(stripped); + g_free(stripped); + g_free(format); + } + + /* calculate columns */ + cols = get_max_column_count(cmdlist, get_cmd_length, + max_width, 6, 1, 3, &columns, &rows); + cmdlist = columns_sort_list(cmdlist, rows); + + /* if the screen is too narrow the window width may be not + enough for even 1 column */ + if (cols == 1 && columns[0] > max_width) + max_width = columns[0]; + + /* rows in last column */ + last_col_rows = rows-(cols*rows-g_slist_length(cmdlist)); + if (last_col_rows == 0) + last_col_rows = rows; + + str = g_string_new(NULL); + linebuf = g_malloc(max_width+1); + + col = 0; row = 0; + for (tmp = cmdlist; tmp != NULL; tmp = tmp->next) { + COMMAND_REC *rec = tmp->data; + + memset(linebuf, ' ', columns[col]); + linebuf[columns[col]] = '\0'; + memcpy(linebuf, rec->cmd, strlen(rec->cmd)); + g_string_append(str, linebuf); + + if (++col == cols) { + printtext(NULL, NULL, + MSGLEVEL_CLIENTCRAP, "%s", str->str); + g_string_truncate(str, 0); + col = 0; row++; + + if (row == last_col_rows) + cols--; + } + } + if (str->len != 0) + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "%s", str->str); + + g_slist_free(cmdlist); + g_string_free(str, TRUE); + g_free(columns); + g_free(linebuf); +} + +static int show_help_file(const char *file) +{ + const char *helppath; + char *path, **paths, **tmp; + GIOChannel *handle; + GString *buf; + gsize tpos; + + helppath = settings_get_str("help_path"); + + paths = g_strsplit(helppath, ":", -1); + + handle = NULL; + for (tmp = paths; *tmp != NULL; tmp++) { + /* helpdir/command or helpdir/category/command */ + path = g_strdup_printf("%s/%s", *tmp, file); + handle = g_io_channel_new_file(path, "r", NULL); + g_free(path); + + if (handle != NULL) + break; + + } + + g_strfreev(paths); + + if (handle == NULL) + return FALSE; + + g_io_channel_set_encoding(handle, NULL, NULL); + buf = g_string_sized_new(512); + /* just print to screen whatever is in the file */ + while (g_io_channel_read_line_string(handle, buf, &tpos, NULL) == G_IO_STATUS_NORMAL) { + buf->str[tpos] = '\0'; + g_string_prepend(buf, "%|"); + printtext_string(NULL, NULL, MSGLEVEL_CLIENTCRAP, buf->str); + } + g_string_free(buf, TRUE); + + g_io_channel_unref(handle); + return TRUE; +} + +static void show_help(const char *data) +{ + COMMAND_REC *rec, *last; + GSList *tmp, *cmdlist; + int items, findlen; + int header, found, fullmatch; + + g_return_if_fail(data != NULL); + + /* sort the commands list */ + commands = g_slist_sort(commands, (GCompareFunc) commands_equal); + + /* print command, sort by category */ + cmdlist = NULL; last = NULL; header = FALSE; fullmatch = FALSE; + items = 0; findlen = strlen(data); found = FALSE; + for (tmp = commands; tmp != NULL; last = rec, tmp = tmp->next) { + rec = tmp->data; + + if (last != NULL && rec->category != NULL && + (last->category == NULL || + g_strcmp0(rec->category, last->category) != 0)) { + /* category changed */ + if (items > 0) { + if (!header) { + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "Irssi commands:"); + header = TRUE; + } + if (last->category != NULL) { + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, ""); + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "%s:", last->category); + } + help_category(cmdlist, items); + } + + g_slist_free(cmdlist); cmdlist = NULL; + items = 0; + } + + if (last != NULL && g_ascii_strcasecmp(rec->cmd, last->cmd) == 0) + continue; /* don't display same command twice */ + + if ((int)strlen(rec->cmd) >= findlen && + g_ascii_strncasecmp(rec->cmd, data, findlen) == 0) { + if (rec->cmd[findlen] == '\0') { + fullmatch = TRUE; + found = TRUE; + break; + } + else if (strchr(rec->cmd+findlen+1, ' ') == NULL) { + /* not a subcommand (and matches the query) */ + items++; + cmdlist = g_slist_append(cmdlist, rec); + found = TRUE; + } + } + } + + if ((!found || fullmatch) && !show_help_file(data)) { + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, + "No help for %s", data); + } + + if (*data != '\0' && data[strlen(data)-1] != ' ' && + command_have_sub(data)) { + char *cmd; + + cmd = g_strconcat(data, " ", NULL); + show_help(cmd); + g_free(cmd); + } + + if (items != 0) { + /* display the last category */ + if (!header) { + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, + "Irssi commands:"); + header = TRUE; + } + + if (last->category != NULL) { + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, ""); + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, + "%s:", last->category); + } + help_category(cmdlist, items); + g_slist_free(cmdlist); + } +} + +/* SYNTAX: HELP [<command>] */ +static void cmd_help(const char *data) +{ + char *cmd; + + cmd = g_ascii_strdown(data, -1); + g_strchomp(cmd); + show_help(cmd); + g_free(cmd); +} + +void fe_help_init(void) +{ + settings_add_str("misc", "help_path", HELPDIR); + command_bind("help", NULL, (SIGNAL_FUNC) cmd_help); +} + +void fe_help_deinit(void) +{ + command_unbind("help", (SIGNAL_FUNC) cmd_help); +} diff --git a/src/fe-common/core/fe-ignore-messages.c b/src/fe-common/core/fe-ignore-messages.c new file mode 100644 index 0000000..4f625de --- /dev/null +++ b/src/fe-common/core/fe-ignore-messages.c @@ -0,0 +1,155 @@ +/* + fe-ignore-messages.c : irssi + + Copyright (C) 2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/signals.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/ignore.h> +#include <irssi/src/core/servers.h> + +static void sig_message_public(SERVER_REC *server, const char *msg, + const char *nick, const char *address, + const char *target) +{ + if (ignore_check(server, nick, address, target, msg, MSGLEVEL_PUBLIC)) + signal_stop(); +} + +static void sig_message_private(SERVER_REC *server, const char *msg, + const char *nick, const char *address) +{ + if (ignore_check(server, nick, address, NULL, msg, MSGLEVEL_MSGS)) + signal_stop(); +} + +static void sig_message_join(SERVER_REC *server, const char *channel, + const char *nick, const char *address) +{ + if (ignore_check(server, nick, address, channel, NULL, MSGLEVEL_JOINS)) + signal_stop(); +} + +static void sig_message_host_changed(SERVER_REC *server, const char *nick, + const char *address, const char *old_address) +{ + if (ignore_check(server, nick, address, NULL, NULL, MSGLEVEL_JOINS)) + signal_stop(); +} + +static void sig_message_part(SERVER_REC *server, const char *channel, + const char *nick, const char *address, + const char *reason) +{ + if (ignore_check(server, nick, address, channel, NULL, MSGLEVEL_PARTS)) + signal_stop(); +} + +static void sig_message_quit(SERVER_REC *server, const char *nick, + const char *address, const char *reason) +{ + if (ignore_check(server, nick, address, NULL, reason, MSGLEVEL_QUITS)) + signal_stop(); +} + +static void sig_message_kick(SERVER_REC *server, const char *channel, + const char *nick, const char *kicker, + const char *address, const char *reason) +{ + /* never ignore if you were kicked */ + if (g_ascii_strcasecmp(nick, server->nick) != 0 && + ignore_check(server, kicker, address, + channel, reason, MSGLEVEL_KICKS)) + signal_stop(); +} + +static void sig_message_nick(SERVER_REC *server, const char *newnick, + const char *oldnick, const char *address) +{ + if (ignore_check(server, oldnick, address, + NULL, NULL, MSGLEVEL_NICKS) || + ignore_check(server, newnick, address, + NULL, NULL, MSGLEVEL_NICKS)) + signal_stop(); +} + +static void sig_message_own_nick(SERVER_REC *server, const char *newnick, + const char *oldnick, const char *address) +{ + if (ignore_check(server, oldnick, address, NULL, NULL, MSGLEVEL_NICKS)) + signal_stop(); +} + +static void sig_message_invite(SERVER_REC *server, const char *channel, + const char *nick, const char *address) +{ + if (*channel == '\0' || + ignore_check(server, nick, address, + channel, NULL, MSGLEVEL_INVITES)) + signal_stop(); +} + +static void sig_message_invite_other(SERVER_REC *server, const char *channel, + const char *invited, const char *nick, const char *address) +{ + if (ignore_check(server, nick, address, + channel, invited, MSGLEVEL_INVITES)) + signal_stop(); +} + +static void sig_message_topic(SERVER_REC *server, const char *channel, + const char *topic, + const char *nick, const char *address) +{ + if (ignore_check(server, nick, address, + channel, topic, MSGLEVEL_TOPICS)) + signal_stop(); +} + +void fe_ignore_messages_init(void) +{ + signal_add_first("message public", (SIGNAL_FUNC) sig_message_public); + signal_add_first("message private", (SIGNAL_FUNC) sig_message_private); + signal_add_first("message join", (SIGNAL_FUNC) sig_message_join); + signal_add_first("message host_changed", (SIGNAL_FUNC) sig_message_host_changed); + signal_add_first("message part", (SIGNAL_FUNC) sig_message_part); + signal_add_first("message quit", (SIGNAL_FUNC) sig_message_quit); + signal_add_first("message kick", (SIGNAL_FUNC) sig_message_kick); + signal_add_first("message nick", (SIGNAL_FUNC) sig_message_nick); + signal_add_first("message own_nick", (SIGNAL_FUNC) sig_message_own_nick); + signal_add_first("message invite", (SIGNAL_FUNC) sig_message_invite); + signal_add_first("message invite_other", (SIGNAL_FUNC) sig_message_invite_other); + signal_add_first("message topic", (SIGNAL_FUNC) sig_message_topic); +} + +void fe_ignore_messages_deinit(void) +{ + signal_remove("message public", (SIGNAL_FUNC) sig_message_public); + signal_remove("message private", (SIGNAL_FUNC) sig_message_private); + signal_remove("message join", (SIGNAL_FUNC) sig_message_join); + signal_remove("message host_changed", (SIGNAL_FUNC) sig_message_host_changed); + signal_remove("message part", (SIGNAL_FUNC) sig_message_part); + signal_remove("message quit", (SIGNAL_FUNC) sig_message_quit); + signal_remove("message kick", (SIGNAL_FUNC) sig_message_kick); + signal_remove("message nick", (SIGNAL_FUNC) sig_message_nick); + signal_remove("message own_nick", (SIGNAL_FUNC) sig_message_own_nick); + signal_remove("message invite", (SIGNAL_FUNC) sig_message_invite); + signal_remove("message invite_other", (SIGNAL_FUNC) sig_message_invite_other); + signal_remove("message topic", (SIGNAL_FUNC) sig_message_topic); +} diff --git a/src/fe-common/core/fe-ignore.c b/src/fe-common/core/fe-ignore.c new file mode 100644 index 0000000..3594b6f --- /dev/null +++ b/src/fe-common/core/fe-ignore.c @@ -0,0 +1,319 @@ +/* + fe-ignore.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <time.h> +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/misc.h> + +#include <irssi/src/core/servers.h> +#include <irssi/src/core/ignore.h> +#include <irssi/src/fe-common/core/printtext.h> + +static char *ignore_get_key(IGNORE_REC *rec) +{ + char *chans, *ret; + + if (rec->channels == NULL) + return g_strdup(rec->mask != NULL ? rec->mask : "*" ); + + chans = g_strjoinv(",", rec->channels); + if (rec->mask == NULL) return chans; + + ret = g_strdup_printf("%s %s", rec->mask, chans); + g_free(chans); + return ret; +} + +static void ignore_print(int index, IGNORE_REC *rec) +{ + GString *options; + char *key, *levels; + struct tm ts; + char buf[20]; + + key = ignore_get_key(rec); + levels = bits2level(rec->level); + + options = g_string_new(NULL); + if (rec->exception) g_string_append(options, "-except "); + if (rec->regexp) { + g_string_append(options, "-regexp "); + if (rec->pattern == NULL) + g_string_append(options, "[INVALID! -pattern missing] "); + else if (rec->preg == NULL) + g_string_append(options, "[INVALID!] "); + } + if (rec->fullword) g_string_append(options, "-full "); + if (rec->replies) g_string_append(options, "-replies "); + if (rec->servertag != NULL) + g_string_append_printf(options, "-network %s ", rec->servertag); + if (rec->pattern != NULL) + g_string_append_printf(options, "-pattern %s ", rec->pattern); + if (rec->unignore_time != 0) { + ts = *localtime(&rec->unignore_time); + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &ts); + g_string_append_printf(options, "ignore ends: %s ", buf); + } + + if (options->len > 1) g_string_truncate(options, options->len-1); + + if (index >= 0) { + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, + TXT_IGNORE_LINE, index, key != NULL ? key : "", + levels != NULL ? levels : "", options->str); + } else { + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, + options->len > 0 ? TXT_IGNORED_OPTIONS : TXT_IGNORED, + key != NULL ? key : "", + levels != NULL ? levels : "", options->str); + } + g_string_free(options, TRUE); + g_free(key); + g_free(levels); +} + +static void cmd_ignore_show(void) +{ + GSList *tmp; + int index; + + if (ignores == NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, + TXT_IGNORE_NO_IGNORES); + return; + } + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_IGNORE_HEADER); + index = 1; + for (tmp = ignores; tmp != NULL; tmp = tmp->next, index++) { + IGNORE_REC *rec = tmp->data; + + ignore_print(index, rec); + } + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_IGNORE_FOOTER); +} + +/* SYNTAX: IGNORE [-regexp | -full] [-pattern <pattern>] [-except] [-replies] + [-network <network>] [-channels <channel>] [-time <time>] <mask> [<levels>] + IGNORE [-regexp | -full] [-pattern <pattern>] [-except] [-replies] + [-network <network>] [-time <time>] <channels> [<levels>] */ +/* NOTE: -network replaces the old -ircnet flag. */ +static void cmd_ignore(const char *data) +{ + GHashTable *optlist; + IGNORE_REC *rec; + char *patternarg, *chanarg, *mask, *levels, *timestr, *servertag; + char **channels; + void *free_arg; + int new_ignore, msecs, level, flags; + + if (*data == '\0') { + cmd_ignore_show(); + return; + } + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS | + PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS, + "ignore", &optlist, &mask, &levels)) + return; + + patternarg = g_hash_table_lookup(optlist, "pattern"); + chanarg = g_hash_table_lookup(optlist, "channels"); + servertag = g_hash_table_lookup(optlist, "network"); + /* Allow -ircnet for backwards compatibility */ + if (!servertag) + servertag = g_hash_table_lookup(optlist, "ircnet"); + + if (*mask == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + if (*levels == '\0') levels = "ALL"; + level = level2bits(levels, NULL); + + msecs = 0; + timestr = g_hash_table_lookup(optlist, "time"); + if (timestr != NULL) { + if (!parse_time_interval(timestr, &msecs)) + cmd_param_error(CMDERR_INVALID_TIME); + } + + if (active_win->active_server != NULL && + server_ischannel(active_win->active_server, mask)) { + chanarg = mask; + mask = NULL; + } + channels = (chanarg == NULL || *chanarg == '\0') ? NULL : + g_strsplit(chanarg, ",", -1); + + flags = IGNORE_FIND_PATTERN; + if (level & MSGLEVEL_NO_ACT) + flags |= IGNORE_FIND_NOACT; + if (level & MSGLEVEL_HIDDEN) + flags |= IGNORE_FIND_HIDDEN; + if (level & MSGLEVEL_NOHILIGHT) + flags |= IGNORE_FIND_NOHILIGHT; + + rec = ignore_find_full(servertag, mask, patternarg, channels, flags); + new_ignore = rec == NULL; + + if (rec == NULL) { + rec = g_new0(IGNORE_REC, 1); + + rec->mask = mask == NULL || *mask == '\0' || + g_strcmp0(mask, "*") == 0 ? NULL : g_strdup(mask); + rec->channels = channels; + } else { + g_free_and_null(rec->pattern); + g_strfreev(channels); + } + + rec->level = combine_level(rec->level, levels); + + if (rec->level == MSGLEVEL_NO_ACT) { + /* If only NO_ACT was specified add all levels; it makes no + * sense on its own. */ + rec->level |= MSGLEVEL_ALL; + } + + if (rec->level == MSGLEVEL_HIDDEN) { + /* If only HIDDEN was specified add all levels; it makes no + * sense on its own. */ + rec->level |= MSGLEVEL_ALL; + } + + if (rec->level == MSGLEVEL_NOHILIGHT) { + /* If only NOHILIGHT was specified add all levels; it makes no + * sense on its own. */ + rec->level |= MSGLEVEL_ALL; + } + + if (new_ignore && rec->level == 0) { + /* tried to unignore levels from nonexisting ignore */ + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_IGNORE_NOT_FOUND, rec->mask); + g_free(rec->mask); + g_strfreev(rec->channels); + g_free(rec); + cmd_params_free(free_arg); + return; + } + rec->servertag = (servertag == NULL || *servertag == '\0') ? + NULL : g_strdup(servertag); + rec->pattern = (patternarg == NULL || *patternarg == '\0') ? + NULL : g_strdup(patternarg); + rec->exception = g_hash_table_lookup(optlist, "except") != NULL; + rec->regexp = g_hash_table_lookup(optlist, "regexp") != NULL; + rec->fullword = g_hash_table_lookup(optlist, "full") != NULL; + rec->replies = g_hash_table_lookup(optlist, "replies") != NULL; + if (msecs != 0) + rec->unignore_time = time(NULL)+msecs/1000; + + if (new_ignore) + ignore_add_rec(rec); + else + ignore_update_rec(rec); + + cmd_params_free(free_arg); +} + +/* SYNTAX: UNIGNORE <id>|<mask> */ +static void cmd_unignore(const char *data) +{ + IGNORE_REC *rec; + GSList *tmp; + char *mask, *mask_orig; + void *free_arg; + + if (!cmd_get_params(data, &free_arg, 1, &mask)) + return; + + if (*mask == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + /* Save the mask string here since it might be modified in the code + * below and we need it to print meaningful error messages. */ + mask_orig = mask; + + if (is_numeric(mask, ' ')) { + /* with index number */ + tmp = g_slist_nth(ignores, atoi(mask)-1); + rec = tmp == NULL ? NULL : tmp->data; + } else { + /* with mask */ + const char *chans[2] = { "*", NULL }; + + if (active_win->active_server != NULL && + server_ischannel(active_win->active_server, mask)) { + chans[0] = mask; + mask = NULL; + } + rec = ignore_find_full("*", mask, NULL, (char **) chans, 0); + if (rec == NULL) { + rec = ignore_find_full("*", mask, NULL, (char **) chans, IGNORE_FIND_NOACT); + } + } + + if (rec != NULL) { + rec->level = 0; + ignore_update_rec(rec); + } else { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_IGNORE_NOT_FOUND, mask_orig); + } + cmd_params_free(free_arg); +} + +static void sig_ignore_created(IGNORE_REC *rec) +{ + ignore_print(-1, rec); +} + +static void sig_ignore_destroyed(IGNORE_REC *rec) +{ + char *key; + + key = ignore_get_key(rec); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_UNIGNORED, key); + g_free(key); +} + +void fe_ignore_init(void) +{ + command_bind("ignore", NULL, (SIGNAL_FUNC) cmd_ignore); + command_bind("unignore", NULL, (SIGNAL_FUNC) cmd_unignore); + + signal_add("ignore destroyed", (SIGNAL_FUNC) sig_ignore_destroyed); + signal_add("ignore created", (SIGNAL_FUNC) sig_ignore_created); + signal_add("ignore changed", (SIGNAL_FUNC) sig_ignore_created); + + command_set_options("ignore", "regexp full except replies -network -ircnet -time -pattern -channels"); +} + +void fe_ignore_deinit(void) +{ + command_unbind("ignore", (SIGNAL_FUNC) cmd_ignore); + command_unbind("unignore", (SIGNAL_FUNC) cmd_unignore); + + signal_remove("ignore destroyed", (SIGNAL_FUNC) sig_ignore_destroyed); + signal_remove("ignore created", (SIGNAL_FUNC) sig_ignore_created); + signal_remove("ignore changed", (SIGNAL_FUNC) sig_ignore_created); +} diff --git a/src/fe-common/core/fe-log.c b/src/fe-common/core/fe-log.c new file mode 100644 index 0000000..164036c --- /dev/null +++ b/src/fe-common/core/fe-log.c @@ -0,0 +1,817 @@ +/* + fe-log.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/chat-protocols.h> +#include <irssi/src/core/servers.h> +#include <irssi/src/core/channels.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/log.h> +#include <irssi/src/core/special-vars.h> +#include <irssi/src/core/settings.h> +#include <irssi/src/lib-config/iconfig.h> +#ifdef HAVE_CAPSICUM +#include <irssi/src/core/capsicum.h> +#endif + +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/core/window-items.h> +#include <irssi/src/fe-common/core/formats.h> +#include <irssi/src/fe-common/core/themes.h> +#include <irssi/src/fe-common/core/printtext.h> +#include <irssi/src/fe-common/core/fe-common-core.h> + +#include <irssi/src/core/channels-setup.h> + +/* close autologs after 5 minutes of inactivity */ +#define AUTOLOG_INACTIVITY_CLOSE (60*5) + +static int autolog_level; +static int log_server_time; +static int autoremove_tag; +static char *autolog_path; + +static THEME_REC *log_theme; +static int skip_next_printtext; +static char *log_theme_name; + +static char **autolog_ignore_targets; + +static char *log_colorizer_strip(const char *str) +{ + return strip_codes(str); +} + +static void log_add_targets(LOG_REC *log, const char *targets, const char *tag) +{ + char **tmp, **items; + + g_return_if_fail(log != NULL); + g_return_if_fail(targets != NULL); + + items = g_strsplit(targets, " ", -1); + + for (tmp = items; *tmp != NULL; tmp++) + log_item_add(log, LOG_ITEM_TARGET, *tmp, tag); + + g_strfreev(items); +} + +/* SYNTAX: LOG OPEN [-noopen] [-autoopen] [-window] [-<server tag>] + [-targets <targets>] [-colors] + <fname> [<levels>] */ +static void cmd_log_open(const char *data) +{ + SERVER_REC *server; + GHashTable *optlist; + char *targetarg, *fname, *levels, *servertag; + void *free_arg; + char window[MAX_INT_STRLEN]; + LOG_REC *log; + int level; + + if (!cmd_get_params(data, &free_arg, + 2 | PARAM_FLAG_GETREST | PARAM_FLAG_UNKNOWN_OPTIONS | + PARAM_FLAG_OPTIONS | PARAM_FLAG_STRIP_TRAILING_WS, + "log open", &optlist, &fname, &levels)) + return; + if (*fname == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + level = level2bits(levels, NULL); + log = log_create_rec(fname, level != 0 ? level : MSGLEVEL_ALL); + + /* -<server tag> */ + server = cmd_options_get_server("log open", optlist, NULL); + servertag = server == NULL ? NULL : server->tag; + + if (g_hash_table_lookup(optlist, "window")) { + /* log by window ref# */ + targetarg = g_hash_table_lookup(optlist, "targets"); + if (targetarg == NULL || !is_numeric(targetarg, '\0')) { + ltoa(window, active_win->refnum); + targetarg = window; + } + log_item_add(log, LOG_ITEM_WINDOW_REFNUM, targetarg, servertag); + } else { + targetarg = g_hash_table_lookup(optlist, "targets"); + if (targetarg != NULL && *targetarg != '\0') + log_add_targets(log, targetarg, servertag); + else if (servertag != NULL) + log_add_targets(log, "*", servertag); + } + + if (g_hash_table_lookup(optlist, "autoopen")) + log->autoopen = TRUE; + + if (g_hash_table_lookup(optlist, "colors") == NULL) + log->colorizer = log_colorizer_strip; + + log_update(log); + + if (log->handle == -1 && g_hash_table_lookup(optlist, "noopen") == NULL) { + /* start logging */ + if (log_start_logging(log)) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_OPENED, fname); + } else { + log_close(log); + } + } + + cmd_params_free(free_arg); +} + +static LOG_REC *log_find_from_data(const char *data) +{ + GSList *tmp; + + if (!is_numeric(data, ' ')) + return log_find(data); + + /* with index number */ + tmp = g_slist_nth(logs, atoi(data)-1); + return tmp == NULL ? NULL : tmp->data; +} + +/* SYNTAX: LOG CLOSE <id>|<file> */ +static void cmd_log_close(const char *data) +{ + LOG_REC *log; + + log = log_find_from_data(data); + if (log == NULL) + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_LOG_NOT_OPEN, data); + else { + log_close(log); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_CLOSED, data); + } +} + +/* SYNTAX: LOG START <id>|<file> */ +static void cmd_log_start(const char *data) +{ + LOG_REC *log; + + log = log_find_from_data(data); + if (log != NULL) { + log_start_logging(log); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_OPENED, data); + } +} + +/* SYNTAX: LOG STOP <id>|<file> */ +static void cmd_log_stop(const char *data) +{ + LOG_REC *log; + + log = log_find_from_data(data); + if (log == NULL || log->handle == -1) + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_LOG_NOT_OPEN, data); + else { + log_stop_logging(log); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_CLOSED, data); + } +} + +static char *log_items_get_list(LOG_REC *log) +{ + GSList *tmp; + GString *str; + char *ret; + LOG_ITEM_REC *rec = NULL; + + g_return_val_if_fail(log != NULL, NULL); + g_return_val_if_fail(log->items != NULL, NULL); + + str = g_string_new(NULL); + for (tmp = log->items; tmp != NULL; tmp = tmp->next) { + rec = tmp->data; + + g_string_append_printf(str, "%s, ", rec->name); + } + g_string_truncate(str, str->len-2); + if(rec->servertag != NULL) + g_string_append_printf(str, " (%s)", rec->servertag); + + ret = str->str; + g_string_free(str, FALSE); + return ret; +} + +static void cmd_log_list(void) +{ + GSList *tmp; + char *levelstr, *items; + int index; + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_LOG_LIST_HEADER); + for (tmp = logs, index = 1; tmp != NULL; tmp = tmp->next, index++) { + LOG_REC *rec = tmp->data; + + levelstr = bits2level(rec->level); + items = rec->items == NULL ? NULL : log_items_get_list(rec); + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_LOG_LIST, index, rec->fname, + items != NULL ? items : "", levelstr, rec->autoopen ? " -autoopen" : "", + rec->handle != -1 ? " active" : ""); + + g_free_not_null(items); + g_free(levelstr); + } + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_LOG_LIST_FOOTER); +} + +static void cmd_log(const char *data, SERVER_REC *server, void *item) +{ + if (*data == '\0') + cmd_log_list(); + else + command_runsub("log", data, server, item); +} + +static LOG_REC *logs_find_item(int type, const char *item, const char *servertag, + LOG_ITEM_REC **ret_item) +{ + LOG_ITEM_REC *logitem; + GSList *tmp; + + for (tmp = logs; tmp != NULL; tmp = tmp->next) { + LOG_REC *log = tmp->data; + + if (type == LOG_ITEM_TARGET && log->temp == 0) continue; + logitem = log_item_find(log, type, item, servertag); + if (logitem != NULL) { + if (ret_item != NULL) *ret_item = logitem; + return log; + } + } + + return NULL; +} + +/* SYNTAX: WINDOW LOG on|off|toggle [<filename>] */ +static void cmd_window_log(const char *data) +{ + LOG_REC *log; + char *set, *fname, window[MAX_INT_STRLEN]; + void *free_arg; + int open_log, close_log; + + if (!cmd_get_params(data, &free_arg, 2, &set, &fname)) + return; + + ltoa(window, active_win->refnum); + log = logs_find_item(LOG_ITEM_WINDOW_REFNUM, window, NULL, NULL); + + open_log = close_log = FALSE; + if (g_ascii_strcasecmp(set, "ON") == 0) + open_log = TRUE; + else if (g_ascii_strcasecmp(set, "OFF") == 0) { + close_log = TRUE; + } else if (g_ascii_strcasecmp(set, "TOGGLE") == 0) { + open_log = log == NULL; + close_log = log != NULL; + } else { + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_NOT_TOGGLE); + cmd_params_free(free_arg); + return; + } + + if (open_log && log == NULL) { + /* irc.log.<windowname> or irc.log.Window<ref#> */ + fname = *fname != '\0' ? g_strdup(fname) : + g_strdup_printf("~/irc.log.%s%s", + active_win->name != NULL ? active_win->name : "Window", + active_win->name != NULL ? "" : window); + log = log_create_rec(fname, MSGLEVEL_ALL); + log->colorizer = log_colorizer_strip; + log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window, NULL); + log_update(log); + g_free(fname); + } + + if (open_log && log != NULL) { + log_start_logging(log); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_OPENED, log->fname); + } else if (close_log && log != NULL && log->handle != -1) { + log_stop_logging(log); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_CLOSED, log->fname); + } + + cmd_params_free(free_arg); +} + +/* Create log file entry to window, but don't start logging */ +/* SYNTAX: WINDOW LOGFILE <file> */ +static void cmd_window_logfile(const char *data) +{ + LOG_REC *log; + char window[MAX_INT_STRLEN]; + void *free_arg; + char *fname; + + if (!cmd_get_params(data, &free_arg, 1, &fname)) { + return; + } + + if (!fname || strlen(fname) == 0) { + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + } + + ltoa(window, active_win->refnum); + log = logs_find_item(LOG_ITEM_WINDOW_REFNUM, window, NULL, NULL); + + if (log != NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_WINDOWLOG_FILE_LOGGING); + cmd_params_free(free_arg); + return; + } + + log = log_create_rec(fname, MSGLEVEL_ALL); + log->colorizer = log_colorizer_strip; + log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window, NULL); + log_update(log); + + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_WINDOWLOG_FILE, data); + + cmd_params_free(free_arg); +} + +/* window's refnum changed - update the logs to log the new window refnum */ +static void sig_window_refnum_changed(WINDOW_REC *window, gpointer old_refnum) +{ + char winnum[MAX_INT_STRLEN]; + LOG_REC *log; + LOG_ITEM_REC *item; + + ltoa(winnum, GPOINTER_TO_INT(old_refnum)); + log = logs_find_item(LOG_ITEM_WINDOW_REFNUM, winnum, NULL, &item); + + if (log != NULL) { + ltoa(winnum, window->refnum); + + g_free(item->name); + item->name = g_strdup(winnum); + } +} + +static void sig_server_disconnected(SERVER_REC *server) +{ + LOG_ITEM_REC *logitem; + GSList *tmp, *next; + + for (tmp = logs; tmp != NULL; tmp = next) { + LOG_REC *log = tmp->data; + next = tmp->next; + + if (!log->temp || log->items == NULL) + continue; + + logitem = log->items->data; + if (logitem->type == LOG_ITEM_TARGET && logitem->servertag != NULL && + g_ascii_strcasecmp(logitem->servertag, server->tag) == 0 && + server_ischannel( + server, logitem->name)) /* kludge again.. so we won't close dcc chats */ + log_close(log); + } +} + +static void autologs_close_all(void) +{ + GSList *tmp, *next; + + for (tmp = logs; tmp != NULL; tmp = next) { + LOG_REC *rec = tmp->data; + + next = tmp->next; + if (rec->temp) log_close(rec); + } +} + +/* '%' -> '%%', badness -> '_' */ +static char *escape_target(const char *target) +{ + char *str, *p; + + p = str = g_malloc(strlen(target)*2+1); + while (*target != '\0') { + if (strchr("/\\|*?\"<>:", *target)) + *p++ = '_'; + else { + if (*target == '%') + *p++ = '%'; + *p++ = *target; + } + + target++; + } + *p = '\0'; + + return str; +} + +static void autolog_open(SERVER_REC *server, const char *server_tag, const char *target) +{ + LOG_REC *log; + char *fname, *dir, *fixed_target, *params; + + log = logs_find_item(LOG_ITEM_TARGET, target, server_tag, NULL); + if (log != NULL && !log->failed) { + log_start_logging(log); + return; + } + + /* '/' -> '_' - don't even accidentally try to log to + #../../../file if you happen to join to such channel.. + similar for some characters that are metacharacters + and/or illegal in Windows filenames. + + '%' -> '%%' - so strftime() won't mess with them */ + fixed_target = escape_target(target); + if (CHAT_PROTOCOL(server)->case_insensitive) + ascii_strdown(fixed_target); + + /* $0 = target, $1 = server tag */ + params = g_strconcat(fixed_target, " ", server_tag, NULL); + g_free(fixed_target); + + fname = parse_special_string(autolog_path, server, NULL, params, NULL, 0); + g_free(params); + + if (log_find(fname) == NULL) { + log = log_create_rec(fname, autolog_level); + if (!settings_get_bool("autolog_colors")) + log->colorizer = log_colorizer_strip; + log_item_add(log, LOG_ITEM_TARGET, target, server_tag); + + dir = g_path_get_dirname(log->real_fname); +#ifdef HAVE_CAPSICUM + capsicum_mkdir_with_parents_wrapper(dir, log_dir_create_mode); +#else + g_mkdir_with_parents(dir, log_dir_create_mode); +#endif + g_free(dir); + + log->temp = TRUE; + log_update(log); + log_start_logging(log); + } + g_free(fname); +} + +static void autolog_open_check(TEXT_DEST_REC *dest) +{ + const char *deftarget; + SERVER_REC *server = dest->server; + const char *server_tag = dest->server_tag; + const char *target = dest->target; + int level = dest->level; + + /* FIXME: kind of a kludge, but we don't want to reopen logs when + we're parting the channel with /WINDOW CLOSE.. Maybe a small + timeout would be nice instead of immediately closing the log file + after "window item destroyed" */ + if (level == MSGLEVEL_PARTS || (autolog_level & level) == 0 || target == NULL || + *target == '\0') + return; + + deftarget = server ? server->nick : "unknown"; + + /* log only channels that have been saved to the config */ + if (settings_get_bool("autolog_only_saved_channels") && IS_CHANNEL(window_item_find(server, target)) + && channel_setup_find(target, server_tag) == NULL) + return; + + if (autolog_ignore_targets != NULL && strarray_find_dest(autolog_ignore_targets, dest)) + return; + + if (target != NULL) + autolog_open(server, server_tag, g_strcmp0(target, "*") ? target : deftarget); +} + +static void log_single_line(WINDOW_REC *window, const char *server_tag, const char *target, + int level, time_t t, const char *text) +{ + char windownum[MAX_INT_STRLEN]; + LOG_REC *log; + + if (window != NULL) { + /* save to log created with /WINDOW LOG */ + ltoa(windownum, window->refnum); + log = logs_find_item(LOG_ITEM_WINDOW_REFNUM, windownum, NULL, NULL); + if (log != NULL) + log_write_rec(log, text, level, t); + } + + log_file_write(server_tag, target, level, t, text, FALSE); +} + +static void log_line(TEXT_DEST_REC *dest, const char *text) +{ + char **lines, **tmp; + time_t t = (time_t) -1; + + if (dest->level == MSGLEVEL_NEVER) + return; + + /* let autolog open the log records */ + autolog_open_check(dest); + + if (logs == NULL) + return; + + /* text may contain one or more lines, log wants to eat them one + line at a time */ + lines = g_strsplit(text, "\n", -1); + if (log_server_time && dest->meta != NULL) { + char *val; + if ((val = g_hash_table_lookup(dest->meta, "time")) != NULL) { + GDateTime *time; + if ((time = g_date_time_new_from_iso8601(val, NULL)) != NULL) { + t = g_date_time_to_unix(time); + g_date_time_unref(time); + } + } + } + for (tmp = lines; *tmp != NULL; tmp++) + log_single_line(dest->window, dest->server_tag, dest->target, dest->level, t, *tmp); + g_strfreev(lines); +} + +static void sig_printtext(TEXT_DEST_REC *dest, const char *text, const char *stripped) +{ + if (skip_next_printtext) { + skip_next_printtext = FALSE; + return; + } + + log_line(dest, text); +} + +static void sig_print_format(THEME_REC *theme, const char *module, TEXT_DEST_REC *dest, + void *formatnum, char **args) +{ + char *str, *linestart, *tmp; + + if (log_theme == NULL) { + /* theme isn't loaded for some reason (/reload destroys it), + reload it. */ + log_theme = theme_load(log_theme_name); + if (log_theme == NULL) return; + } + + if (theme == log_theme) + return; + + str = format_get_text_theme_charargs(log_theme, module, dest, GPOINTER_TO_INT(formatnum), + args); + if (str != NULL && *str != '\0') { + skip_next_printtext = TRUE; + + /* add the line start format */ + linestart = format_get_level_tag(log_theme, dest); + tmp = str; + str = format_add_linestart(tmp, linestart); + g_free_not_null(linestart); + g_free(tmp); + + /* strip colors from text, log it. */ + log_line(dest, str); + } + g_free(str); + +} + +static int sig_autoremove(void) +{ + SERVER_REC *server; + LOG_ITEM_REC *logitem; + GSList *tmp, *next; + time_t removetime; + + removetime = time(NULL) - AUTOLOG_INACTIVITY_CLOSE; + for (tmp = logs; tmp != NULL; tmp = next) { + LOG_REC *log = tmp->data; + + next = tmp->next; + + if (!log->temp || log->last > removetime || log->items == NULL) + continue; + + /* Close only logs with private messages */ + logitem = log->items->data; + if (logitem->servertag == NULL) + continue; + + server = server_find_tag(logitem->servertag); + if (logitem->type == LOG_ITEM_TARGET && server != NULL && + !server_ischannel(server, logitem->name)) + log_close(log); + } + return 1; +} + +static void sig_window_item_remove(WINDOW_REC *window, WI_ITEM_REC *item) +{ + LOG_REC *log; + + log = logs_find_item(LOG_ITEM_TARGET, item->visible_name, + item->server == NULL ? NULL : item->server->tag, NULL); + if (log != NULL && log->temp) + log_close(log); +} + +static void sig_log_locked(LOG_REC *log) +{ + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_LOG_LOCKED, log->real_fname); +} + +static void sig_log_create_failed(LOG_REC *log) +{ + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_LOG_CREATE_FAILED, log->real_fname, + g_strerror(errno)); +} + +static void sig_log_new(LOG_REC *log) +{ + if (!settings_get_bool("awaylog_colors") && + g_strcmp0(log->fname, settings_get_str("awaylog_file")) == 0) + log->colorizer = log_colorizer_strip; +} + +static void sig_log_config_read(LOG_REC *log, CONFIG_NODE *node) +{ + if (!config_node_get_bool(node, "colors", FALSE)) + log->colorizer = log_colorizer_strip; +} + +static void sig_log_config_save(LOG_REC *log, CONFIG_NODE *node) +{ + if (log->colorizer == NULL) + iconfig_node_set_bool(node, "colors", TRUE); + else + iconfig_node_set_str(node, "colors", NULL); +} + +static void sig_awaylog_show(LOG_REC *log, gpointer pmsgs, gpointer pfilepos) +{ + char *str; + int msgs, filepos; + + msgs = GPOINTER_TO_INT(pmsgs); + filepos = GPOINTER_TO_INT(pfilepos); + + if (msgs == 0) + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_NO_AWAY_MSGS, log->real_fname); + else { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_AWAY_MSGS, log->real_fname, msgs); + + str = g_strdup_printf("\"%s\" %d", log->real_fname, filepos); + signal_emit("command cat", 1, str); + g_free(str); + } +} + +static void sig_theme_destroyed(THEME_REC *theme) +{ + if (theme == log_theme) + log_theme = NULL; +} + +static void read_settings(void) +{ + int old_autolog = autolog_level; + + g_free_not_null(autolog_path); + autolog_path = g_strdup(settings_get_str("autolog_path")); + + autolog_level = !settings_get_bool("autolog") ? 0 : + settings_get_level("autolog_level"); + + if (old_autolog && !autolog_level) + autologs_close_all(); + + /* write to log files with different theme? */ + if (log_theme_name != NULL) + signal_remove("print format", (SIGNAL_FUNC) sig_print_format); + + g_free_not_null(log_theme_name); + log_theme_name = g_strdup(settings_get_str("log_theme")); + + if (*log_theme_name == '\0') { + g_free(log_theme_name); + log_theme_name = NULL; + } + else + signal_add("print format", (SIGNAL_FUNC) sig_print_format); + + log_theme = log_theme_name == NULL ? NULL : + theme_load(log_theme_name); + + if (autolog_ignore_targets != NULL) + g_strfreev(autolog_ignore_targets); + + autolog_ignore_targets = g_strsplit(settings_get_str("autolog_ignore_targets"), " ", -1); + + log_server_time = settings_get_choice("log_server_time"); + if (log_server_time == 2) { + SETTINGS_REC *rec = settings_get_record("show_server_time"); + if (rec != NULL) + log_server_time = settings_get_bool("show_server_time"); + } +} + +void fe_log_init(void) +{ + autoremove_tag = g_timeout_add(60000, (GSourceFunc) sig_autoremove, NULL); + skip_next_printtext = FALSE; + + settings_add_bool("log", "awaylog_colors", TRUE); + settings_add_bool("log", "autolog", FALSE); + settings_add_bool("log", "autolog_colors", FALSE); + settings_add_bool("log", "autolog_only_saved_channels", FALSE); + settings_add_choice("log", "log_server_time", 2, "off;on;auto"); + settings_add_str("log", "autolog_path", "~/irclogs/$tag/$0.log"); + settings_add_level("log", "autolog_level", "all -crap -clientcrap -ctcps"); + settings_add_str("log", "log_theme", ""); + settings_add_str("log", "autolog_ignore_targets", ""); + + autolog_level = 0; + log_theme_name = NULL; + read_settings(); + + command_bind("log", NULL, (SIGNAL_FUNC) cmd_log); + command_bind("log open", NULL, (SIGNAL_FUNC) cmd_log_open); + command_bind("log close", NULL, (SIGNAL_FUNC) cmd_log_close); + command_bind("log start", NULL, (SIGNAL_FUNC) cmd_log_start); + command_bind("log stop", NULL, (SIGNAL_FUNC) cmd_log_stop); + command_bind("window log", NULL, (SIGNAL_FUNC) cmd_window_log); + command_bind("window logfile", NULL, (SIGNAL_FUNC) cmd_window_logfile); + signal_add_first("print text", (SIGNAL_FUNC) sig_printtext); + signal_add("window item remove", (SIGNAL_FUNC) sig_window_item_remove); + signal_add("window refnum changed", (SIGNAL_FUNC) sig_window_refnum_changed); + signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected); + signal_add("log locked", (SIGNAL_FUNC) sig_log_locked); + signal_add("log create failed", (SIGNAL_FUNC) sig_log_create_failed); + signal_add("log new", (SIGNAL_FUNC) sig_log_new); + signal_add("log config read", (SIGNAL_FUNC) sig_log_config_read); + signal_add("log config save", (SIGNAL_FUNC) sig_log_config_save); + signal_add("awaylog show", (SIGNAL_FUNC) sig_awaylog_show); + signal_add("theme destroyed", (SIGNAL_FUNC) sig_theme_destroyed); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); + + command_set_options("log open", "noopen autoopen -targets window colors"); +} + +void fe_log_deinit(void) +{ + g_source_remove(autoremove_tag); + if (log_theme_name != NULL) + signal_remove("print format", (SIGNAL_FUNC) sig_print_format); + + command_unbind("log", (SIGNAL_FUNC) cmd_log); + command_unbind("log open", (SIGNAL_FUNC) cmd_log_open); + command_unbind("log close", (SIGNAL_FUNC) cmd_log_close); + command_unbind("log start", (SIGNAL_FUNC) cmd_log_start); + command_unbind("log stop", (SIGNAL_FUNC) cmd_log_stop); + command_unbind("window log", (SIGNAL_FUNC) cmd_window_log); + command_unbind("window logfile", (SIGNAL_FUNC) cmd_window_logfile); + signal_remove("print text", (SIGNAL_FUNC) sig_printtext); + signal_remove("window item remove", (SIGNAL_FUNC) sig_window_item_remove); + signal_remove("window refnum changed", (SIGNAL_FUNC) sig_window_refnum_changed); + signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected); + signal_remove("log locked", (SIGNAL_FUNC) sig_log_locked); + signal_remove("log create failed", (SIGNAL_FUNC) sig_log_create_failed); + signal_remove("log new", (SIGNAL_FUNC) sig_log_new); + signal_remove("log config read", (SIGNAL_FUNC) sig_log_config_read); + signal_remove("log config save", (SIGNAL_FUNC) sig_log_config_save); + signal_remove("awaylog show", (SIGNAL_FUNC) sig_awaylog_show); + signal_remove("theme destroyed", (SIGNAL_FUNC) sig_theme_destroyed); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); + + if (autolog_ignore_targets != NULL) + g_strfreev(autolog_ignore_targets); + + g_free_not_null(autolog_path); + g_free_not_null(log_theme_name); +} diff --git a/src/fe-common/core/fe-messages.c b/src/fe-common/core/fe-messages.c new file mode 100644 index 0000000..355d4fd --- /dev/null +++ b/src/fe-common/core/fe-messages.c @@ -0,0 +1,858 @@ +/* + fe-messages.c : irssi + + Copyright (C) 2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/special-vars.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/core/servers.h> +#include <irssi/src/core/channels.h> +#include <irssi/src/core/nicklist.h> +#include <irssi/src/core/ignore.h> + +#include <irssi/src/fe-common/core/window-items.h> +#include <irssi/src/fe-common/core/fe-queries.h> +#include <irssi/src/fe-common/core/hilight-text.h> +#include <irssi/src/fe-common/core/printtext.h> + +#define ishighalnum(c) ((unsigned char) (c) >= 128 || i_isalnum(c)) +#define isnickchar(a) \ + (i_isalnum(a) || (a) == '`' || (a) == '-' || (a) == '_' || \ + (a) == '[' || (a) == ']' || (a) == '{' || (a) == '}' || \ + (a) == '|' || (a) == '\\' || (a) == '^') + +GHashTable *printnicks; + +/* convert _underlined_, /italics/, and *bold* words (and phrases) to use real + underlining or bolding */ +char *expand_emphasis(WI_ITEM_REC *item, const char *text) +{ + GString *str; + char *ret; + int pos; + int emphasis_italics; + + g_return_val_if_fail(text != NULL, NULL); + + emphasis_italics = settings_get_bool("emphasis_italics"); + + str = g_string_new(text); + + for (pos = 0; pos < str->len; pos++) { + char type, *bgn, *end; + + bgn = str->str + pos; + + if (*bgn == '*') + type = 2; /* bold */ + else if (*bgn == '/' && emphasis_italics) + type = 29; /* italics */ + else if (*bgn == '_') + type = 31; /* underlined */ + else + continue; + + /* check that the beginning marker starts a word, and + that the matching end marker ends a word */ + if ((pos > 0 && bgn[-1] != ' ') || !ishighalnum(bgn[1])) + continue; + if ((end = strchr(bgn+1, *bgn)) == NULL) + continue; + if (!ishighalnum(end[-1]) || ishighalnum(end[1]) || + end[1] == type || end[1] == '*' || end[1] == '_' || + /* special case for italics to not emphasise + common paths by skipping /.../.X */ + (type == 29 && i_ispunct(end[1]) && ishighalnum(end[2]))) + continue; + + if (IS_CHANNEL(item)) { + /* check that this isn't a _nick_, we don't want to + use emphasis on them. */ + int found; + char c; + char *end2; + + /* check if _foo_ is a nick */ + c = end[1]; + end[1] = '\0'; + found = nicklist_find(CHANNEL(item), bgn) != NULL; + end[1] = c; + if (found) continue; + + /* check if the whole 'word' (e.g. "_foo_^") is a nick + in "_foo_^ ", end will be the second _, end2 the ^ */ + end2 = end; + while (isnickchar(end2[1])) + end2++; + c = end2[1]; + end2[1] = '\0'; + found = nicklist_find(CHANNEL(item), bgn) != NULL; + end2[1] = c; + if (found) continue; + } + + /* allow only *word* emphasis, not *multiple words* */ + if (!settings_get_bool("emphasis_multiword")) { + char *c; + for (c = bgn+1; c != end; c++) { + if (!ishighalnum(*c)) + break; + } + if (c != end) continue; + } + + if (settings_get_bool("emphasis_replace")) { + *bgn = *end = type; + pos += (end-bgn); + } else { + g_string_insert_c(str, pos, type); + pos += (end - bgn) + 2; + g_string_insert_c(str, pos++, type); + } + } + + ret = str->str; + g_string_free(str, FALSE); + return ret; +} + +static char *channel_get_nickmode_rec(NICK_REC *nickrec) +{ + char *emptystr; + char *nickmode; + + if (!settings_get_bool("show_nickmode")) + return g_strdup(""); + + emptystr = settings_get_bool("show_nickmode_empty") ? " " : ""; + + if (nickrec == NULL || nickrec->prefixes[0] == '\0') + nickmode = g_strdup(emptystr); + else { + nickmode = g_malloc(2); + nickmode[0] = nickrec->prefixes[0]; + nickmode[1] = '\0'; + } + return nickmode; +} + +char *channel_get_nickmode(CHANNEL_REC *channel, const char *nick) +{ + g_return_val_if_fail(nick != NULL, NULL); + + return channel_get_nickmode_rec(channel == NULL ? NULL : + nicklist_find(channel, nick)); +} + +static void sig_message_public(SERVER_REC *server, const char *msg, + const char *nick, const char *address, + const char *target, NICK_REC *nickrec) +{ + CHANNEL_REC *chanrec; + const char *printnick; + int for_me, print_channel, level; + char *nickmode, *color, *freemsg = NULL; + HILIGHT_REC *hilight; + TEXT_DEST_REC dest; + + /* NOTE: this may return NULL if some channel is just closed with + /WINDOW CLOSE and server still sends the few last messages */ + chanrec = channel_find(server, target); + if (nickrec == NULL && chanrec != NULL) + nickrec = nicklist_find(chanrec, nick); + + for_me = !settings_get_bool("hilight_nick_matches") ? FALSE : + !settings_get_bool("hilight_nick_matches_everywhere") ? + nick_match_msg(chanrec, msg, server->nick) : + nick_match_msg_everywhere(chanrec, msg, server->nick); + hilight = for_me ? NULL : + hilight_match_nick(server, target, nick, address, MSGLEVEL_PUBLIC, msg); + color = (hilight == NULL) ? NULL : hilight_get_color(hilight); + + print_channel = chanrec == NULL || + !window_item_is_active((WI_ITEM_REC *) chanrec); + if (!print_channel && settings_get_bool("print_active_channel") && + window_item_window((WI_ITEM_REC *) chanrec)->items->next != NULL) + print_channel = TRUE; + + level = MSGLEVEL_PUBLIC; + if (for_me) + level |= MSGLEVEL_HILIGHT; + + ignore_check_plus(server, nick, address, target, msg, &level, FALSE); + if (level & MSGLEVEL_NOHILIGHT) { + for_me = FALSE; + g_free_and_null(color); + level &= ~MSGLEVEL_HILIGHT; + } + + if (settings_get_bool("emphasis")) + msg = freemsg = expand_emphasis((WI_ITEM_REC *) chanrec, msg); + + /* get nick mode & nick what to print the msg with + (in case there's multiple identical nicks) */ + nickmode = channel_get_nickmode_rec(nickrec); + printnick = nickrec == NULL ? nick : + g_hash_table_lookup(printnicks, nickrec); + if (printnick == NULL) + printnick = nick; + + format_create_dest(&dest, server, target, level, NULL); + dest.address = address; + dest.nick = nick; + if (color != NULL) { + /* highlighted nick */ + hilight_update_text_dest(&dest,hilight); + if (!print_channel) /* message to active channel in window */ + printformat_dest(&dest, TXT_PUBMSG_HILIGHT, color, + printnick, msg, nickmode); + else /* message to not existing/active channel */ + printformat_dest(&dest, TXT_PUBMSG_HILIGHT_CHANNEL, + color, printnick, target, msg, + nickmode); + } else { + if (!print_channel) + printformat_dest(&dest, + for_me ? TXT_PUBMSG_ME : TXT_PUBMSG, + printnick, msg, nickmode); + else + printformat_dest(&dest, + for_me ? TXT_PUBMSG_ME_CHANNEL : + TXT_PUBMSG_CHANNEL, + printnick, target, msg, nickmode); + } + + g_free_not_null(nickmode); + g_free_not_null(freemsg); + g_free_not_null(color); +} + +static void sig_message_private(SERVER_REC *server, const char *msg, + const char *nick, const char *address, const char *target) +{ + QUERY_REC *query; + char *freemsg = NULL; + int level = MSGLEVEL_MSGS; + + /* own message returned by bouncer? */ + int own = (!g_strcmp0(nick, server->nick)); + + query = query_find(server, own ? target : nick); + + if (settings_get_bool("emphasis")) + msg = freemsg = expand_emphasis((WI_ITEM_REC *) query, msg); + + ignore_check_plus(server, nick, address, NULL, msg, &level, FALSE); + + if (own) { + printformat(server, target, level, + query == NULL ? TXT_OWN_MSG_PRIVATE : + TXT_OWN_MSG_PRIVATE_QUERY, target, msg, server->nick); + } else { + printformat(server, nick, level, + query == NULL ? TXT_MSG_PRIVATE : + TXT_MSG_PRIVATE_QUERY, nick, address, msg); + } + + g_free_not_null(freemsg); +} + +static void sig_message_own_public(SERVER_REC *server, const char *msg, + const char *target) +{ + WINDOW_REC *window; + CHANNEL_REC *channel; + char *nickmode; + char *freemsg = NULL; + int print_channel; + channel = channel_find(server, target); + if (channel != NULL) + target = channel->visible_name; + + nickmode = channel_get_nickmode(channel, server->nick); + + window = channel == NULL ? NULL : + window_item_window((WI_ITEM_REC *) channel); + + print_channel = window == NULL || + window->active != (WI_ITEM_REC *) channel; + + if (!print_channel && settings_get_bool("print_active_channel") && + window != NULL && g_slist_length(window->items) > 1) + print_channel = TRUE; + + if (settings_get_bool("emphasis")) + msg = freemsg = expand_emphasis((WI_ITEM_REC *) channel, msg); + + if (!print_channel) { + printformat(server, target, MSGLEVEL_PUBLIC | MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT, + TXT_OWN_MSG, server->nick, msg, nickmode); + } else { + printformat(server, target, MSGLEVEL_PUBLIC | MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT, + TXT_OWN_MSG_CHANNEL, server->nick, target, msg, nickmode); + } + + g_free_not_null(nickmode); + g_free_not_null(freemsg); +} + +static void sig_message_own_private(SERVER_REC *server, const char *msg, + const char *target, const char *origtarget) +{ + QUERY_REC *query; + char *freemsg = NULL; + + g_return_if_fail(server != NULL); + g_return_if_fail(msg != NULL); + if (target == NULL) { + /* this should only happen if some special target failed and + we should display some error message. currently the special + targets are only ',' and '.'. */ + g_return_if_fail(g_strcmp0(origtarget, ",") == 0 || + g_strcmp0(origtarget, ".") == 0); + + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + *origtarget == ',' ? TXT_NO_MSGS_GOT : + TXT_NO_MSGS_SENT); + signal_stop(); + return; + } + + query = privmsg_get_query(server, target, TRUE, MSGLEVEL_MSGS); + + if (settings_get_bool("emphasis")) + msg = freemsg = expand_emphasis((WI_ITEM_REC *) query, msg); + + printformat(server, target, + MSGLEVEL_MSGS | MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT, + query == NULL ? TXT_OWN_MSG_PRIVATE : + TXT_OWN_MSG_PRIVATE_QUERY, target, msg, server->nick); + + g_free_not_null(freemsg); +} + +static void sig_message_join(SERVER_REC *server, const char *channel, + const char *nick, const char *address, + const char *account, const char *realname) +{ + int level = MSGLEVEL_JOINS; + + ignore_check_plus(server, nick, address, channel, NULL, &level, FALSE); + + if (settings_get_bool("show_extended_join")) { + int txt; + if (*account == '\0') txt = TXT_JOIN; + else if (g_strcmp0("*", account) == 0) txt = TXT_JOIN_EXTENDED; + else txt = TXT_JOIN_EXTENDED_ACCOUNT; + printformat(server, channel, level, + txt, nick, address, channel, account, realname); + } else { + printformat(server, channel, level, + TXT_JOIN, nick, address, channel, account, realname); + } +} + +static void sig_message_part(SERVER_REC *server, const char *channel, + const char *nick, const char *address, + const char *reason) +{ + int level = MSGLEVEL_PARTS; + + ignore_check_plus(server, nick, address, channel, NULL, &level, FALSE); + + printformat(server, channel, level, + TXT_PART, nick, address, channel, reason); +} + +static void spread_server_message_to_windows(SERVER_REC *server, gboolean once, + gboolean in_query, + int base_level, + int txt, int txt_once, + const char *nick, const char *address, + const char *data, + const char *ignore_data + ) +{ + WINDOW_REC *window; + GString *chans; + GSList *tmp, *windows; + char *print_channel; + int count, level = base_level; + + if (ignore_check_plus(server, nick, address, NULL, ignore_data, &level, TRUE)) + return; + + print_channel = NULL; + + count = 0; windows = NULL; + chans = g_string_new(NULL); + for (tmp = server->channels; tmp != NULL; tmp = tmp->next) { + CHANNEL_REC *rec; + level = base_level; + rec = tmp->data; + + if (!nicklist_find(rec, nick)) { + continue; + } + + if (ignore_check_plus(server, nick, address, rec->visible_name, + ignore_data, &level, TRUE)) { + count++; + continue; + } + + if (print_channel == NULL || + active_win->active == (WI_ITEM_REC *) rec) { + print_channel = rec->visible_name; + } + + if (once) { + g_string_append_printf(chans, "%s,", rec->visible_name); + } else { + window = window_item_window((WI_ITEM_REC *) rec); + if (g_slist_find(windows, window) == NULL) { + windows = g_slist_prepend(windows, window); + printformat(server, rec->visible_name, + level, + txt, nick, address, data, + rec->visible_name); + } + } + count++; + } + g_slist_free(windows); + + if (!once && in_query) { + /* check if you had query with the nick and + display the change there too */ + QUERY_REC *query = query_find(server, nick); + if (query != NULL) { + printformat(server, nick, level, + txt, nick, address, data, ""); + } + } + + if (once || count == 0) { + if (chans->len > 0) { + g_string_truncate(chans, chans->len-1); + } + printformat(server, print_channel, base_level, + count <= 1 ? txt : txt_once, + nick, address, data, chans->str); + } + g_string_free(chans, TRUE); +} + +static void sig_message_host_changed(SERVER_REC *server, const char *nick, + const char *address, const char *old_address) +{ + spread_server_message_to_windows( + server, + settings_get_bool("show_quit_once"), + TRUE, + MSGLEVEL_JOINS, + TXT_HOST_CHANGED, TXT_HOST_CHANGED, + nick, address, old_address, + NULL + ); +} + +static void sig_message_account_changed(SERVER_REC *server, const char *nick, + const char *address, const char *account) +{ + gboolean logged_in; + int txt; + + if (!settings_get_bool("show_account_notify")) + return; + + logged_in = g_strcmp0("*", account) != 0; + txt = logged_in ? TXT_LOGGED_IN : TXT_LOGGED_OUT; + + spread_server_message_to_windows( + server, + settings_get_bool("show_quit_once"), + TRUE, + MSGLEVEL_MODES, + txt, txt, + nick, address, account, + "account" + ); +} + +static void sig_message_quit(SERVER_REC *server, const char *nick, + const char *address, const char *reason) +{ + spread_server_message_to_windows( + server, + settings_get_bool("show_quit_once"), + TRUE, + MSGLEVEL_QUITS, + TXT_QUIT, TXT_QUIT_ONCE, + nick, address, reason, + reason + ); +} + +static void sig_message_kick(SERVER_REC *server, const char *channel, + const char *nick, const char *kicker, + const char *address, const char *reason) +{ + int level = MSGLEVEL_KICKS; + + ignore_check_plus(server, kicker, address, channel, reason, &level, FALSE); + + printformat(server, channel, level, + TXT_KICK, nick, channel, kicker, reason, address); +} + +static void print_nick_change_channel(SERVER_REC *server, const char *channel, + const char *newnick, const char *oldnick, + const char *address, + int ownnick) +{ + int level; + + level = MSGLEVEL_NICKS; + if (ownnick) level |= MSGLEVEL_NO_ACT; + if (ignore_check_plus(server, oldnick, address, + channel, newnick, &level, TRUE)) + return; + + printformat(server, channel, level, + ownnick ? TXT_YOUR_NICK_CHANGED : TXT_NICK_CHANGED, + oldnick, newnick, channel, address); +} + +static void print_nick_change(SERVER_REC *server, const char *newnick, + const char *oldnick, const char *address, + int ownnick) +{ + GSList *tmp, *windows; + int msgprint; + + msgprint = FALSE; + + /* Print to each channel where the nick is. + Don't print more than once to the same window. */ + windows = NULL; + for (tmp = server->channels; tmp != NULL; tmp = tmp->next) { + CHANNEL_REC *channel = tmp->data; + WINDOW_REC *window = + window_item_window((WI_ITEM_REC *) channel); + + if (nicklist_find(channel, newnick) == NULL || + g_slist_find(windows, window) != NULL) + continue; + + windows = g_slist_append(windows, window); + print_nick_change_channel(server, channel->visible_name, + newnick, oldnick, address, ownnick); + msgprint = TRUE; + } + + g_slist_free(windows); + + if (!msgprint && ownnick) { + printformat(server, NULL, MSGLEVEL_NICKS, + TXT_YOUR_NICK_CHANGED, oldnick, newnick, "", + address); + } +} + +static void sig_message_nick(SERVER_REC *server, const char *newnick, + const char *oldnick, const char *address) +{ + print_nick_change(server, newnick, oldnick, address, FALSE); +} + +static void sig_message_own_nick(SERVER_REC *server, const char *newnick, + const char *oldnick, const char *address) +{ + if (!settings_get_bool("show_own_nickchange_once")) + print_nick_change(server, newnick, oldnick, address, TRUE); + else { + printformat(server, NULL, MSGLEVEL_NICKS, + TXT_YOUR_NICK_CHANGED, oldnick, newnick, "", + address); + } +} + +static void sig_message_invite(SERVER_REC *server, const char *channel, + const char *nick, const char *address) +{ + char *str; + + str = show_lowascii(channel); + printformat(server, NULL, MSGLEVEL_INVITES, + TXT_INVITE, nick, str, address); + g_free(str); +} + +static void sig_message_invite_other(SERVER_REC *server, const char *channel, + const char *invited, const char *nick, const char *address) +{ + char *str; + int level = MSGLEVEL_INVITES; + + ignore_check_plus(server, nick, address, channel, invited, &level, FALSE); + + str = show_lowascii(channel); + printformat(server, channel, level, + TXT_INVITE_OTHER, invited, nick, str, address); + g_free(str); +} + +static void sig_message_topic(SERVER_REC *server, const char *channel, + const char *topic, + const char *nick, const char *address) +{ + int level = MSGLEVEL_TOPICS; + + ignore_check_plus(server, nick, address, channel, topic, &level, FALSE); + + printformat(server, channel, level, + *topic != '\0' ? TXT_NEW_TOPIC : TXT_TOPIC_UNSET, + nick, channel, topic, address); +} + +static void sig_message_away_notify(SERVER_REC *server, const char *nick, + const char *addr, const char *awaymsg) +{ + int txt = *awaymsg == '\0' ? TXT_NOTIFY_UNAWAY_CHANNEL : + TXT_NOTIFY_AWAY_CHANNEL; + + if (!settings_get_bool("away_notify_public")) + return; + + spread_server_message_to_windows(server, FALSE, + FALSE, + MSGLEVEL_CRAP, + txt, txt, + nick, addr, + awaymsg, + awaymsg + ); +} + +static int printnick_exists(NICK_REC *first, NICK_REC *ignore, + const char *nick) +{ + char *printnick; + + while (first != NULL) { + if (first != ignore) { + printnick = g_hash_table_lookup(printnicks, first); + if (printnick != NULL && g_strcmp0(printnick, nick) == 0) + return TRUE; + } + + first = first->next; + } + + return FALSE; +} + +static NICK_REC *printnick_find_original(NICK_REC *nick) +{ + while (nick != NULL) { + if (g_hash_table_lookup(printnicks, nick) == NULL) + return nick; + + nick = nick->next; + } + + return NULL; +} + +static void sig_nicklist_new(CHANNEL_REC *channel, NICK_REC *nick) +{ + NICK_REC *firstnick; + GString *newnick; + char *nickhost, *p; + int n; + + firstnick = g_hash_table_lookup(channel->nicks, nick->nick); + if (firstnick->next == NULL) + return; + + if (nick == channel->ownnick) { + /* own nick is being added, might be a nick change and + someone else having the original nick already in use.. */ + nick = printnick_find_original(firstnick->next); + if (nick == NULL) + return; /* nope, we have it */ + } + + if (nick->host == NULL) + return; + + /* identical nick already exists, have to change it somehow.. */ + p = strchr(nick->host, '@'); + if (p == NULL) p = nick->host; else p++; + + nickhost = g_strdup_printf("%s@%s", nick->nick, p); + p = strchr(nickhost+strlen(nick->nick), '.'); + if (p != NULL) *p = '\0'; + + if (!printnick_exists(firstnick, nick, nickhost)) { + /* use nick@host */ + g_hash_table_insert(printnicks, nick, nickhost); + return; + } + + newnick = g_string_new(NULL); + n = 2; + do { + g_string_printf(newnick, "%s%d", nickhost, n); + n++; + } while (printnick_exists(firstnick, nick, newnick->str)); + + g_hash_table_insert(printnicks, nick, newnick->str); + g_string_free(newnick, FALSE); + g_free(nickhost); +} + +static void sig_nicklist_remove(CHANNEL_REC *channel, NICK_REC *nick) +{ + char *nickname; + + nickname = g_hash_table_lookup(printnicks, nick); + if (nickname != NULL) { + g_free(nickname); + g_hash_table_remove(printnicks, nick); + } +} + +static void sig_nicklist_changed(CHANNEL_REC *channel, NICK_REC *nick) +{ + sig_nicklist_remove(channel, nick); + sig_nicklist_new(channel, nick); +} + +static void sig_channel_joined(CHANNEL_REC *channel) +{ + NICK_REC *nick; + char *nickname; + + /* channel->ownnick is set at this point - check if our own nick + has been changed, if it was set it back to the original nick and + change the previous original to something else */ + + nickname = g_hash_table_lookup(printnicks, channel->ownnick); + if (nickname == NULL) + return; + + g_free(nickname); + g_hash_table_remove(printnicks, channel->ownnick); + + /* our own nick is guaranteed to be the first in list */ + nick = channel->ownnick->next; + while (nick != NULL) { + if (g_hash_table_lookup(printnicks, nick) == NULL) { + sig_nicklist_new(channel, nick); + break; + } + nick = nick->next; + } +} + +static void i_hash_free_value(void *key, void *value) +{ + g_free(value); +} + +void fe_messages_init(void) +{ + printnicks = g_hash_table_new((GHashFunc) g_direct_hash, + (GCompareFunc) g_direct_equal); + + settings_add_bool("lookandfeel", "hilight_nick_matches", TRUE); + settings_add_bool("lookandfeel", "hilight_nick_matches_everywhere", FALSE); + settings_add_bool("lookandfeel", "emphasis", TRUE); + settings_add_bool("lookandfeel", "emphasis_replace", FALSE); + settings_add_bool("lookandfeel", "emphasis_multiword", FALSE); + settings_add_bool("lookandfeel", "emphasis_italics", FALSE); + settings_add_bool("lookandfeel", "show_nickmode", TRUE); + settings_add_bool("lookandfeel", "show_nickmode_empty", TRUE); + settings_add_bool("lookandfeel", "print_active_channel", FALSE); + settings_add_bool("lookandfeel", "show_quit_once", FALSE); + settings_add_bool("lookandfeel", "show_own_nickchange_once", FALSE); + settings_add_bool("lookandfeel", "away_notify_public", FALSE); + settings_add_bool("lookandfeel", "show_extended_join", FALSE); + settings_add_bool("lookandfeel", "show_account_notify", FALSE); + + signal_add_last("message public", (SIGNAL_FUNC) sig_message_public); + signal_add_last("message private", (SIGNAL_FUNC) sig_message_private); + signal_add_last("message own_public", (SIGNAL_FUNC) sig_message_own_public); + signal_add_last("message own_private", (SIGNAL_FUNC) sig_message_own_private); + signal_add_last("message join", (SIGNAL_FUNC) sig_message_join); + signal_add_last("message host_changed", (SIGNAL_FUNC) sig_message_host_changed); + signal_add_last("message account_changed", (SIGNAL_FUNC) sig_message_account_changed); + signal_add_last("message part", (SIGNAL_FUNC) sig_message_part); + signal_add_last("message quit", (SIGNAL_FUNC) sig_message_quit); + signal_add_last("message kick", (SIGNAL_FUNC) sig_message_kick); + signal_add_last("message nick", (SIGNAL_FUNC) sig_message_nick); + signal_add_last("message own_nick", (SIGNAL_FUNC) sig_message_own_nick); + signal_add_last("message invite", (SIGNAL_FUNC) sig_message_invite); + signal_add_last("message invite_other", (SIGNAL_FUNC) sig_message_invite_other); + signal_add_last("message topic", (SIGNAL_FUNC) sig_message_topic); + signal_add_last("message away_notify", (SIGNAL_FUNC) sig_message_away_notify); + + signal_add("nicklist new", (SIGNAL_FUNC) sig_nicklist_new); + signal_add("nicklist remove", (SIGNAL_FUNC) sig_nicklist_remove); + signal_add("nicklist changed", (SIGNAL_FUNC) sig_nicklist_changed); + signal_add("nicklist host changed", (SIGNAL_FUNC) sig_nicklist_new); + signal_add("channel joined", (SIGNAL_FUNC) sig_channel_joined); +} + +void fe_messages_deinit(void) +{ + g_hash_table_foreach(printnicks, (GHFunc) i_hash_free_value, NULL); + g_hash_table_destroy(printnicks); + + signal_remove("message public", (SIGNAL_FUNC) sig_message_public); + signal_remove("message private", (SIGNAL_FUNC) sig_message_private); + signal_remove("message own_public", (SIGNAL_FUNC) sig_message_own_public); + signal_remove("message own_private", (SIGNAL_FUNC) sig_message_own_private); + signal_remove("message join", (SIGNAL_FUNC) sig_message_join); + signal_remove("message host_changed", (SIGNAL_FUNC) sig_message_host_changed); + signal_remove("message account_changed", (SIGNAL_FUNC) sig_message_account_changed); + signal_remove("message part", (SIGNAL_FUNC) sig_message_part); + signal_remove("message quit", (SIGNAL_FUNC) sig_message_quit); + signal_remove("message kick", (SIGNAL_FUNC) sig_message_kick); + signal_remove("message nick", (SIGNAL_FUNC) sig_message_nick); + signal_remove("message own_nick", (SIGNAL_FUNC) sig_message_own_nick); + signal_remove("message invite_other", (SIGNAL_FUNC) sig_message_invite_other); + signal_remove("message invite", (SIGNAL_FUNC) sig_message_invite); + signal_remove("message topic", (SIGNAL_FUNC) sig_message_topic); + signal_remove("message away_notify", (SIGNAL_FUNC) sig_message_away_notify); + + signal_remove("nicklist new", (SIGNAL_FUNC) sig_nicklist_new); + signal_remove("nicklist remove", (SIGNAL_FUNC) sig_nicklist_remove); + signal_remove("nicklist changed", (SIGNAL_FUNC) sig_nicklist_changed); + signal_remove("nicklist host changed", (SIGNAL_FUNC) sig_nicklist_new); + signal_remove("channel joined", (SIGNAL_FUNC) sig_channel_joined); +} diff --git a/src/fe-common/core/fe-messages.h b/src/fe-common/core/fe-messages.h new file mode 100644 index 0000000..03f7da7 --- /dev/null +++ b/src/fe-common/core/fe-messages.h @@ -0,0 +1,12 @@ +#ifndef IRSSI_FE_COMMON_CORE_FE_MESSAGES_H +#define IRSSI_FE_COMMON_CORE_FE_MESSAGES_H + +/* convert _underlined_ and *bold* words (and phrases) to use real + underlining or bolding */ +char *expand_emphasis(WI_ITEM_REC *item, const char *text); + +char *channel_get_nickmode(CHANNEL_REC *channel, const char *nick); + +extern GHashTable *printnicks; + +#endif diff --git a/src/fe-common/core/fe-modules.c b/src/fe-common/core/fe-modules.c new file mode 100644 index 0000000..955b9a9 --- /dev/null +++ b/src/fe-common/core/fe-modules.c @@ -0,0 +1,294 @@ +/* + fe-common-core.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/modules.h> +#include <irssi/src/core/modules-load.h> +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/chat-protocols.h> + +#include <irssi/src/fe-common/core/printtext.h> + +#ifdef HAVE_GMODULE + +static void sig_module_error(void *number, const char *data, + const char *rootmodule, const char *submodule) +{ + switch (GPOINTER_TO_INT(number)) { + case MODULE_ERROR_ALREADY_LOADED: + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + TXT_MODULE_ALREADY_LOADED, rootmodule, submodule); + break; + case MODULE_ERROR_LOAD: + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + TXT_MODULE_LOAD_ERROR, rootmodule, submodule, data); + break; + case MODULE_ERROR_VERSION_MISMATCH: + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + TXT_MODULE_VERSION_MISMATCH, rootmodule, submodule, data); + break; + case MODULE_ERROR_INVALID: + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + TXT_MODULE_INVALID, rootmodule, submodule); + break; + } +} + +static void sig_module_loaded(MODULE_REC *module, MODULE_FILE_REC *file) +{ + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_MODULE_LOADED, module->name, file->name); +} + +static void sig_module_unloaded(MODULE_REC *module, MODULE_FILE_REC *file) +{ + if (file != NULL && file->gmodule != NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_MODULE_UNLOADED, module->name, file->name); + } +} + +static int module_list_sub(MODULE_REC *module, int mark_type, + GString *submodules) +{ + GSList *tmp; + int all_dynamic, dynamic; + + g_string_truncate(submodules, 0); + + all_dynamic = -1; + for (tmp = module->files; tmp != NULL; tmp = tmp->next) { + MODULE_FILE_REC *file = tmp->data; + + /* if there's dynamic and static modules mixed, we'll need + to specify them separately */ + if (!mark_type) { + dynamic = file->gmodule != NULL; + if (all_dynamic != -1 && all_dynamic != dynamic) { + return module_list_sub(module, TRUE, + submodules); + } + all_dynamic = dynamic; + } + + if (submodules->len > 0) + g_string_append_c(submodules, ' '); + g_string_append(submodules, file->name); + if (mark_type) { + g_string_append(submodules, file->gmodule == NULL ? + " (static)" : " (dynamic)"); + } + } + + return all_dynamic; +} + +static void cmd_load_list(void) +{ + GSList *tmp; + GString *submodules; + const char *type; + int dynamic; + + submodules = g_string_new(NULL); + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_MODULE_HEADER); + for (tmp = modules; tmp != NULL; tmp = tmp->next) { + MODULE_REC *rec = tmp->data; + + dynamic = module_list_sub(rec, FALSE, submodules); + type = dynamic == -1 ? "mixed" : + dynamic ? "dynamic" : "static"; + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, + TXT_MODULE_LINE, rec->name, type, submodules->str); + } + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_MODULE_FOOTER); + + g_string_free(submodules, TRUE); +} + +static char **module_prefixes_get(void) +{ + GSList *tmp; + char **list, *name; + int count; + + list = g_new(char *, 3 + 3*g_slist_length(chat_protocols)); + list[0] = "fe"; + list[1] = "fe_common"; + + count = 2; + for (tmp = chat_protocols; tmp != NULL; tmp = tmp->next) { + CHAT_PROTOCOL_REC *rec = tmp->data; + + name = g_ascii_strdown(rec->name, -1); + + list[count++] = name; + list[count++] = g_strconcat("fe_", name, NULL); + list[count++] = g_strconcat("fe_common_", name, NULL); + } + list[count] = NULL; + + return list; +} + +static void module_prefixes_free(char **list) +{ + char **pos = list+2; + + while (*pos != NULL) { + g_free(*pos); + pos++; + } + g_free(list); +} + +/* SYNTAX: LOAD [-silent] <module> [<submodule>] */ +static void cmd_load(const char *data) +{ + char *rootmodule, *submodule; + char **module_prefixes; + void *free_arg; + gboolean silent; + GHashTable *optlist; + + g_return_if_fail(data != NULL); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS, "load", &optlist, &rootmodule, + &submodule)) + return; + + silent = g_hash_table_lookup(optlist, "silent") != NULL; + + if (*rootmodule == '\0') { + if (!silent) { + cmd_load_list(); + } + } else { + if (silent) { + signal_add_first("module error", (SIGNAL_FUNC) signal_stop); + signal_add_first("module loaded", (SIGNAL_FUNC) signal_stop); + } + + module_prefixes = module_prefixes_get(); + if (*submodule == '\0') + module_load(rootmodule, module_prefixes); + else { + module_load_sub(rootmodule, submodule, + module_prefixes); + } + module_prefixes_free(module_prefixes); + + if (silent) { + signal_remove("module error", (SIGNAL_FUNC) signal_stop); + signal_remove("module loaded", (SIGNAL_FUNC) signal_stop); + } + } + + cmd_params_free(free_arg); +} + +/* SYNTAX: UNLOAD <module> [<submodule>] */ +static void cmd_unload(const char *data) +{ + MODULE_REC *module; + MODULE_FILE_REC *file; + char *rootmodule, *submodule; + void *free_arg; + GSList *tmp; + int all_dynamic; + + g_return_if_fail(data != NULL); + + if (!cmd_get_params(data, &free_arg, 2 , &rootmodule, &submodule)) + return; + if (*rootmodule == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + module = module_find(rootmodule); + if (module != NULL) { + if (*submodule == '\0') { + all_dynamic = 1; + for (tmp = module->files; tmp != NULL; tmp = tmp->next) + all_dynamic &= !MODULE_IS_STATIC((MODULE_FILE_REC*) tmp->data); + if (all_dynamic) + module_unload(module); + } + else { + file = module_file_find(module, submodule); + if (file != NULL) { + if (!MODULE_IS_STATIC(file)) + module_file_unload(file); + } + else + module = NULL; + } + } + + if (module == NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + TXT_MODULE_NOT_LOADED, rootmodule, submodule); + } + + cmd_params_free(free_arg); +} + +void fe_modules_init(void) +{ + signal_add("module error", (SIGNAL_FUNC) sig_module_error); + signal_add("module loaded", (SIGNAL_FUNC) sig_module_loaded); + signal_add("module unloaded", (SIGNAL_FUNC) sig_module_unloaded); + + command_bind("load", NULL, (SIGNAL_FUNC) cmd_load); + command_bind("unload", NULL, (SIGNAL_FUNC) cmd_unload); + command_set_options("load", "silent"); +} + +void fe_modules_deinit(void) +{ + signal_remove("module error", (SIGNAL_FUNC) sig_module_error); + signal_remove("module loaded", (SIGNAL_FUNC) sig_module_loaded); + signal_remove("module unloaded", (SIGNAL_FUNC) sig_module_unloaded); + + command_unbind("load", (SIGNAL_FUNC) cmd_load); + command_unbind("unload", (SIGNAL_FUNC) cmd_unload); +} + +#else /* !HAVE_GMODULE */ + +static void cmd_load(const char *data) +{ + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, + "Dynamic modules loading not supported"); +} + +void fe_modules_init(void) +{ + command_bind("load", NULL, (SIGNAL_FUNC) cmd_load); +} + +void fe_modules_deinit(void) +{ + command_unbind("load", (SIGNAL_FUNC) cmd_load); +} +#endif diff --git a/src/fe-common/core/fe-queries.c b/src/fe-common/core/fe-queries.c new file mode 100644 index 0000000..77f0747 --- /dev/null +++ b/src/fe-common/core/fe-queries.c @@ -0,0 +1,399 @@ +/* + fe-queries.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/core/modules.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/core/chat-protocols.h> +#include <irssi/src/core/servers.h> +#include <irssi/src/core/queries.h> + +#include <irssi/src/fe-common/core/fe-core-commands.h> +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/core/window-items.h> +#include <irssi/src/fe-common/core/printtext.h> + +static int queryclose_tag, query_auto_close, querycreate_level; + +/* Return query where to put the private message. */ +QUERY_REC *privmsg_get_query(SERVER_REC *server, const char *nick, + int own, int level) +{ + QUERY_REC *query; + + g_return_val_if_fail(IS_SERVER(server), NULL); + g_return_val_if_fail(nick != NULL, NULL); + + query = query_find(server, nick); + if (query == NULL && !command_hide_output && + (querycreate_level & level) != 0 && + (!own || settings_get_bool("autocreate_own_query"))) { + query = CHAT_PROTOCOL(server)-> + query_create(server->tag, nick, TRUE); + } + + return query; +} + +static void signal_query_created(QUERY_REC *query, gpointer automatic) +{ + TEXT_DEST_REC dest; + + g_return_if_fail(IS_QUERY(query)); + + if (window_item_window(query) == NULL) { + window_item_create((WI_ITEM_REC *) query, + GPOINTER_TO_INT(automatic)); + } + + format_create_dest_tag(&dest, query->server, query->server_tag, + query->name, MSGLEVEL_CLIENTNOTICE, NULL); + printformat_dest(&dest, TXT_QUERY_START, + query->name, query->server_tag); +} + +static void signal_query_created_curwin(QUERY_REC *query) +{ + g_return_if_fail(IS_QUERY(query)); + + window_item_add(active_win, (WI_ITEM_REC *) query, FALSE); +} + +static void signal_query_destroyed(QUERY_REC *query) +{ + WINDOW_REC *window; + TEXT_DEST_REC dest; + + g_return_if_fail(IS_QUERY(query)); + + window = window_item_window((WI_ITEM_REC *) query); + if (window == NULL) + return; + + format_create_dest_tag(&dest, query->server, query->server_tag, + query->name, MSGLEVEL_CLIENTNOTICE, NULL); + printformat_dest(&dest, TXT_QUERY_STOP, query->name); + + window_item_destroy((WI_ITEM_REC *) query); + + if (!query->unwanted) + window_auto_destroy(window); + else { + /* eg. connection lost to dcc chat */ + window_bind_add(window, query->server_tag, query->name); + } +} + +static void signal_query_server_changed(QUERY_REC *query) +{ + WINDOW_REC *window; + + g_return_if_fail(query != NULL); + + window = window_item_window((WI_ITEM_REC *) query); + if (window->active == (WI_ITEM_REC *) query) + window_change_server(window, query->server); +} + +static void signal_query_nick_changed(QUERY_REC *query, const char *oldnick) +{ + TEXT_DEST_REC dest; + + g_return_if_fail(query != NULL); + + format_create_dest_tag(&dest, query->server, query->server_tag, + query->name, MSGLEVEL_NICKS, NULL); + + /* don't print the nick change message if only the case was changed */ + if (g_ascii_strcasecmp(query->name, oldnick) != 0) { + printformat_dest(&dest, TXT_NICK_CHANGED, oldnick, + query->name, query->name, + query->address == NULL ? "" : query->address); + } + + signal_emit("window item changed", 2, + window_item_window((WI_ITEM_REC *) query), query); +} + +static void signal_window_item_server_changed(WINDOW_REC *window, + QUERY_REC *query) +{ + if (IS_QUERY(query)) { + g_free_and_null(query->server_tag); + if (query->server != NULL) + query->server_tag = g_strdup(query->server->tag); + } +} + +static void sig_server_connected(SERVER_REC *server) +{ + GSList *tmp; + + if (!IS_SERVER(server)) + return; + + /* check if there's any queries without server */ + for (tmp = queries; tmp != NULL; tmp = tmp->next) { + QUERY_REC *rec = tmp->data; + + if (rec->server == NULL && + (rec->server_tag == NULL || + g_ascii_strcasecmp(rec->server_tag, server->tag) == 0)) { + window_item_change_server((WI_ITEM_REC *) rec, server); + server->queries = g_slist_append(server->queries, rec); + } + } +} + +static void cmd_window_server(const char *data) +{ + SERVER_REC *server; + QUERY_REC *query; + TEXT_DEST_REC dest; + + g_return_if_fail(data != NULL); + + server = server_find_tag(data); + query = QUERY(active_win->active); + if (server == NULL || query == NULL) + return; + + /* /WINDOW SERVER used in a query window */ + format_create_dest_tag(&dest, query->server, query->server_tag, + query->name, MSGLEVEL_CLIENTNOTICE, NULL); + printformat_dest(&dest, TXT_QUERY_SERVER_CHANGED, + query->name, server->tag); + + query_change_server(query, server); + signal_stop(); +} + +/* SYNTAX: UNQUERY [<nick>] */ +static void cmd_unquery(const char *data, SERVER_REC *server, WI_ITEM_REC *item) +{ + QUERY_REC *query; + char *nick; + void *free_arg; + + g_return_if_fail(data != NULL); + + if (!cmd_get_params(data, &free_arg, 1, &nick)) + return; + + if (*nick == '\0') { + /* remove current query */ + query = QUERY(item); + } else { + query = query_find(server, nick); + if (query == NULL) { + printformat(server, NULL, MSGLEVEL_CLIENTERROR, + TXT_NO_QUERY, nick); + } + } + + if (query != NULL) + query_destroy(query); + + cmd_params_free(free_arg); +} + +/* SYNTAX: QUERY [-window] [-<server tag>] <nick> [<message>] */ +static void cmd_query(const char *data, SERVER_REC *server, WI_ITEM_REC *item) +{ + GHashTable *optlist; + QUERY_REC *query; + char *nick, *msg; + void *free_arg; + + g_return_if_fail(data != NULL); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | + PARAM_FLAG_OPTIONS | PARAM_FLAG_UNKNOWN_OPTIONS, + "query", &optlist, &nick, &msg)) + return; + + if (*nick == '\0') { + /* remove current query */ + cmd_unquery("", server, item); + cmd_params_free(free_arg); + return; + } + + server = cmd_options_get_server("query", optlist, server); + if (server == NULL) { + cmd_params_free(free_arg); + return; + } + + if (*nick != '=' && (server == NULL || !server->connected)) + cmd_param_error(CMDERR_NOT_CONNECTED); + + if (g_hash_table_lookup(optlist, "window") != NULL) { + signal_add("query created", + (SIGNAL_FUNC) signal_query_created_curwin); + } + + query = query_find(server, nick); + if (query == NULL) + query = CHAT_PROTOCOL(server)-> + query_create(server->tag, nick, FALSE); + else { + /* query already exists, set it active */ + WINDOW_REC *window = window_item_window(query); + + if (window != active_win) + window_set_active(window); + window_item_set_active(active_win, (WI_ITEM_REC *) query); + } + + if (g_hash_table_lookup(optlist, "window") != NULL) { + signal_remove("query created", + (SIGNAL_FUNC) signal_query_created_curwin); + } + + if (*msg != '\0') { + msg = g_strdup_printf("-nick %s %s", nick, msg); + signal_emit("command msg", 3, msg, server, query); + g_free(msg); + } + + cmd_params_free(free_arg); +} + +static void window_reset_query_timestamps(WINDOW_REC *window) +{ + GSList *tmp; + + if (window == NULL) + return; + + for (tmp = window->items; tmp != NULL; tmp = tmp->next) { + QUERY_REC *query = QUERY(tmp->data); + + if (query != NULL) + query->last_unread_msg = time(NULL); + } +} + +static void sig_window_changed(WINDOW_REC *window, WINDOW_REC *old_window) +{ + /* reset the queries last_unread_msg so query doesn't get closed + immediately after switched to the window, or after changed to + some other window from it */ + window_reset_query_timestamps(window); + window_reset_query_timestamps(old_window); +} + +static int sig_query_autoclose(void) +{ + WINDOW_REC *window; + GSList *tmp, *next; + time_t now; + + now = time(NULL); + for (tmp = queries; tmp != NULL; tmp = next) { + QUERY_REC *rec = tmp->data; + + next = tmp->next; + window = window_item_window((WI_ITEM_REC *) rec); + if (window != active_win && rec->data_level < DATA_LEVEL_MSG && + now-rec->last_unread_msg > query_auto_close) + query_destroy(rec); + } + return 1; +} + +static void sig_message_private(SERVER_REC *server, const char *msg, + const char *nick, const char *address, const char *target) +{ + QUERY_REC *query; + + /* own message returned by bouncer? */ + int own = (!g_strcmp0(nick, server->nick)); + + /* create query window if needed */ + query = privmsg_get_query(server, own ? target : nick, FALSE, MSGLEVEL_MSGS); + + /* reset the query's last_unread_msg timestamp */ + if (query != NULL) + query->last_unread_msg = time(NULL); +} + +static void read_settings(void) +{ + querycreate_level = settings_get_level("autocreate_query_level"); + query_auto_close = settings_get_time("autoclose_query")/1000; + if (query_auto_close > 0 && queryclose_tag == -1) + queryclose_tag = g_timeout_add(5000, (GSourceFunc) sig_query_autoclose, NULL); + else if (query_auto_close <= 0 && queryclose_tag != -1) { + g_source_remove(queryclose_tag); + queryclose_tag = -1; + } +} + +void fe_queries_init(void) +{ + settings_add_level("lookandfeel", "autocreate_query_level", "MSGS DCCMSGS"); + settings_add_bool("lookandfeel", "autocreate_own_query", TRUE); + settings_add_time("lookandfeel", "autoclose_query", "0"); + + queryclose_tag = -1; + read_settings(); + + signal_add("query created", (SIGNAL_FUNC) signal_query_created); + signal_add("query destroyed", (SIGNAL_FUNC) signal_query_destroyed); + signal_add("query server changed", (SIGNAL_FUNC) signal_query_server_changed); + signal_add("query nick changed", (SIGNAL_FUNC) signal_query_nick_changed); + signal_add("window item server changed", (SIGNAL_FUNC) signal_window_item_server_changed); + signal_add("server connected", (SIGNAL_FUNC) sig_server_connected); + signal_add("window changed", (SIGNAL_FUNC) sig_window_changed); + signal_add_first("message private", (SIGNAL_FUNC) sig_message_private); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); + + command_bind("query", NULL, (SIGNAL_FUNC) cmd_query); + command_bind("unquery", NULL, (SIGNAL_FUNC) cmd_unquery); + command_bind("window server", NULL, (SIGNAL_FUNC) cmd_window_server); + + command_set_options("query", "window"); +} + +void fe_queries_deinit(void) +{ + if (queryclose_tag != -1) g_source_remove(queryclose_tag); + + signal_remove("query created", (SIGNAL_FUNC) signal_query_created); + signal_remove("query destroyed", (SIGNAL_FUNC) signal_query_destroyed); + signal_remove("query server changed", (SIGNAL_FUNC) signal_query_server_changed); + signal_remove("query nick changed", (SIGNAL_FUNC) signal_query_nick_changed); + signal_remove("window item server changed", (SIGNAL_FUNC) signal_window_item_server_changed); + signal_remove("server connected", (SIGNAL_FUNC) sig_server_connected); + signal_remove("window changed", (SIGNAL_FUNC) sig_window_changed); + signal_remove("message private", (SIGNAL_FUNC) sig_message_private); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); + + command_unbind("query", (SIGNAL_FUNC) cmd_query); + command_unbind("unquery", (SIGNAL_FUNC) cmd_unquery); + command_unbind("window server", (SIGNAL_FUNC) cmd_window_server); +} diff --git a/src/fe-common/core/fe-queries.h b/src/fe-common/core/fe-queries.h new file mode 100644 index 0000000..30c45e4 --- /dev/null +++ b/src/fe-common/core/fe-queries.h @@ -0,0 +1,13 @@ +#ifndef IRSSI_FE_COMMON_CORE_FE_QUERIES_H +#define IRSSI_FE_COMMON_CORE_FE_QUERIES_H + +#include <irssi/src/core/queries.h> + +/* Return query where to put the private message. */ +QUERY_REC *privmsg_get_query(SERVER_REC *server, const char *nick, + int own, int level); + +void fe_queries_init(void); +void fe_queries_deinit(void); + +#endif diff --git a/src/fe-common/core/fe-recode.c b/src/fe-common/core/fe-recode.c new file mode 100644 index 0000000..5e7c1e4 --- /dev/null +++ b/src/fe-common/core/fe-recode.c @@ -0,0 +1,208 @@ +/* + fe-recode.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/modules.h> +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/lib-config/iconfig.h> +#include <irssi/src/core/settings.h> +#include <irssi/src/fe-common/core/printtext.h> +#include <irssi/src/fe-common/core/formats.h> +#include <irssi/src/core/recode.h> + +static char *recode_fallback = NULL; +static char *recode_out_default = NULL; +static char *term_charset = NULL; + +static const char *fe_recode_get_target (WI_ITEM_REC *witem) +{ + if (witem && (witem->type == module_get_uniq_id_str("WINDOW ITEM TYPE", "QUERY") + || witem->type == module_get_uniq_id_str("WINDOW ITEM TYPE", "CHANNEL"))) + return window_item_get_target(witem); + + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_NOT_CHANNEL_OR_QUERY); + return NULL; +} + +static int fe_recode_compare_func (CONFIG_NODE *node1, CONFIG_NODE *node2) +{ + return g_strcmp0(node1->key, node2->key); +} + +/* SYNTAX: RECODE */ +static void fe_recode_cmd (const char *data, SERVER_REC *server, WI_ITEM_REC *witem) +{ + if (*data) + command_runsub("recode", data, server, witem); + else { + CONFIG_NODE *conversions; + GSList *tmp; + GSList *sorted = NULL; + + conversions = iconfig_node_traverse("conversions", FALSE); + + for (tmp = conversions ? config_node_first(conversions->value) : NULL; + tmp != NULL; + tmp = config_node_next(tmp)) { + CONFIG_NODE *node = tmp->data; + + if (node->type == NODE_TYPE_KEY) + sorted = g_slist_insert_sorted(sorted, node, (GCompareFunc) fe_recode_compare_func); + } + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_RECODE_HEADER); + for (tmp = sorted; tmp != NULL; tmp = tmp->next) { + CONFIG_NODE *node = tmp->data; + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_RECODE_LINE, node->key, node->value); + } + + g_slist_free(sorted); + } +} + +/* SYNTAX: RECODE ADD [[<tag>/]<target>] <charset> */ +static void fe_recode_add_cmd (const char *data, SERVER_REC *server, WI_ITEM_REC *witem) +{ + const char *first; + const char *second; + const char *target; + const char *charset; + void *free_arg; + + if (! cmd_get_params(data, &free_arg, 2, &first, &second)) + return; + + if (! *first) + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + if (*second) { + target = first; + charset = second; + } else { + target = fe_recode_get_target(witem); + charset = first; + if (! target) + goto end; + } + if (is_valid_charset(charset)) { + iconfig_set_str("conversions", target, charset); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CONVERSION_ADDED, target, charset); + } else + signal_emit("error command", 2, GINT_TO_POINTER(CMDERR_INVALID_CHARSET), charset); + end: + cmd_params_free(free_arg); +} + +/* SYNTAX: RECODE REMOVE [<target>] */ +static void fe_recode_remove_cmd (const char *data, SERVER_REC *server, WI_ITEM_REC *witem) +{ + const char *target; + void *free_arg; + + if (! cmd_get_params(data, &free_arg, 1, &target)) + return; + + if (! *target) { + target = fe_recode_get_target(witem); + if (! target) + goto end; + } + + if (iconfig_get_str("conversions", target, NULL) == NULL) + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CONVERSION_NOT_FOUND, target); + else { + iconfig_set_str("conversions", target, NULL); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CONVERSION_REMOVED, target); + } + + end: + cmd_params_free(free_arg); +} + +static void read_settings(void) +{ + /* preserve the valid values */ + char *old_term_charset = g_strdup(term_charset); + char *old_recode_fallback = g_strdup(recode_fallback); + char *old_recode_out_default = g_strdup(recode_out_default); + + if (settings_get_bool("recode_transliterate")) { + /* check if transliterations are supported in this system */ + if (!is_valid_charset("ASCII")) { + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + TXT_CONVERSION_NO_TRANSLITS); + settings_set_bool("recode_transliterate", FALSE); + } + } + + if (recode_fallback) + g_free(recode_fallback); + recode_fallback = g_strdup(settings_get_str("recode_fallback")); + if (!is_valid_charset(recode_fallback)) { + signal_emit("error command", 2, GINT_TO_POINTER(CMDERR_INVALID_CHARSET), recode_fallback); + g_free(recode_fallback); + recode_fallback = is_valid_charset(old_recode_fallback) ? g_strdup(old_recode_fallback) : NULL; + settings_set_str("recode_fallback", recode_fallback); + } + + if (term_charset) + g_free(term_charset); + term_charset = g_strdup(settings_get_str("term_charset")); + if (!is_valid_charset(term_charset)) { + g_free(term_charset); + term_charset = is_valid_charset(old_term_charset) ? g_strdup(old_term_charset) : NULL; + settings_set_str("term_charset", term_charset); + } + recode_update_charset(); + + if (recode_out_default) + g_free(recode_out_default); + recode_out_default = g_strdup(settings_get_str("recode_out_default_charset")); + if (recode_out_default != NULL && *recode_out_default != '\0' && + !is_valid_charset(recode_out_default)) { + signal_emit("error command", 2, GINT_TO_POINTER(CMDERR_INVALID_CHARSET), recode_out_default); + g_free(recode_out_default); + recode_out_default = is_valid_charset(old_recode_out_default) ? g_strdup(old_recode_out_default) : NULL; + settings_set_str("recode_out_default_charset", recode_out_default); + } + + g_free(old_term_charset); + g_free(old_recode_fallback); + g_free(old_recode_out_default); +} + +void fe_recode_init (void) +{ + command_bind("recode", NULL, (SIGNAL_FUNC) fe_recode_cmd); + command_bind("recode add", NULL, (SIGNAL_FUNC) fe_recode_add_cmd); + command_bind("recode remove", NULL, (SIGNAL_FUNC) fe_recode_remove_cmd); + signal_add_first("setup changed", (SIGNAL_FUNC) read_settings); + read_settings(); +} + +void fe_recode_deinit (void) +{ + command_unbind("recode", (SIGNAL_FUNC) fe_recode_cmd); + command_unbind("recode add", (SIGNAL_FUNC) fe_recode_add_cmd); + command_unbind("recode remove", (SIGNAL_FUNC) fe_recode_remove_cmd); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); +} diff --git a/src/fe-common/core/fe-recode.h b/src/fe-common/core/fe-recode.h new file mode 100644 index 0000000..50775f2 --- /dev/null +++ b/src/fe-common/core/fe-recode.h @@ -0,0 +1,7 @@ +#ifndef IRSSI_FE_COMMON_CORE_FE_RECODE_H +#define IRSSI_FE_COMMON_CORE_FE_RECODE_H + +void fe_recode_init (void); +void fe_recode_deinit (void); + +#endif /* IRSSI_FE_COMMON_CORE_FE_RECODE_H */ diff --git a/src/fe-common/core/fe-server.c b/src/fe-common/core/fe-server.c new file mode 100644 index 0000000..ab76c36 --- /dev/null +++ b/src/fe-common/core/fe-server.c @@ -0,0 +1,548 @@ +/* + fe-server.c : irssi + + Copyright (C) 1999-2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/network.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/core/chat-protocols.h> +#include <irssi/src/core/chatnets.h> +#include <irssi/src/core/servers.h> +#include <irssi/src/core/servers-setup.h> +#include <irssi/src/core/servers-reconnect.h> + +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/fe-common/core/printtext.h> + +static void print_servers(void) +{ + GSList *tmp; + + for (tmp = servers; tmp != NULL; tmp = tmp->next) { + SERVER_REC *rec = tmp->data; + + printformat(NULL, NULL, MSGLEVEL_CRAP, TXT_SERVER_LIST, + rec->tag, rec->connrec->address, rec->connrec->port, + rec->connrec->chatnet == NULL ? "" : rec->connrec->chatnet, rec->connrec->nick); + } +} + +static void print_lookup_servers(void) +{ + GSList *tmp; + for (tmp = lookup_servers; tmp != NULL; tmp = tmp->next) { + SERVER_REC *rec = tmp->data; + + printformat(NULL, NULL, MSGLEVEL_CRAP, TXT_SERVER_LOOKUP_LIST, + rec->tag, rec->connrec->address, rec->connrec->port, + rec->connrec->chatnet == NULL ? "" : rec->connrec->chatnet, rec->connrec->nick); + } +} + +static void print_reconnects(void) +{ + GSList *tmp; + char *tag, *next_connect; + int left; + + for (tmp = reconnects; tmp != NULL; tmp = tmp->next) { + RECONNECT_REC *rec = tmp->data; + SERVER_CONNECT_REC *conn = rec->conn; + + tag = g_strdup_printf("RECON-%d", rec->tag); + left = rec->next_connect-time(NULL); + next_connect = g_strdup_printf("%02d:%02d", left/60, left%60); + printformat(NULL, NULL, MSGLEVEL_CRAP, TXT_SERVER_RECONNECT_LIST, + tag, conn->address, conn->port, + conn->chatnet == NULL ? "" : conn->chatnet, + conn->nick, next_connect); + g_free(next_connect); + g_free(tag); + } +} + +static SERVER_SETUP_REC *create_server_setup(GHashTable *optlist) +{ + CHAT_PROTOCOL_REC *rec; + SERVER_SETUP_REC *server; + char *chatnet; + + rec = chat_protocol_find_net(optlist); + if (rec == NULL) + rec = chat_protocol_get_default(); + else { + chatnet = g_hash_table_lookup(optlist, "network"); + if (chatnet == NULL && g_hash_table_lookup(optlist, rec->chatnet) != NULL) + chatnet = rec->chatnet; + if (chatnet_find(chatnet) == NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_UNKNOWN_CHATNET, chatnet); + return NULL; + } + } + + server = rec->create_server_setup(); + server->chat_type = rec->id; + server->tls_verify = TRUE; + return server; +} + +static void cmd_server_add_modify(const char *data, gboolean add) +{ + GHashTable *optlist; + SERVER_SETUP_REC *rec, *tmp; + char *addr, *portstr, *password, *value, *chatnet, *old_chatnet; + void *free_arg; + gboolean newrec; + int port, old_port, add_port; + + if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS, + "server add", &optlist, &addr, &portstr, &password)) + return; + + if (*addr == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + port = old_port = -1; + + value = g_hash_table_lookup(optlist, "port"); + if (value != NULL && *value != '\0') + port = add_port = atoi(value); + else if (g_hash_table_lookup(optlist, "tls") || + g_hash_table_lookup(optlist, "ssl")) + add_port = DEFAULT_SERVER_ADD_TLS_PORT; + else + add_port = DEFAULT_SERVER_ADD_PORT; + + if (*portstr != '\0') + old_port = atoi(portstr); + + chatnet = g_hash_table_lookup(optlist, "network"); + + rec = server_setup_find(addr, old_port != -1 ? old_port : add_port, chatnet); + if (old_port == -1 && rec != NULL) + old_port = rec->port; + + if (port == -1) + port = old_port != -1 ? old_port : add_port; + + /* make sure the new port doesn't exist */ + tmp = server_setup_find(addr, port, chatnet); + if (tmp != NULL && tmp->port == port) + rec = tmp; + + if (rec == NULL || (rec->port != old_port && rec->port != port)) { + newrec = TRUE; + if (add == FALSE) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_SETUPSERVER_NOT_FOUND, + addr, old_port == -1 ? port : old_port); + cmd_params_free(free_arg); + return; + } + + rec = create_server_setup(optlist); + if (rec == NULL) { + cmd_params_free(free_arg); + return; + } + rec->address = g_strdup(addr); + rec->port = port; + } else { + newrec = FALSE; + old_chatnet = g_strdup(rec->chatnet); + old_port = rec->port; + rec->port = port; + + if (*password != '\0') g_free_and_null(rec->password); + if (g_hash_table_lookup(optlist, "host")) { + g_free_and_null(rec->own_host); + rec->own_ip4 = rec->own_ip6 = NULL; + } + } + + if (g_hash_table_lookup(optlist, "6")) + rec->family = AF_INET6; + else if (g_hash_table_lookup(optlist, "4")) + rec->family = AF_INET; + + value = g_hash_table_lookup(optlist, "tls_cert"); + if (value == NULL) + value = g_hash_table_lookup(optlist, "ssl_cert"); + if (value != NULL && *value != '\0') { + rec->tls_cert = g_strdup(value); + if (newrec) { + /* convenience and backward compatibility, turn on tls if tls_cert is given + */ + rec->use_tls = TRUE; + } + } + + value = g_hash_table_lookup(optlist, "tls_pkey"); + if (value == NULL) + value = g_hash_table_lookup(optlist, "ssl_pkey"); + if (value != NULL && *value != '\0') + rec->tls_pkey = g_strdup(value); + + value = g_hash_table_lookup(optlist, "tls_pass"); + if (value == NULL) + value = g_hash_table_lookup(optlist, "ssl_pass"); + if (value != NULL && *value != '\0') + rec->tls_pass = g_strdup(value); + + value = g_hash_table_lookup(optlist, "tls_cafile"); + if (value == NULL) + value = g_hash_table_lookup(optlist, "ssl_cafile"); + if (value != NULL && *value != '\0') + rec->tls_cafile = g_strdup(value); + else if (value != NULL && *value == '\0') + g_free_and_null(rec->tls_cafile); + + value = g_hash_table_lookup(optlist, "tls_capath"); + if (value == NULL) + value = g_hash_table_lookup(optlist, "ssl_capath"); + if (value != NULL && *value != '\0') + rec->tls_capath = g_strdup(value); + else if (value != NULL && *value == '\0') + g_free_and_null(rec->tls_capath); + + value = g_hash_table_lookup(optlist, "tls_ciphers"); + if (value == NULL) + value = g_hash_table_lookup(optlist, "ssl_ciphers"); + if (value != NULL && *value != '\0') + rec->tls_ciphers = g_strdup(value); + + value = g_hash_table_lookup(optlist, "tls_pinned_cert"); + if (value == NULL) + value = g_hash_table_lookup(optlist, "ssl_pinned_cert"); + if (value != NULL && *value != '\0') + rec->tls_pinned_cert = g_strdup(value); + + value = g_hash_table_lookup(optlist, "tls_pinned_pubkey"); + if (value == NULL) + value = g_hash_table_lookup(optlist, "ssl_pinned_pubkey"); + if (value != NULL && *value != '\0') + rec->tls_pinned_pubkey = g_strdup(value); + + if ((rec->tls_cafile != NULL && rec->tls_cafile[0] != '\0') + || (rec->tls_capath != NULL && rec->tls_capath[0] != '\0')) + rec->tls_verify = TRUE; + + if (g_hash_table_lookup(optlist, "tls_verify") || + g_hash_table_lookup(optlist, "ssl_verify")) { + rec->tls_verify = TRUE; + if (newrec) { + /* convenience and backward compatibility, turn on tls if tls_verify is + * given */ + rec->use_tls = TRUE; + } + } else if (g_hash_table_lookup(optlist, "notls_verify") || + g_hash_table_lookup(optlist, "nossl_verify")) { + rec->tls_verify = FALSE; + } + + if (g_hash_table_lookup(optlist, "tls") || g_hash_table_lookup(optlist, "ssl")) + rec->use_tls = TRUE; + else if (g_hash_table_lookup(optlist, "notls") || g_hash_table_lookup(optlist, "nossl")) + rec->use_tls = FALSE; + + if (g_hash_table_lookup(optlist, "auto")) rec->autoconnect = TRUE; + if (g_hash_table_lookup(optlist, "noauto")) rec->autoconnect = FALSE; + if (g_hash_table_lookup(optlist, "proxy")) rec->no_proxy = FALSE; + if (g_hash_table_lookup(optlist, "noproxy")) rec->no_proxy = TRUE; + + if (*password != '\0' && g_strcmp0(password, "-") != 0) rec->password = g_strdup(password); + value = g_hash_table_lookup(optlist, "host"); + if (value != NULL && *value != '\0') { + rec->own_host = g_strdup(value); + rec->own_ip4 = rec->own_ip6 = NULL; + } + + signal_emit("server add fill", 3, rec, optlist, GINT_TO_POINTER(add)); + + if (newrec) { + server_setup_add(rec); + } else { + server_setup_modify(rec, old_port, old_chatnet); + g_free(old_chatnet); + } + + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_SETUPSERVER_ADDED, addr, port); + + cmd_params_free(free_arg); +} + +static void cmd_server_add(const char *data) +{ + cmd_server_add_modify(data, TRUE); +} + +static void cmd_server_modify(const char *data) +{ + cmd_server_add_modify(data, FALSE); +} + +/* SYNTAX: SERVER REMOVE <address> [<port>] [<network>] */ +static void cmd_server_remove(const char *data) +{ + SERVER_SETUP_REC *rec; + char *addr, *port, *chatnet; + void *free_arg; + int portnum; + + if (!cmd_get_params(data, &free_arg, 3, &addr, &port, &chatnet)) + return; + if (*addr == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + if (*port == '\0') { + portnum = DEFAULT_SERVER_ADD_PORT; + if (*chatnet == '\0') + rec = server_setup_find(addr, -1, NULL); + else + rec = server_setup_find(addr, -1, chatnet); + } + else + { + portnum = atoi(port); + if (*chatnet == '\0') + rec = server_setup_find(addr, portnum, NULL); + else + rec = server_setup_find(addr, portnum, chatnet); + } + + if (rec == NULL) + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_SETUPSERVER_NOT_FOUND, addr, + portnum); + else { + portnum = rec->port; + server_setup_remove(rec); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_SETUPSERVER_REMOVED, addr, + portnum); + } + + cmd_params_free(free_arg); +} + +static void cmd_server(const char *data) +{ + if (*data != '\0') + return; + + if (servers == NULL && lookup_servers == NULL && + reconnects == NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_NO_CONNECTED_SERVERS); + } else { + print_servers(); + print_lookup_servers(); + print_reconnects(); + } + + signal_stop(); +} + +static void cmd_server_connect(const char *data) +{ + GHashTable *optlist; + char *addr; + void *free_arg; + + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS, + "connect", &optlist, &addr)) + return; + + if (*addr == '\0' || g_strcmp0(addr, "+") == 0) + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + if (*addr == '+') window_create(NULL, FALSE); + + cmd_params_free(free_arg); +} + +static void server_command(const char *data, SERVER_REC *server, + WI_ITEM_REC *item) +{ + if (server == NULL) { + /* this command accepts non-connected server too */ + server = active_win->connect_server; + } + + signal_continue(3, data, server, item); +} + +static void sig_server_looking(SERVER_REC *server) +{ + g_return_if_fail(server != NULL); + + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOOKING_UP, server->connrec->address); +} + +static void sig_server_connecting(SERVER_REC *server, IPADDR *ip) +{ + char ipaddr[MAX_IP_LEN]; + + g_return_if_fail(server != NULL); + + if (ip == NULL) + ipaddr[0] = '\0'; + else + net_ip2host(ip, ipaddr); + + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, + !server->connrec->reconnecting ? + TXT_CONNECTING : TXT_RECONNECTING, + server->connrec->address, ipaddr, server->connrec->port); +} + +static void sig_server_connected(SERVER_REC *server) +{ + g_return_if_fail(server != NULL); + + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_CONNECTION_ESTABLISHED, server->connrec->address); +} + +static void sig_connect_failed(SERVER_REC *server, gchar *msg) +{ + g_return_if_fail(server != NULL); + + if (msg == NULL) { + /* no message so this wasn't unexpected fail - send + connection_lost message instead */ + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_CONNECTION_LOST, server->connrec->address); + } else { + printformat(server, NULL, MSGLEVEL_CLIENTERROR, + TXT_CANT_CONNECT, server->connrec->address, server->connrec->port, msg); + } +} + +static void sig_server_disconnected(SERVER_REC *server) +{ + g_return_if_fail(server != NULL); + + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_CONNECTION_LOST, server->connrec->address); +} + +static void sig_server_quit(SERVER_REC *server, const char *msg) +{ + g_return_if_fail(server != NULL); + + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_SERVER_QUIT, server->connrec->address, msg); +} + +static void sig_server_lag_disconnected(SERVER_REC *server) +{ + g_return_if_fail(server != NULL); + + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_LAG_DISCONNECTED, server->connrec->address, + time(NULL)-(server->lag_sent / G_TIME_SPAN_SECOND)); +} + +static void sig_server_reconnect_removed(RECONNECT_REC *reconnect) +{ + g_return_if_fail(reconnect != NULL); + + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_RECONNECT_REMOVED, reconnect->conn->address, reconnect->conn->port, + reconnect->conn->chatnet == NULL ? "" : reconnect->conn->chatnet); +} + +static void sig_server_reconnect_not_found(const char *tag) +{ + g_return_if_fail(tag != NULL); + + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_RECONNECT_NOT_FOUND, tag); +} + +static void sig_chat_protocol_unknown(const char *protocol) +{ + g_return_if_fail(protocol != NULL); + + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + TXT_UNKNOWN_CHAT_PROTOCOL, protocol); +} + +void fe_server_init(void) +{ + command_bind("server", NULL, (SIGNAL_FUNC) cmd_server); + command_bind("server connect", NULL, (SIGNAL_FUNC) cmd_server_connect); + command_bind("server add", NULL, (SIGNAL_FUNC) cmd_server_add); + command_bind("server modify", NULL, (SIGNAL_FUNC) cmd_server_modify); + command_bind("server remove", NULL, (SIGNAL_FUNC) cmd_server_remove); + command_bind_first("server", NULL, (SIGNAL_FUNC) server_command); + command_bind_first("disconnect", NULL, (SIGNAL_FUNC) server_command); + + command_set_options( + "server add", "4 6 !! ~ssl ~nossl ~+ssl_cert ~+ssl_pkey ~+ssl_pass ~ssl_verify " + "~nossl_verify ~+ssl_cafile ~+ssl_capath ~+ssl_ciphers ~+ssl_fingerprint " + "tls notls +tls_cert +tls_pkey +tls_pass tls_verify notls_verify " + "+tls_cafile +tls_capath +tls_ciphers +tls_pinned_cert " + "+tls_pinned_pubkey auto noauto proxy noproxy -host -port noautosendcmd"); + command_set_options( + "server modify", + "4 6 !! ~ssl ~nossl ~+ssl_cert ~+ssl_pkey ~+ssl_pass ~ssl_verify ~nossl_verify " + "~+ssl_cafile ~+ssl_capath ~+ssl_ciphers ~+ssl_fingerprint tls notls +tls_cert " + "+tls_pkey +tls_pass tls_verify notls_verify +tls_cafile +tls_capath +tls_ciphers " + "+tls_pinned_cert +tls_pinned_pubkey auto noauto proxy noproxy -host -port " + "noautosendcmd"); + + signal_add("server looking", (SIGNAL_FUNC) sig_server_looking); + signal_add("server connecting", (SIGNAL_FUNC) sig_server_connecting); + signal_add("server connected", (SIGNAL_FUNC) sig_server_connected); + signal_add("server connect failed", (SIGNAL_FUNC) sig_connect_failed); + signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected); + signal_add("server quit", (SIGNAL_FUNC) sig_server_quit); + + signal_add("server lag disconnect", (SIGNAL_FUNC) sig_server_lag_disconnected); + signal_add("server reconnect remove", (SIGNAL_FUNC) sig_server_reconnect_removed); + signal_add("server reconnect not found", (SIGNAL_FUNC) sig_server_reconnect_not_found); + + signal_add("chat protocol unknown", (SIGNAL_FUNC) sig_chat_protocol_unknown); +} + +void fe_server_deinit(void) +{ + command_unbind("server", (SIGNAL_FUNC) cmd_server); + command_unbind("server connect", (SIGNAL_FUNC) cmd_server_connect); + command_unbind("server add", (SIGNAL_FUNC) cmd_server_add); + command_unbind("server modify", (SIGNAL_FUNC) cmd_server_modify); + command_unbind("server remove", (SIGNAL_FUNC) cmd_server_remove); + command_unbind("server", (SIGNAL_FUNC) server_command); + command_unbind("disconnect", (SIGNAL_FUNC) server_command); + + signal_remove("server looking", (SIGNAL_FUNC) sig_server_looking); + signal_remove("server connecting", (SIGNAL_FUNC) sig_server_connecting); + signal_remove("server connected", (SIGNAL_FUNC) sig_server_connected); + signal_remove("server connect failed", (SIGNAL_FUNC) sig_connect_failed); + signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected); + signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit); + + signal_remove("server lag disconnect", (SIGNAL_FUNC) sig_server_lag_disconnected); + signal_remove("server reconnect remove", (SIGNAL_FUNC) sig_server_reconnect_removed); + signal_remove("server reconnect not found", (SIGNAL_FUNC) sig_server_reconnect_not_found); + + signal_remove("chat protocol unknown", (SIGNAL_FUNC) sig_chat_protocol_unknown); +} diff --git a/src/fe-common/core/fe-settings.c b/src/fe-common/core/fe-settings.c new file mode 100644 index 0000000..f686916 --- /dev/null +++ b/src/fe-common/core/fe-settings.c @@ -0,0 +1,452 @@ +/* + fe-settings.c : irssi + + Copyright (C) 1999 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/servers.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/lib-config/iconfig.h> +#include <irssi/src/core/settings.h> +#include <irssi/src/fe-common/core/fe-settings.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/fe-common/core/printtext.h> +#include <irssi/src/fe-common/core/keyboard.h> + +static void set_print(SETTINGS_REC *rec) +{ + char *value; + + value = settings_get_print(rec); + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_SET_ITEM, + rec->key, value); + g_free(value); +} + +void fe_settings_set_print(const char *key) +{ + set_print(settings_get_record(key)); +} + +static void set_print_pattern(const char *pattern) +{ + GSList *sets, *tmp; + const char *last_section; + + last_section = ""; + sets = settings_get_sorted(); + for (tmp = sets; tmp != NULL; tmp = tmp->next) { + SETTINGS_REC *rec = tmp->data; + + if (stristr(rec->key, pattern) == NULL) + continue; + if (g_strcmp0(last_section, rec->section) != 0) { + /* print section */ + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, + TXT_SET_TITLE, rec->section); + last_section = rec->section; + } + set_print(rec); + } + g_slist_free(sets); +} + +static void set_print_section(const char *pattern) +{ + GSList *sets, *tmp; + const char *last_section; + + last_section = ""; + sets = settings_get_sorted(); + for (tmp = sets; tmp != NULL; tmp = tmp->next) { + SETTINGS_REC *rec = tmp->data; + + if (stristr(rec->section, pattern) != NULL) { + if (g_strcmp0(last_section, rec->section) != 0) { + /* print section */ + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, + TXT_SET_TITLE, rec->section); + last_section = rec->section; + } + set_print(rec); + } + } + g_slist_free(sets); +} + +static void set_boolean(const char *key, const char *value) +{ + char *stripped_value; + + stripped_value = g_strdup(value); + g_strstrip(stripped_value); + + if (g_ascii_strcasecmp(stripped_value, "ON") == 0) + settings_set_bool(key, TRUE); + else if (g_ascii_strcasecmp(stripped_value, "OFF") == 0) + settings_set_bool(key, FALSE); + else if (g_ascii_strcasecmp(stripped_value, "TOGGLE") == 0) + settings_set_bool(key, !settings_get_bool(key)); + else + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_NOT_TOGGLE); + + g_free(stripped_value); +} + +static void set_int(const char *key, const char *value) +{ + char *endp; + long longval; + int error; + + errno = 0; + longval = strtol(value, &endp, 10); + error = errno; + while (i_isspace(*endp)) + endp++; + if (error != 0 || *endp != '\0' || longval < INT_MIN || longval > INT_MAX) + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_INVALID_NUMBER); + else + settings_set_int(key, (int)longval); +} + +static void set_choice(const char *key, const char *value) +{ + char *stripped_value; + + stripped_value = g_strdup(value); + g_strstrip(stripped_value); + + if (settings_set_choice(key, stripped_value) == FALSE) { + SETTINGS_REC *rec = settings_get_record(key); + char *msg = g_strjoinv(", ", rec->choices); + + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_INVALID_CHOICE, msg); + g_free(msg); + } + + g_free(stripped_value); +} + +/* SYNTAX: SET [-clear | -default | -section] [<key> [<value>]] */ +static void cmd_set(char *data) +{ + GHashTable *optlist; + char *key, *value; + void *free_arg; + int clear, set_default, list_section; + SETTINGS_REC *rec; + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | + PARAM_FLAG_OPTIONS, + "set", &optlist, &key, &value)) + return; + + clear = g_hash_table_lookup(optlist, "clear") != NULL; + set_default = g_hash_table_lookup(optlist, "default") != NULL; + list_section = g_hash_table_lookup(optlist, "section") != NULL; + + if (*key == '\0') + clear = set_default = list_section = FALSE; + + if (list_section) + set_print_section(key); + else if (!(clear || set_default || *value != '\0')) + set_print_pattern(key); + else { + rec = settings_get_record(key); + if (rec != NULL) { + /* change the setting */ + switch (rec->type) { + case SETTING_TYPE_BOOLEAN: + if (clear) + settings_set_bool(key, FALSE); + else if (set_default) + settings_set_bool(key, rec->default_value.v_bool); + else + set_boolean(key, value); + break; + case SETTING_TYPE_INT: + if (clear) + settings_set_int(key, 0); + else if (set_default) + settings_set_int(key, rec->default_value.v_int); + else + set_int(key, value); + break; + case SETTING_TYPE_CHOICE: + if (clear || set_default) + settings_set_choice(key, rec->choices[rec->default_value.v_int]); + else + set_choice(key, value); + break; + case SETTING_TYPE_STRING: + settings_set_str(key, clear ? "" : + set_default ? rec->default_value.v_string : + value); + break; + case SETTING_TYPE_TIME: + if (!settings_set_time(key, clear ? "0" : + set_default ? rec->default_value.v_string : value)) + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_INVALID_TIME); + break; + case SETTING_TYPE_LEVEL: + if (!settings_set_level(key, clear ? "" : + set_default ? rec->default_value.v_string : value)) + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_INVALID_LEVEL); + break; + case SETTING_TYPE_SIZE: + if (!settings_set_size(key, clear ? "0" : + set_default ? rec->default_value.v_string : value)) + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_INVALID_SIZE); + break; + case SETTING_TYPE_ANY: + /* Unpossible! */ + break; + } + signal_emit("setup changed", 0); + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_SET_TITLE, rec->section); + set_print(rec); + } else + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_SET_UNKNOWN, key); + } + + cmd_params_free(free_arg); +} + +/* SYNTAX: TOGGLE <key> [on|off|toggle] */ +static void cmd_toggle(const char *data) +{ + char *key, *value; + void *free_arg; + int type; + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS, &key, &value)) + return; + + if (*key == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + type = settings_get_type(key); + if (type == SETTING_TYPE_ANY) + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_SET_UNKNOWN, key); + else if (type != SETTING_TYPE_BOOLEAN) + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_SET_NOT_BOOLEAN, key); + else { + set_boolean(key, *value != '\0' ? value : "TOGGLE"); + set_print(settings_get_record(key)); + signal_emit("setup changed", 0); + } + + cmd_params_free(free_arg); +} + +static int config_key_compare(CONFIG_NODE *node1, CONFIG_NODE *node2) +{ + return g_ascii_strcasecmp(node1->key, node2->key); +} + +static void show_aliases(const char *alias) +{ + CONFIG_NODE *node; + GSList *tmp, *list; + int aliaslen; + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_ALIASLIST_HEADER); + + node = iconfig_node_traverse("aliases", FALSE); + tmp = node == NULL ? NULL : config_node_first(node->value); + + /* first get the list of aliases sorted */ + list = NULL; + aliaslen = strlen(alias); + for (; tmp != NULL; tmp = config_node_next(tmp)) { + CONFIG_NODE *node = tmp->data; + + if (node->type != NODE_TYPE_KEY) + continue; + + if (aliaslen != 0 && g_ascii_strncasecmp(node->key, alias, aliaslen) != 0) + continue; + + list = g_slist_insert_sorted(list, node, (GCompareFunc) config_key_compare); + } + + /* print the aliases */ + for (tmp = list; tmp != NULL; tmp = tmp->next) { + CONFIG_NODE *node = tmp->data; + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_ALIASLIST_LINE, + node->key, node->value); + } + g_slist_free(list); + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_ALIASLIST_FOOTER); +} + +static void alias_remove(const char *alias) +{ + if (iconfig_get_str("aliases", alias, NULL) == NULL) + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_ALIAS_NOT_FOUND, alias); + else { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_ALIAS_REMOVED, alias); + iconfig_set_str("aliases", alias, NULL); + + signal_emit("alias removed", 1, alias); + } +} + +/* SYNTAX: ALIAS [[-]<alias> [<command>]] */ +static void cmd_alias(const char *data) +{ + char *alias, *value; + void *free_arg; + + g_return_if_fail(data != NULL); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &alias, &value)) + return; + + if (*alias == '-') { + if (alias[1] != '\0') alias_remove(alias+1); + } else if (*alias == '\0' || *value == '\0') + show_aliases(alias); + else { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_ALIAS_ADDED, alias); + iconfig_set_str("aliases", alias, value); + signal_emit("alias added", 2, alias, value); + } + cmd_params_free(free_arg); +} + +/* SYNTAX: UNALIAS <alias> */ +static void cmd_unalias(const char *data) +{ + char *alias; + void *free_arg; + + g_return_if_fail(data != NULL); + + if (!cmd_get_params(data, &free_arg, 1, &alias)) + return; + if (*alias == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + alias_remove(alias); + cmd_params_free(free_arg); +} + +/* SYNTAX: RELOAD [<file>] */ +static void cmd_reload(const char *data) +{ + const char *fname; + + fname = *data == '\0' ? get_irssi_config() : data; + + if (settings_reread(fname)) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_CONFIG_RELOADED, fname); + } +} + +static void settings_save_fe(const char *fname) +{ + if (settings_save(fname, FALSE /* not autosaved */)) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_CONFIG_SAVED, fname); + } +} + +static void settings_save_confirm(const char *line, char *fname) +{ + if (i_toupper(line[0]) == 'Y') + settings_save_fe(fname); + g_free(fname); +} + +/* SYNTAX: SAVE [<file>] */ +static void cmd_save(const char *data) +{ + GHashTable *optlist; + char *format, *fname; + void *free_arg; + + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS, + "save", &optlist, &fname)) + return; + + if (*fname == '\0') + fname = mainconfig->fname; + + if (!irssi_config_is_changed(fname)) + settings_save_fe(fname); + else { + /* config file modified outside irssi */ + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_CONFIG_MODIFIED, fname); + + format = format_get_text(MODULE_NAME, NULL, NULL, NULL, + TXT_OVERWRITE_CONFIG); + keyboard_entry_redirect((SIGNAL_FUNC) settings_save_confirm, + format, 0, g_strdup(fname)); + g_free(format); + } + + cmd_params_free(free_arg); +} + +static void settings_clean_confirm(const char *line) +{ + if (i_toupper(line[0]) == 'Y') + settings_clean_invalid(); +} + +static void sig_settings_errors(const char *msg) +{ + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", msg); + keyboard_entry_redirect((SIGNAL_FUNC) settings_clean_confirm, + "Remove unknown settings from config file (Y/n)?", + 0, NULL); +} + +void fe_settings_init(void) +{ + command_bind("set", NULL, (SIGNAL_FUNC) cmd_set); + command_bind("toggle", NULL, (SIGNAL_FUNC) cmd_toggle); + command_bind("alias", NULL, (SIGNAL_FUNC) cmd_alias); + command_bind("unalias", NULL, (SIGNAL_FUNC) cmd_unalias); + command_bind("reload", NULL, (SIGNAL_FUNC) cmd_reload); + command_bind("save", NULL, (SIGNAL_FUNC) cmd_save); + command_set_options("set", "clear default section"); + + signal_add("settings errors", (SIGNAL_FUNC) sig_settings_errors); +} + +void fe_settings_deinit(void) +{ + command_unbind("set", (SIGNAL_FUNC) cmd_set); + command_unbind("toggle", (SIGNAL_FUNC) cmd_toggle); + command_unbind("alias", (SIGNAL_FUNC) cmd_alias); + command_unbind("unalias", (SIGNAL_FUNC) cmd_unalias); + command_unbind("reload", (SIGNAL_FUNC) cmd_reload); + command_unbind("save", (SIGNAL_FUNC) cmd_save); + + signal_remove("settings errors", (SIGNAL_FUNC) sig_settings_errors); +} diff --git a/src/fe-common/core/fe-settings.h b/src/fe-common/core/fe-settings.h new file mode 100644 index 0000000..61797bc --- /dev/null +++ b/src/fe-common/core/fe-settings.h @@ -0,0 +1,6 @@ +#ifndef IRSSI_FE_COMMON_CORE_FE_SETTINGS_H +#define IRSSI_FE_COMMON_CORE_FE_SETTINGS_H + +void fe_settings_set_print(const char *key); + +#endif diff --git a/src/fe-common/core/fe-tls.c b/src/fe-common/core/fe-tls.c new file mode 100644 index 0000000..d72a13b --- /dev/null +++ b/src/fe-common/core/fe-tls.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2015 Alexander Færøy <ahf@irssi.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA + */ + +#include "module.h" +#include <irssi/src/core/signals.h> +#include <irssi/src/core/settings.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/tls.h> + +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/fe-common/core/printtext.h> + +#include <irssi/src/fe-common/core/fe-tls.h> + +static void tls_handshake_finished(SERVER_REC *server, TLS_REC *tls) +{ + GSList *certs = NULL; + GSList *subject = NULL; + GSList *issuer = NULL; + GString *str = NULL; + TLS_CERT_ENTRY_REC *data = NULL; + + if (! settings_get_bool("tls_verbose_connect")) + return; + + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_HEADER); + + for (certs = tls->certs; certs != NULL; certs = certs->next) { + TLS_CERT_REC *tls_cert_rec = certs->data; + str = g_string_new(NULL); + + for (subject = tls_cert_rec->subject; subject != NULL; subject = subject->next) { + data = subject->data; + g_string_append_printf(str, "%s: %s, ", data->name, data->value); + } + + if (str->len > 1) + g_string_truncate(str, str->len - 2); + + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_SUBJECT, str->str); + g_string_free(str, TRUE); + + str = g_string_new(NULL); + + for (issuer = tls_cert_rec->issuer; issuer != NULL; issuer = issuer->next) { + data = issuer->data; + g_string_append_printf(str, "%s: %s, ", data->name, data->value); + } + + if (str->len > 1) + g_string_truncate(str, str->len - 2); + + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_ISSUER, str->str); + g_string_free(str, TRUE); + } + + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PROTOCOL_VERSION, tls->protocol_version, tls->cipher_size, tls->cipher); + + if (tls->ephemeral_key_algorithm != NULL) + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_EPHEMERAL_KEY, tls->ephemeral_key_size, tls->ephemeral_key_algorithm); + else + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_EPHEMERAL_KEY_UNAVAILBLE); + + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PUBKEY, tls->public_key_size, tls->public_key_algorithm, tls->not_before, tls->not_after); + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PUBKEY_FINGERPRINT, tls->public_key_fingerprint, tls->public_key_fingerprint_algorithm); + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_FINGERPRINT, tls->certificate_fingerprint, tls->certificate_fingerprint_algorithm); +} + +void fe_tls_init(void) +{ + settings_add_bool("lookandfeel", "tls_verbose_connect", TRUE); + + signal_add("tls handshake finished", (SIGNAL_FUNC)tls_handshake_finished); +} + +void fe_tls_deinit(void) +{ + signal_remove("tls handshake finished", (SIGNAL_FUNC)tls_handshake_finished); +} diff --git a/src/fe-common/core/fe-tls.h b/src/fe-common/core/fe-tls.h new file mode 100644 index 0000000..b0a3ea9 --- /dev/null +++ b/src/fe-common/core/fe-tls.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2015 Alexander Færøy <ahf@irssi.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA + */ + +#ifndef IRSSI_FE_COMMON_CORE_FE_TLS_H +#define IRSSI_FE_COMMON_CORE_FE_TLS_H + +void fe_tls_init(void); +void fe_tls_deinit(void); + +#endif diff --git a/src/fe-common/core/fe-windows.c b/src/fe-common/core/fe-windows.c new file mode 100644 index 0000000..d575813 --- /dev/null +++ b/src/fe-common/core/fe-windows.c @@ -0,0 +1,845 @@ +/* + windows.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/core/modules.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/servers.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/core/levels.h> + +#include <irssi/src/fe-common/core/printtext.h> +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/core/window-items.h> + +GSList *windows; /* first in the list is the active window, + next is the last active, etc. */ +GSequence *windows_seq; +WINDOW_REC *active_win; + +static int daytag; +static int daycheck; /* 0 = don't check, 1 = time is 00:00, check, + 2 = time is 00:00, already checked */ + +static int window_refnum_lookup(WINDOW_REC *window, void *refnum_p) +{ + int refnum = GPOINTER_TO_INT(refnum_p); + return window->refnum == refnum ? 0 : window->refnum < refnum ? -1 : 1; +} + +static GSequenceIter *windows_seq_begin(void) +{ + return g_sequence_get_begin_iter(windows_seq); +} + +static GSequenceIter *windows_seq_end(void) +{ + return g_sequence_get_end_iter(windows_seq); +} + +static GSequenceIter *windows_seq_insert(WINDOW_REC *rec) +{ + return g_sequence_insert_sorted(windows_seq, rec, (GCompareDataFunc)window_refnum_cmp, NULL); +} + +static GSequenceIter *windows_seq_refnum_lookup(int refnum) +{ + return g_sequence_lookup(windows_seq, GINT_TO_POINTER(refnum), (GCompareDataFunc)window_refnum_lookup, NULL); +} + +static void windows_seq_changed(GSequenceIter *iter) +{ + g_sequence_sort_changed(iter, (GCompareDataFunc)window_refnum_cmp, NULL); +} + +static GSequenceIter *windows_seq_window_lookup(WINDOW_REC *rec) +{ + return g_sequence_lookup(windows_seq, rec, (GCompareDataFunc)window_refnum_cmp, NULL); +} + +/* search to the numerically right iterator of refnum */ +static GSequenceIter *windows_seq_refnum_search_right(int refnum) +{ + return g_sequence_search(windows_seq, GINT_TO_POINTER(refnum), (GCompareDataFunc)window_refnum_lookup, NULL); +} + +/* we want to find the numerically left iterator of refnum, so we + search the right of the previous refnum. but we need to figure out + the case where the iterator is already at the beginning, i.e + iter->refnum >= refnum */ +static GSequenceIter *windows_seq_refnum_search_left(int refnum) +{ + GSequenceIter *iter = windows_seq_refnum_search_right(refnum - 1); + return iter == windows_seq_begin() ? NULL : g_sequence_iter_prev(iter); +} + +static int window_get_new_refnum(void) +{ + WINDOW_REC *win; + GSequenceIter *iter, *end; + int refnum; + + refnum = 1; + iter = windows_seq_begin(); + end = windows_seq_end(); + + while (iter != end) { + win = g_sequence_get(iter); + + if (refnum != win->refnum) + return refnum; + + refnum++; + iter = g_sequence_iter_next(iter); + } + + return refnum; +} + +WINDOW_REC *window_create(WI_ITEM_REC *item, int automatic) +{ + WINDOW_REC *rec; + + rec = g_new0(WINDOW_REC, 1); + rec->refnum = window_get_new_refnum(); + rec->level = settings_get_level("window_default_level"); + + windows = g_slist_prepend(windows, rec); + windows_seq_insert(rec); + signal_emit("window created", 2, rec, GINT_TO_POINTER(automatic)); + + if (item != NULL) window_item_add(rec, item, automatic); + if (windows->next == NULL || !automatic || settings_get_bool("window_auto_change")) { + if (automatic && windows->next != NULL) + signal_emit("window changed automatic", 1, rec); + window_set_active(rec); + } + return rec; +} + +static void window_set_refnum0(WINDOW_REC *window, int refnum) +{ + int old_refnum; + + g_return_if_fail(window != NULL); + g_return_if_fail(refnum >= 1); + if (window->refnum == refnum) return; + + old_refnum = window->refnum; + window->refnum = refnum; + signal_emit("window refnum changed", 2, window, GINT_TO_POINTER(old_refnum)); +} + +/* removed_refnum was removed from the windows list, pack the windows so + there won't be any holes. If there is any holes after removed_refnum, + leave the windows behind it alone. */ +static void windows_pack(int removed_refnum) +{ + WINDOW_REC *window; + int refnum; + GSequenceIter *iter, *end; + + refnum = removed_refnum + 1; + end = windows_seq_end(); + iter = windows_seq_refnum_lookup(refnum); + if (iter == NULL) return; + + while (iter != end) { + window = g_sequence_get(iter); + + if (window == NULL || window->sticky_refnum || window->refnum != refnum) + break; + + window_set_refnum0(window, refnum - 1); + windows_seq_changed(iter); + + refnum++; + iter = g_sequence_iter_next(iter); + } +} + +void window_destroy(WINDOW_REC *window) +{ + GSequenceIter *iter; + g_return_if_fail(window != NULL); + + if (window->destroying) return; + window->destroying = TRUE; + windows = g_slist_remove(windows, window); + iter = windows_seq_window_lookup(window); + if (iter != NULL) g_sequence_remove(iter); + + if (active_win == window) { + active_win = NULL; /* it's corrupted */ + if (windows != NULL) + window_set_active(windows->data); + } + + while (window->items != NULL) + window_item_destroy(window->items->data); + + if (settings_get_bool("windows_auto_renumber")) + windows_pack(window->refnum); + + signal_emit("window destroyed", 1, window); + + while (window->bound_items != NULL) + window_bind_destroy(window, window->bound_items->data); + + g_free_not_null(window->hilight_color); + g_free_not_null(window->servertag); + g_free_not_null(window->theme_name); + g_free_not_null(window->name); + g_free(window); +} + +void window_auto_destroy(WINDOW_REC *window) +{ + if (settings_get_bool("autoclose_windows") && windows->next != NULL && + window->items == NULL && window->bound_items == NULL && + window->level == 0 && !window->immortal) + window_destroy(window); +} + +void window_set_active(WINDOW_REC *window) +{ + WINDOW_REC *old_window; + + if (window == active_win) + return; + + old_window = active_win; + active_win = window; + if (active_win != NULL) { + windows = g_slist_remove(windows, active_win); + windows = g_slist_prepend(windows, active_win); + } + + if (active_win != NULL) + signal_emit("window changed", 2, active_win, old_window); +} + +void window_change_server(WINDOW_REC *window, void *server) +{ + SERVER_REC *active, *connect; + + if (server != NULL && SERVER(server)->disconnected) + return; + + if (server == NULL) { + active = connect = NULL; + } else if (g_slist_find(servers, server) != NULL) { + active = server; + connect = NULL; + } else { + active = NULL; + connect = server; + } + + if (window->connect_server != connect) { + window->connect_server = connect; + signal_emit("window connect changed", 2, window, connect); + } + + if (window->active_server != active) { + window->active_server = active; + signal_emit("window server changed", 2, window, active); + } +} + +void window_set_refnum(WINDOW_REC *window, int refnum) +{ + GSequenceIter *other_iter, *window_iter; + int old_refnum; + + g_return_if_fail(window != NULL); + g_return_if_fail(refnum >= 1); + if (window->refnum == refnum) return; + + other_iter = windows_seq_refnum_lookup(refnum); + window_iter = windows_seq_refnum_lookup(window->refnum); + + if (other_iter != NULL) { + WINDOW_REC *rec = g_sequence_get(other_iter); + + rec->refnum = window->refnum; + signal_emit("window refnum changed", 2, rec, GINT_TO_POINTER(refnum)); + } + + old_refnum = window->refnum; + window->refnum = refnum; + signal_emit("window refnum changed", 2, window, GINT_TO_POINTER(old_refnum)); + + if (window_iter != NULL && other_iter != NULL) { + g_sequence_swap(other_iter, window_iter); + } else { + windows_seq_changed(window_iter); + } +} + +void window_set_name(WINDOW_REC *window, const char *name) +{ + g_free_not_null(window->name); + window->name = name == NULL || *name == '\0' ? NULL : g_strdup(name); + + signal_emit("window name changed", 1, window); +} + +void window_set_history(WINDOW_REC *window, const char *name) +{ + char *oldname; + oldname = window->history_name; + + if (name == NULL || *name == '\0') + window->history_name = NULL; + else + window->history_name = g_strdup(name); + + signal_emit("window history changed", 2, window, oldname); + + g_free_not_null(oldname); +} + +void window_clear_history(WINDOW_REC *window, const char *name) +{ + signal_emit("window history cleared", 2, window, name); +} + +void window_set_level(WINDOW_REC *window, int level) +{ + g_return_if_fail(window != NULL); + + window->level = level; + signal_emit("window level changed", 1, window); +} + +void window_set_immortal(WINDOW_REC *window, int immortal) +{ + g_return_if_fail(window != NULL); + + window->immortal = immortal; + signal_emit("window immortal changed", 1, window); +} + +/* return active item's name, or if none is active, window's name */ +const char *window_get_active_name(WINDOW_REC *window) +{ + g_return_val_if_fail(window != NULL, NULL); + + if (window->active != NULL) + return window->active->visible_name; + + return window->name; +} + +#define WINDOW_LEVEL_MATCH(window, server, level) \ + (((window)->level & level) && \ + (server == NULL || (window)->active_server == server)) + +WINDOW_REC *window_find_level(void *server, int level) +{ + GSList *tmp; + WINDOW_REC *match; + + match = NULL; + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + if (WINDOW_LEVEL_MATCH(rec, server, level)) { + /* prefer windows without any items */ + if (rec->items == NULL) + return rec; + + if (match == NULL) + match = rec; + else if (active_win == rec) { + /* prefer active window over others */ + match = rec; + } + } + } + + return match; +} + +WINDOW_REC *window_find_closest(void *server, const char *name, int level) +{ + WINDOW_REC *window,*namewindow=NULL; + WI_ITEM_REC *item; + int i; + + /* match by name */ + item = name == NULL ? NULL : + window_item_find(server, name); + if (item != NULL) { + namewindow = window_item_window(item); + if (namewindow != NULL && + ((namewindow->level & level) != 0 || + !settings_get_bool("window_check_level_first"))) { + /* match, but if multiple windows have the same level + we could be choosing a bad one here, eg. + name=nick1 would get nick2's query instead of + generic msgs window. + + And check for prefixed !channel name --Borys */ + if (g_ascii_strcasecmp(name, item->visible_name) == 0 || + g_ascii_strcasecmp(name, (char *) window_item_get_target((WI_ITEM_REC *) item)) == 0) + return namewindow; + } + } + + /* prefer windows without items */ + for (i = 0; i < 2; i++) { + /* match by level */ + if (level != MSGLEVEL_HILIGHT) + level &= ~(MSGLEVEL_HILIGHT | MSGLEVEL_NOHILIGHT); + window = window_find_level(server, level); + if (window != NULL && (i == 1 || window->items == NULL)) + return window; + + /* match by level - ignore server */ + window = window_find_level(NULL, level); + if (window != NULL && (i == 1 || window->items == NULL)) + return window; + } + + /* still return item's window if we didnt find anything */ + if (namewindow != NULL) return namewindow; + + /* fallback to active */ + return active_win; +} + +WINDOW_REC *window_find_refnum(int refnum) +{ + GSequenceIter *iter; + + iter = windows_seq_refnum_lookup(refnum); + if (iter != NULL) { + WINDOW_REC *rec = g_sequence_get(iter); + + return rec; + } + + return NULL; +} + +WINDOW_REC *window_find_name(const char *name) +{ + GSList *tmp; + + g_return_val_if_fail(name != NULL, NULL); + + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + if (rec->name != NULL && + g_ascii_strcasecmp(rec->name, name) == 0) + return rec; + } + + return NULL; +} + +WINDOW_REC *window_find_item(SERVER_REC *server, const char *name) +{ + WINDOW_REC *rec; + WI_ITEM_REC *item; + + g_return_val_if_fail(name != NULL, NULL); + + rec = window_find_name(name); + if (rec != NULL) return rec; + + item = server == NULL ? NULL : + window_item_find(server, name); + if (item == NULL) { + /* not found from the active server - any server? */ + item = window_item_find(NULL, name); + } + + if (item == NULL) + return NULL; + + return window_item_window(item); +} + +int window_refnum_prev(int refnum, int wrap) +{ + WINDOW_REC *rec; + GSequenceIter *iter, *end; + + iter = windows_seq_refnum_search_left(refnum); + end = windows_seq_end(); + + if (iter != NULL) { + rec = g_sequence_get(iter); + return rec->refnum; + } + + if (wrap) { + iter = g_sequence_iter_prev(end); + if (iter != end) { + rec = g_sequence_get(iter); + return rec->refnum; + } + } + + return -1; +} + +int window_refnum_next(int refnum, int wrap) +{ + WINDOW_REC *rec; + GSequenceIter *iter, *end; + + iter = windows_seq_refnum_search_right(refnum); + end = windows_seq_end(); + + if (iter != end) { + rec = g_sequence_get(iter); + return rec->refnum; + } + + if (wrap) { + iter = windows_seq_begin(); + if (iter != end) { + rec = g_sequence_get(iter); + return rec->refnum; + } + } + + return -1; +} + +int windows_refnum_last(void) +{ + WINDOW_REC *rec; + GSequenceIter *end, *iter; + + end = windows_seq_end(); + iter = g_sequence_iter_prev(end); + if (iter != end) { + rec = g_sequence_get(iter); + return rec->refnum; + } + + return -1; +} + +int window_refnum_cmp(WINDOW_REC *w1, WINDOW_REC *w2) +{ + return w1 == w2 ? 0 : w1->refnum < w2->refnum ? -1 : 1; +} + +GSList *windows_get_sorted(void) +{ + GSequenceIter *iter, *begin; + GSList *sorted; + + sorted = NULL; + iter = windows_seq_end(); + begin = windows_seq_begin(); + + while (iter != begin) { + WINDOW_REC *rec; + + iter = g_sequence_iter_prev(iter); + rec = g_sequence_get(iter); + + sorted = g_slist_prepend(sorted, rec); + } + + return sorted; +} + +/* Add a new bind to window - if duplicate is found it's returned */ +WINDOW_BIND_REC *window_bind_add(WINDOW_REC *window, const char *servertag, + const char *name) +{ + WINDOW_BIND_REC *rec; + + g_return_val_if_fail(window != NULL, NULL); + g_return_val_if_fail(servertag != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + + rec = window_bind_find(window, servertag, name); + if (rec != NULL) + return rec; + + rec = g_new0(WINDOW_BIND_REC, 1); + rec->name = g_strdup(name); + rec->servertag = g_strdup(servertag); + + window->bound_items = g_slist_append(window->bound_items, rec); + return rec; +} + +void window_bind_destroy(WINDOW_REC *window, WINDOW_BIND_REC *rec) +{ + g_return_if_fail(window != NULL); + g_return_if_fail(rec != NULL); + + window->bound_items = g_slist_remove(window->bound_items, rec); + + g_free(rec->servertag); + g_free(rec->name); + g_free(rec); +} + +WINDOW_BIND_REC *window_bind_find(WINDOW_REC *window, const char *servertag, + const char *name) +{ + GSList *tmp; + + g_return_val_if_fail(window != NULL, NULL); + g_return_val_if_fail(servertag != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + + for (tmp = window->bound_items; tmp != NULL; tmp = tmp->next) { + WINDOW_BIND_REC *rec = tmp->data; + + if (g_ascii_strcasecmp(rec->name, name) == 0 && + g_ascii_strcasecmp(rec->servertag, servertag) == 0) + return rec; + } + + return NULL; +} + +void window_bind_remove_unsticky(WINDOW_REC *window) +{ + GSList *tmp, *next; + + for (tmp = window->bound_items; tmp != NULL; tmp = next) { + WINDOW_BIND_REC *rec = tmp->data; + + next = tmp->next; + if (!rec->sticky) + window_bind_destroy(window, rec); + } +} + +static void sig_server_connected(SERVER_REC *server) +{ + GSList *tmp; + + g_return_if_fail(server != NULL); + + /* Try to keep some server assigned to windows.. + Also change active window's server if the window is empty */ + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + if ((rec->servertag == NULL || + g_ascii_strcasecmp(rec->servertag, server->tag) == 0) && + (rec->active_server == NULL || + (rec == active_win && rec->items == NULL))) + window_change_server(rec, server); + } +} + +static void sig_server_disconnected(SERVER_REC *server) +{ + GSList *tmp; + SERVER_REC *new_server; + + g_return_if_fail(server != NULL); + + new_server = servers == NULL ? NULL : servers->data; + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + if (rec->active_server == server || + rec->connect_server == server) { + window_change_server(rec, rec->servertag != NULL ? + NULL : new_server); + } + } +} + +static void window_print_daychange(WINDOW_REC *window, time_t t) +{ + THEME_REC *theme; + TEXT_DEST_REC dest; + struct tm *tm; + char *format, str[256]; + int ret; + + theme = active_win->theme != NULL ? active_win->theme : current_theme; + format_create_dest(&dest, NULL, NULL, MSGLEVEL_NEVER, window); + format = format_get_text_theme(theme, MODULE_NAME, &dest, + TXT_DAYCHANGE); + tm = localtime(&t); + ret = strftime(str, sizeof(str), format, tm); + g_free(format); + if (ret <= 0) return; + + printtext_string_window(window, MSGLEVEL_NEVER, str); +} + +short color_24bit_256 (const unsigned char rgb[]) +{ + static const int cstep_size = 40; + static const int cstep_start = 0x5f; + + static const int gstep_size = 10; + static const int gstep_start = 0x08; + + int dist[3] = {0}; + int r[3], gr[3]; + + size_t i; + + for (i = 0; i < 3; ++i) { + const int n = rgb[i]; + gr[i] = -1; + if (n < cstep_start /2) { + r[i] = 0; + dist[i] = -cstep_size/2; + } + else { + r[i] = 1+((n-cstep_start + cstep_size /2)/cstep_size); + dist[i] = ((n-cstep_start + cstep_size /2)%cstep_size - cstep_size/2); + } + if (n < gstep_start /2) { + gr[i] = -1; + } + else { + gr[i] = ((n-gstep_start + gstep_size /2)/gstep_size); + } + } + if (r[0] == r[1] && r[1] == r[2] && + 4*abs(dist[0]) < gstep_size && 4*abs(dist[1]) < gstep_size && 4*abs(dist[2]) < gstep_size) { + /* skip gray detection */ + } + else { + const int j = r[1] == r[2] ? 0 : 1; + if ((r[0] == r[1] || r[j] == r[2]) && abs(r[j]-r[(j+1)%3]) <= 1) { + const int k = gr[1] == gr[2] ? 0 : 1; + if ((gr[0] == gr[1] || gr[k] == gr[2]) && abs(gr[k]-gr[(k+1)%3]) <= 2) { + if (gr[k] < 0) { + r[0] = r[1] = r[2] = 0; + } + else if (gr[k] > 23) { + r[0] = r[1] = r[2] = 5; + } + else { + r[0] = 6; + r[1] = (gr[k] / 6); + r[2] = gr[k]%6; + } + } + } + } + return 16 + r[0]*36 + r[1] * 6 + r[2]; +} + +static void sig_print_text(void) +{ + GSList *tmp; + time_t t; + struct tm *tm; + + t = time(NULL); + tm = localtime(&t); + if (tm->tm_hour != 0 || tm->tm_min != 0) + return; + + daycheck = 2; + signal_remove("print text", (SIGNAL_FUNC) sig_print_text); + + /* day changed, print notice about it to every window */ + for (tmp = windows; tmp != NULL; tmp = tmp->next) + window_print_daychange(tmp->data, t); +} + +static int sig_check_daychange(void) +{ + time_t t; + struct tm *tm; + + t = time(NULL); + tm = localtime(&t); + + if (daycheck == 1 && tm->tm_hour == 0 && tm->tm_min == 0) { + sig_print_text(); + return TRUE; + } + + if (tm->tm_hour != 23 || tm->tm_min != 59) { + daycheck = 0; + return TRUE; + } + + /* time is 23:59 */ + if (daycheck == 0) { + daycheck = 1; + signal_add("print text", (SIGNAL_FUNC) sig_print_text); + } + return TRUE; +} + +static void read_settings(void) +{ + if (daytag != -1) { + g_source_remove(daytag); + daytag = -1; + } + + if (settings_get_bool("timestamps")) + daytag = g_timeout_add(30000, (GSourceFunc) sig_check_daychange, NULL); +} + +void windows_init(void) +{ + active_win = NULL; + windows_seq = g_sequence_new(NULL); + daycheck = 0; daytag = -1; + settings_add_bool("lookandfeel", "window_auto_change", FALSE); + settings_add_bool("lookandfeel", "windows_auto_renumber", TRUE); + settings_add_bool("lookandfeel", "window_check_level_first", FALSE); + settings_add_level("lookandfeel", "window_default_level", "NONE"); + + read_settings(); + signal_add("server looking", (SIGNAL_FUNC) sig_server_connected); + signal_add("server connected", (SIGNAL_FUNC) sig_server_connected); + signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected); + signal_add("server connect failed", (SIGNAL_FUNC) sig_server_disconnected); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); +} + +void windows_deinit(void) +{ + if (daytag != -1) g_source_remove(daytag); + if (daycheck == 1) signal_remove("print text", (SIGNAL_FUNC) sig_print_text); + + signal_remove("server looking", (SIGNAL_FUNC) sig_server_connected); + signal_remove("server connected", (SIGNAL_FUNC) sig_server_connected); + signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected); + signal_remove("server connect failed", (SIGNAL_FUNC) sig_server_disconnected); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); + g_sequence_free(windows_seq); + windows_seq = NULL; +} diff --git a/src/fe-common/core/fe-windows.h b/src/fe-common/core/fe-windows.h new file mode 100644 index 0000000..d5c60c8 --- /dev/null +++ b/src/fe-common/core/fe-windows.h @@ -0,0 +1,112 @@ +#ifndef IRSSI_FE_COMMON_CORE_FE_WINDOWS_H +#define IRSSI_FE_COMMON_CORE_FE_WINDOWS_H + +#include <irssi/src/core/window-item-def.h> +#include <irssi/src/fe-common/core/command-history.h> + +enum { + DATA_LEVEL_NONE = 0, + DATA_LEVEL_TEXT, + DATA_LEVEL_MSG, + DATA_LEVEL_HILIGHT +}; + +enum { + MAIN_WINDOW_TYPE_NONE = -1, + MAIN_WINDOW_TYPE_DEFAULT = 0, + MAIN_WINDOW_TYPE_HIDDEN = 1, + MAIN_WINDOW_TYPE_SPLIT = 2, + MAIN_WINDOW_TYPE_RSPLIT = 3 +}; + +typedef struct { + char *servertag; + char *name; + int type; + unsigned int sticky:1; +} WINDOW_BIND_REC; + +struct _WINDOW_REC { + int refnum; + char *name; + + int width, height; + + GSList *items; + WI_ITEM_REC *active; + SERVER_REC *active_server; + SERVER_REC *connect_server; + char *servertag; /* active_server must be either NULL or have this tag (unless there's items in this window) */ + + int level; /* message level */ + GSList *bound_items; /* list of WINDOW_BIND_RECs */ + + unsigned int immortal:1; + unsigned int sticky_refnum:1; + unsigned int destroying:1; + + /* window-specific command line history */ + HISTORY_REC *history; + char *history_name; + + int data_level; /* current data level */ + char *hilight_color; /* current hilight color in %format */ + + time_t last_timestamp; /* When was last timestamp printed */ + time_t last_line; /* When was last line printed */ + + char *theme_name; /* active theme in window, NULL = default */ + void *theme; /* THEME_REC */ + + void *gui_data; +}; + +extern GSList *windows; +extern WINDOW_REC *active_win; + +WINDOW_REC *window_create(WI_ITEM_REC *item, int automatic); +void window_destroy(WINDOW_REC *window); + +void window_auto_destroy(WINDOW_REC *window); + +void window_set_active(WINDOW_REC *window); +void window_change_server(WINDOW_REC *window, void *server); + +void window_set_refnum(WINDOW_REC *window, int refnum); +void window_set_name(WINDOW_REC *window, const char *name); +void window_set_history(WINDOW_REC *window, const char *name); +void window_clear_history(WINDOW_REC *window, const char *name); +void window_set_level(WINDOW_REC *window, int level); +void window_set_immortal(WINDOW_REC *window, int immortal); + +/* return active item's name, or if none is active, window's name */ +const char *window_get_active_name(WINDOW_REC *window); + +WINDOW_REC *window_find_level(void *server, int level); +WINDOW_REC *window_find_closest(void *server, const char *name, int level); +WINDOW_REC *window_find_refnum(int refnum); +WINDOW_REC *window_find_name(const char *name); +WINDOW_REC *window_find_item(SERVER_REC *server, const char *name); + +int window_refnum_prev(int refnum, int wrap); +int window_refnum_next(int refnum, int wrap); +int windows_refnum_last(void); + +int window_refnum_cmp(WINDOW_REC *w1, WINDOW_REC *w2); +GSList *windows_get_sorted(void); + +/* Add a new bind to window - if duplicate is found it's returned */ +WINDOW_BIND_REC *window_bind_add(WINDOW_REC *window, const char *servertag, + const char *name); +void window_bind_destroy(WINDOW_REC *window, WINDOW_BIND_REC *rec); + +WINDOW_BIND_REC *window_bind_find(WINDOW_REC *window, const char *servertag, + const char *name); +void window_bind_remove_unsticky(WINDOW_REC *window); + +void windows_init(void); +void windows_deinit(void); + +short color_24bit_256(const unsigned char rgb[]); + +#endif diff --git a/src/fe-common/core/formats.c b/src/fe-common/core/formats.c new file mode 100644 index 0000000..0c8b308 --- /dev/null +++ b/src/fe-common/core/formats.c @@ -0,0 +1,1750 @@ +/* + formats.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/special-vars.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/core/levels.h> +#include <irssi/src/core/servers.h> + +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/core/window-items.h> +#include <irssi/src/fe-common/core/formats.h> +#include <irssi/src/fe-common/core/themes.h> +#include <irssi/src/core/recode.h> +#include <irssi/src/core/utf8.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/refstrings.h> + +static const char *format_backs = "04261537"; +static const char *format_fores = "kbgcrmyw"; +static const char *format_boldfores = "KBGCRMYW"; +static const char *ext_color_al = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +static int signal_gui_print_text; +static int hide_text_style, hide_server_tags, hide_colors; + +static int timestamp_level; +static int timestamp_timeout; + +static GHashTable *global_meta; + +int format_find_tag(const char *module, const char *tag) +{ + FORMAT_REC *formats; + int n; + + formats = g_hash_table_lookup(default_formats, module); + if (formats == NULL) + return -1; + + for (n = 0; formats[n].def != NULL; n++) { + if (formats[n].tag != NULL && + g_ascii_strcasecmp(formats[n].tag, tag) == 0) + return n; + } + + return -1; +} + +static void format_expand_code(const char **format, GString *out, int *flags) +{ + int set; + + if (flags == NULL) { + /* flags are being ignored - skip the code */ + while (**format != ']' && **format != '\0') + (*format)++; + return; + } + + set = TRUE; + (*format)++; + while (**format != ']' && **format != '\0') { + if (**format == '+') + set = TRUE; + else if (**format == '-') + set = FALSE; + else switch (**format) { + case 's': + case 'S': + *flags |= !set ? PRINT_FLAG_UNSET_LINE_START : + **format == 's' ? PRINT_FLAG_SET_LINE_START : + PRINT_FLAG_SET_LINE_START_IRSSI; + break; + case 't': + *flags |= set ? PRINT_FLAG_SET_TIMESTAMP : + PRINT_FLAG_UNSET_TIMESTAMP; + break; + case 'T': + *flags |= set ? PRINT_FLAG_SET_SERVERTAG : + PRINT_FLAG_UNSET_SERVERTAG; + break; + } + + (*format)++; + } +} + +void format_ext_color(GString *out, int bg, int color) +{ + g_string_append_c(out, 4); + if (bg && color < 0x10) + g_string_append_c(out, FORMAT_COLOR_NOCHANGE); + if (color < 0x10) + g_string_append_c(out, color+'0'); + else { + if (color < 0x60) + g_string_append_c(out, bg ? FORMAT_COLOR_EXT1_BG + : FORMAT_COLOR_EXT1); + else if (color < 0xb0) + g_string_append_c(out, bg ? FORMAT_COLOR_EXT2_BG + : FORMAT_COLOR_EXT2); + else + g_string_append_c(out, bg ? FORMAT_COLOR_EXT3_BG + : FORMAT_COLOR_EXT3); + g_string_append_c(out, FORMAT_COLOR_NOCHANGE + ((color-0x10)%0x50)); + } + + if (!bg && color < 0x10) + g_string_append_c(out, FORMAT_COLOR_NOCHANGE); +} + +static void format_ext_color_unexpand(GString *out, gboolean bg, int base, char color) +{ + unsigned char value = base + (unsigned char) color - FORMAT_COLOR_NOCHANGE - 0x10; + + g_string_append_c(out, '%'); + g_string_append_c(out, bg ? 'x' : 'X'); + if (value > 214) + value += 10; + g_string_append_c(out, '1' + (value / 36)); + g_string_append_c(out, ext_color_al[value % 36]); +} + +#ifdef TERM_TRUECOLOR +void unformat_24bit_color(char **ptr, int off, int *fgcolor, int *bgcolor, int *flags) +{ + unsigned int color; + unsigned char rgbx[4]; + unsigned int i; + for (i = 0; i < 4; ++i) { + if ((*ptr)[i + off] == '\0') + return; + rgbx[i] = (*ptr)[i + off]; + } + rgbx[3] -= 0x20; + *ptr += 4; + for (i = 0; i < 3; ++i) { + if (rgbx[3] & (0x10 << i)) + rgbx[i] -= 0x20; + } + color = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2]; + if (rgbx[3] & 0x1) { + *bgcolor = color; + *flags |= GUI_PRINT_FLAG_COLOR_24_BG; + } + else { + *fgcolor = color; + *flags |= GUI_PRINT_FLAG_COLOR_24_FG; + } +} + +static void format_24bit_color_unexpand(GString *out, int off, const char **ptr) +{ + unsigned int color; + unsigned char rgbx[4]; + unsigned int i; + for (i = 0; i < 4; ++i) { + if ((*ptr)[i + off] == '\0') + return; + rgbx[i] = (*ptr)[i + off]; + } + rgbx[3] -= 0x20; + *ptr += 4; + g_string_append_c(out, '%'); + for (i = 0; i < 3; ++i) { + if (rgbx[3] & (0x10 << i)) + rgbx[i] -= 0x20; + } + color = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2]; + g_string_append_c(out, rgbx[3] & 0x1 ? 'z' : 'Z'); + g_string_append_printf(out, "%06X", color); +} +#endif + +void format_24bit_color(GString *out, int bg, unsigned int color) +{ + unsigned char rgb[] = { color >> 16, color >> 8, color }; +#ifdef TERM_TRUECOLOR + unsigned char x = bg ? 0x1 : 0; + unsigned int i; + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_COLOR_24); + for (i = 0; i < 3; ++i) { + if (rgb[i] > 0x20) + g_string_append_c(out, rgb[i]); + else { + g_string_append_c(out, 0x20 + rgb[i]); + x |= 0x10 << i; + } + } + g_string_append_c(out, 0x20 + x); +#else /* !TERM_TRUECOLOR */ + format_ext_color(out, bg, color_24bit_256(rgb)); +#endif /* TERM_TRUECOLOR */ +} + +int format_expand_styles(GString *out, const char **format, int *flags) +{ + int retval = 1; + + char *p, fmt; + + /* storage for numerical parsing code for %x/X formats. */ + int tmp; + unsigned int tmp2; + + fmt = **format; + switch (fmt) { + case '{': + case '}': + case '%': + /* escaped char */ + g_string_append_c(out, fmt); + break; + case 'U': + /* Underline on/off */ + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_UNDERLINE); + break; + case '9': + case '_': + /* bold on/off */ + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_BOLD); + break; + case '8': + /* reverse */ + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_REVERSE); + break; + case 'I': + /* italic */ + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_ITALIC); + break; + case ':': + /* Newline */ + g_string_append_c(out, '\n'); + break; + case '|': + /* Indent here */ + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_INDENT); + break; + case 'F': + /* blink */ + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_BLINK); + break; + case 'n': + case 'N': + /* default color */ + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_DEFAULTS); + break; + case '>': + /* clear to end of line */ + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_CLRTOEOL); + break; + case '#': + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_MONOSPACE); + break; + case '[': + /* code */ + format_expand_code(format, out, flags); + if ((*format)[0] == '\0') + /* oops, reached end prematurely */ + (*format)--; + + break; + case 'x': + case 'X': + if ((*format)[1] < '0' || (*format)[1] > '7') + break; + + tmp = 16 + ((*format)[1]-'0'-1)*36; + if (tmp > 231) { + if (!isalpha((*format)[2])) + break; + + tmp += (*format)[2] >= 'a' ? (*format)[2] - 'a' : (*format)[2] - 'A'; + + if (tmp > 255) + break; + } + else if (tmp > 0) { + if (!isalnum((*format)[2])) + break; + + if ((*format)[2] >= 'a') + tmp += 10 + (*format)[2] - 'a'; + else if ((*format)[2] >= 'A') + tmp += 10 + (*format)[2] - 'A'; + else + tmp += (*format)[2] - '0'; + } + else { + if (!isxdigit((*format)[2])) + break; + + tmp = g_ascii_xdigit_value((*format)[2]); + } + + retval += 2; + + format_ext_color(out, fmt == 'x', tmp); + break; + case 'z': + case 'Z': + tmp2 = 0; + for (tmp = 1; tmp < 7; ++tmp) { + if (!isxdigit((*format)[tmp])) { + tmp2 = UINT_MAX; + break; + } + tmp2 <<= 4; + tmp2 |= g_ascii_xdigit_value((*format)[tmp]); + } + + if (tmp2 == UINT_MAX) + break; + + retval += 6; + + format_24bit_color(out, fmt == 'z', tmp2); + break; + case 'o': + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_COLOR_NOCHANGE); + g_string_append_c(out, (char) -1); + break; + case 'O': + g_string_append_c(out, 4); + g_string_append_c(out, (char) -1); + g_string_append_c(out, FORMAT_COLOR_NOCHANGE); + break; + default: + /* check if it's a background color */ + p = strchr(format_backs, fmt); + if (p != NULL) { + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_COLOR_NOCHANGE); + g_string_append_c(out, (char) ((int) (p-format_backs)+'0')); + break; + } + + /* check if it's a foreground color */ + if (fmt == 'p') fmt = 'm'; + p = strchr(format_fores, fmt); + if (p != NULL) { + g_string_append_c(out, 4); + g_string_append_c(out, (char) ((int) (p-format_fores)+'0')); + g_string_append_c(out, FORMAT_COLOR_NOCHANGE); + break; + } + + /* check if it's a bold foreground color */ + if (fmt == 'P') fmt = 'M'; + p = strchr(format_boldfores, fmt); + if (p != NULL) { + g_string_append_c(out, 4); + g_string_append_c(out, (char) (8+(int) (p-format_boldfores)+'0')); + g_string_append_c(out, FORMAT_COLOR_NOCHANGE); + break; + } + + return FALSE; + } + + return retval; +} + +void format_read_arglist(va_list va, FORMAT_REC *format, + char **arglist, int arglist_size, + char *buffer, int buffer_size) +{ + int num, len, bufpos; + + g_return_if_fail(format->params < arglist_size); + + bufpos = 0; + arglist[format->params] = NULL; + for (num = 0; num < format->params; num++) { + switch (format->paramtypes[num]) { + case FORMAT_STRING: + arglist[num] = (char *) va_arg(va, char *); + if (arglist[num] == NULL) + arglist[num] = ""; + break; + case FORMAT_INT: { + int d = (int) va_arg(va, int); + + if (bufpos >= buffer_size) { + arglist[num] = ""; + break; + } + + arglist[num] = buffer+bufpos; + len = g_snprintf(buffer+bufpos, buffer_size-bufpos, + "%d", d); + bufpos += len+1; + break; + } + case FORMAT_LONG: { + long l = (long) va_arg(va, long); + + if (bufpos >= buffer_size) { + arglist[num] = ""; + break; + } + + arglist[num] = buffer+bufpos; + len = g_snprintf(buffer+bufpos, buffer_size-bufpos, + "%ld", l); + bufpos += len+1; + break; + } + case FORMAT_FLOAT: { + double f = (double) va_arg(va, double); + + if (bufpos >= buffer_size) { + arglist[num] = ""; + break; + } + + arglist[num] = buffer+bufpos; + len = g_snprintf(buffer+bufpos, buffer_size-bufpos, + "%0.2f", f); + bufpos += len+1; + break; + } + } + } +} + +void format_dest_meta_stash(TEXT_DEST_REC *dest, const char *meta_key, const char *meta_value) +{ + g_hash_table_replace(dest->meta, i_refstr_intern(meta_key), g_strdup(meta_value)); +} + +const char *format_dest_meta_stash_find(TEXT_DEST_REC *dest, const char *meta_key) +{ + return g_hash_table_lookup(dest->meta, meta_key); +} + +void format_dest_meta_clear_all(TEXT_DEST_REC *dest) +{ + g_hash_table_remove_all(dest->meta); +} + +static void clear_global_meta(WINDOW_REC *window, TEXT_DEST_REC *dest) +{ + if (dest != NULL && dest->meta == global_meta) + g_hash_table_remove_all(global_meta); +} + +void format_create_dest_tag_meta(TEXT_DEST_REC *dest, void *server, const char *server_tag, + const char *target, int level, WINDOW_REC *window, + GHashTable *meta) +{ + memset(dest, 0, sizeof(TEXT_DEST_REC)); + + dest->server = server; + dest->server_tag = server != NULL ? SERVER(server)->tag : server_tag; + dest->target = target; + dest->level = level; + dest->window = window != NULL ? window : + window_find_closest(server, target, level); + dest->meta = meta != NULL ? meta : global_meta; +} + +void format_create_dest_tag(TEXT_DEST_REC *dest, void *server, const char *server_tag, + const char *target, int level, WINDOW_REC *window) +{ + format_create_dest_tag_meta(dest, server, server_tag, target, level, window, + server != NULL ? SERVER(server)->current_incoming_meta : NULL); +} + +void format_create_dest(TEXT_DEST_REC *dest, void *server, const char *target, int level, + WINDOW_REC *window) +{ + format_create_dest_tag(dest, server, NULL, target, level, window); +} + +/* Return length of text part in string (ie. without % codes) */ +int format_get_length(const char *str) +{ + GString *tmp; + int len; + int utf8; + int adv = 0; + + g_return_val_if_fail(str != NULL, 0); + + utf8 = string_policy(str); + + tmp = g_string_new(NULL); + len = 0; + while (*str != '\0') { + if (*str == '%' && str[1] != '\0') { + str++; + if (*str != '%') { + adv = format_expand_styles(tmp, &str, NULL); + str += adv; + if (adv) + continue; + } + + /* %% or unknown %code, written as-is */ + if (*str != '%') + len++; + } + + len += string_advance(&str, utf8); + } + + g_string_free(tmp, TRUE); + return len; +} + +/* Return how many characters in `str' must be skipped before `len' + characters of text is skipped. Like strip_real_length(), except this + handles %codes. */ +int format_real_length(const char *str, int len) +{ + GString *tmp; + const char *start; + const char *oldstr; + int utf8; + int adv = 0; + g_return_val_if_fail(str != NULL, 0); + g_return_val_if_fail(len >= 0, 0); + + utf8 = string_policy(str); + + start = str; + tmp = g_string_new(NULL); + while (*str != '\0') { + oldstr = str; + if (*str == '%' && str[1] != '\0') { + str++; + if (*str != '%') { + adv = format_expand_styles(tmp, &str, NULL); + if (adv) { + str += adv; + continue; + } + /* discount for unknown % */ + if (--len < 0) { + str = oldstr; + break; + } + oldstr = str; + } + } + + len -= string_advance(&str, utf8); + if (len < 0) { + str = oldstr; + break; + } + } + + g_string_free(tmp, TRUE); + return (int) (str-start); +} + +char *format_string_expand(const char *text, int *flags) +{ + GString *out; + char code, *ret; + int adv; + + g_return_val_if_fail(text != NULL, NULL); + + out = g_string_new(NULL); + + if (flags != NULL) *flags = 0; + code = 0; + while (*text != '\0') { + if (code == '%') { + /* color code */ + adv = format_expand_styles(out, &text, flags); + if (!adv) { + g_string_append_c(out, '%'); + g_string_append_c(out, '%'); + g_string_append_c(out, *text); + } else { + text += adv - 1; + } + code = 0; + } else { + if (*text == '%') + code = *text; + else + g_string_append_c(out, *text); + } + + text++; + } + + ret = out->str; + g_string_free(out, FALSE); + return ret; +} + +inline static void format_flag_unexpand(GString *out, char flag) +{ + g_string_append_c(out, '%'); + g_string_append_c(out, flag); +} + +char *format_string_unexpand(const char *text, int flags) +{ + GString *out; + + g_return_val_if_fail(text != NULL, NULL); + + out = g_string_sized_new(strlen(text)); + while (*text != '\0') { + switch (*text) { + case '%': + g_string_append(out, "%%"); + break; + case 4: + text++; + if (*text == '\0') + break; + switch (*text) { + case FORMAT_COLOR_EXT1: + format_ext_color_unexpand(out, FALSE, 0x10, *++text); + break; + case FORMAT_COLOR_EXT1_BG: + format_ext_color_unexpand(out, TRUE, 0x10, *++text); + break; + case FORMAT_COLOR_EXT2: + format_ext_color_unexpand(out, FALSE, 0x60, *++text); + break; + case FORMAT_COLOR_EXT2_BG: + format_ext_color_unexpand(out, TRUE, 0x60, *++text); + break; + case FORMAT_COLOR_EXT3: + format_ext_color_unexpand(out, FALSE, 0xb0, *++text); + break; + case FORMAT_COLOR_EXT3_BG: + format_ext_color_unexpand(out, TRUE, 0xb0, *++text); + break; +#ifdef TERM_TRUECOLOR + case FORMAT_COLOR_24: + format_24bit_color_unexpand(out, 1, &text); + break; +#endif + case FORMAT_STYLE_BLINK: + format_flag_unexpand(out, 'F'); + break; + case FORMAT_STYLE_UNDERLINE: + format_flag_unexpand(out, 'U'); + break; + case FORMAT_STYLE_BOLD: + format_flag_unexpand(out, '9'); + break; + case FORMAT_STYLE_REVERSE: + format_flag_unexpand(out, '8'); + break; + case FORMAT_STYLE_INDENT: + format_flag_unexpand(out, '|'); + break; + case FORMAT_STYLE_ITALIC: + format_flag_unexpand(out, 'I'); + break; + case FORMAT_STYLE_DEFAULTS: + format_flag_unexpand(out, 'N'); + break; + case FORMAT_STYLE_CLRTOEOL: + format_flag_unexpand(out, '>'); + break; + case FORMAT_STYLE_MONOSPACE: + format_flag_unexpand(out, '#'); + break; + default: + if (*text != FORMAT_COLOR_NOCHANGE) { + unsigned int value = (unsigned char) *text - '0'; + + g_string_append_c(out, '%'); + if (value < 8) { + g_string_append_c(out, format_fores[value]); + } else if (value < 16) { + g_string_append_c(out, format_boldfores[value - 8]); + } else { + g_string_append_c(out, 'O'); + } + } + text++; + if (*text == '\0') + break; + + if (*text != FORMAT_COLOR_NOCHANGE) { + unsigned int value = (unsigned char) *text - '0'; + + g_string_append_c(out, '%'); + if (value < 8) { + g_string_append_c(out, format_backs[value]); + } else if (value < 16) { + g_string_append(out, "x0"); + g_string_append_c(out, ext_color_al[value]); + } else { + g_string_append_c(out, 'o'); + } + } + break; + } + break; + default: + g_string_append_c(out, *text); + break; + } + if (*text != '\0') + text++; + } + + return g_string_free(out, FALSE); +} + +static char *format_get_text_args(TEXT_DEST_REC *dest, + const char *text, char **arglist) +{ + GString *out; + char code, *ret; + int need_free; + int adv; + + out = g_string_new(NULL); + + code = 0; + while (*text != '\0') { + if (code == '%') { + /* color code */ + adv = format_expand_styles(out, &text, &dest->flags); + if (!adv) { + g_string_append_c(out, '%'); + g_string_append_c(out, '%'); + g_string_append_c(out, *text); + } else { + text += adv - 1; + } + code = 0; + } else if (code == '$') { + /* argument */ + char *ret; + + ret = parse_special((char **) &text, dest->server, + dest->target == NULL ? NULL : + window_item_find(dest->server, dest->target), + arglist, &need_free, NULL, 0); + + if (ret != NULL) { + /* string shouldn't end with \003 or it could + mess up the next one or two characters */ + int diff; + int len = strlen(ret); + while (len > 0 && ret[len-1] == 3) len--; + diff = strlen(ret)-len; + + g_string_append(out, ret); + if (diff > 0) + g_string_truncate(out, out->len-diff); + if (need_free) g_free(ret); + } + code = 0; + } else { + if (*text == '%' || *text == '$') + code = *text; + else + g_string_append_c(out, *text); + } + + text++; + } + + ret = out->str; + g_string_free(out, FALSE); + return ret; +} + +char *format_get_text_theme(THEME_REC *theme, const char *module, + TEXT_DEST_REC *dest, int formatnum, ...) +{ + va_list va; + char *str; + + if (theme == NULL) + theme = window_get_theme(dest->window); + + va_start(va, formatnum); + str = format_get_text_theme_args(theme, module, dest, formatnum, va); + va_end(va); + + return str; +} + +char *format_get_text_theme_args(THEME_REC *theme, const char *module, + TEXT_DEST_REC *dest, int formatnum, + va_list va) +{ + char *arglist[MAX_FORMAT_PARAMS]; + char buffer[DEFAULT_FORMAT_ARGLIST_SIZE]; + FORMAT_REC *formats; + + formats = g_hash_table_lookup(default_formats, module); + format_read_arglist(va, &formats[formatnum], + arglist, sizeof(arglist)/sizeof(char *), + buffer, sizeof(buffer)); + + return format_get_text_theme_charargs(theme, module, dest, + formatnum, arglist); +} + +char *format_get_text_theme_charargs(THEME_REC *theme, const char *module, + TEXT_DEST_REC *dest, int formatnum, + char **args) +{ + MODULE_THEME_REC *module_theme; + char *text; + + if (module == NULL) + return NULL; + + module_theme = g_hash_table_lookup(theme->modules, module); + if (module_theme == NULL) + return NULL; + + text = module_theme->expanded_formats[formatnum]; + return format_get_text_args(dest, text, args); +} + +char *format_get_text(const char *module, WINDOW_REC *window, + void *server, const char *target, + int formatnum, ...) +{ + TEXT_DEST_REC dest; + THEME_REC *theme; + va_list va; + char *str; + + format_create_dest(&dest, server, target, 0, window); + theme = window_get_theme(dest.window); + + va_start(va, formatnum); + str = format_get_text_theme_args(theme, module, &dest, formatnum, va); + va_end(va); + + return str; +} + +/* add `linestart' to start of each line in `text'. `text' may contain + multiple lines separated with \n. */ +char *format_add_linestart(const char *text, const char *linestart) +{ + GString *str; + char *ret; + + if (linestart == NULL) + return g_strdup(text); + + if (strchr(text, '\n') == NULL) + return g_strconcat(linestart, text, NULL); + + str = g_string_new(linestart); + while (*text != '\0') { + g_string_append_c(str, *text); + if (*text == '\n') + g_string_append(str, linestart); + text++; + } + + ret = str->str; + g_string_free(str, FALSE); + return ret; +} + +char *format_add_lineend(const char *text, const char *linestart) +{ + GString *str; + char *ret; + + if (linestart == NULL) + return g_strdup(text); + + if (strchr(text, '\n') == NULL) + return g_strconcat(text, linestart, NULL); + + str = g_string_new(NULL); + while (*text != '\0') { + if (*text == '\n') + g_string_append(str, linestart); + g_string_append_c(str, *text); + text++; + } + g_string_append(str, linestart); + + ret = str->str; + g_string_free(str, FALSE); + return ret; +} + +#define LINE_START_IRSSI_LEVEL \ + (MSGLEVEL_CLIENTERROR | MSGLEVEL_CLIENTNOTICE) + +#define NOT_LINE_START_LEVEL \ + (MSGLEVEL_NEVER | MSGLEVEL_LASTLOG | MSGLEVEL_CLIENTCRAP | \ + MSGLEVEL_MSGS | MSGLEVEL_PUBLIC | MSGLEVEL_DCC | MSGLEVEL_DCCMSGS | \ + MSGLEVEL_ACTIONS | MSGLEVEL_NOTICES | MSGLEVEL_SNOTES | MSGLEVEL_CTCPS) + +/* return the "-!- " text at the start of the line */ +char *format_get_level_tag(THEME_REC *theme, TEXT_DEST_REC *dest) +{ + int format; + + /* check for flags if we want to override defaults */ + if (dest->flags & PRINT_FLAG_UNSET_LINE_START) + return NULL; + + if (dest->flags & PRINT_FLAG_SET_LINE_START) + format = TXT_LINE_START; + else if (dest->flags & PRINT_FLAG_SET_LINE_START_IRSSI) + format = TXT_LINE_START_IRSSI; + else { + /* use defaults */ + if (dest->level & LINE_START_IRSSI_LEVEL) + format = TXT_LINE_START_IRSSI; + else if ((dest->level & NOT_LINE_START_LEVEL) == 0) + format = TXT_LINE_START; + else + return NULL; + } + + return format_get_text_theme(theme, MODULE_NAME, dest, format); +} + +static char *get_timestamp(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t) +{ + char *format, str[256]; + struct tm *tm; + int diff; + + if ((timestamp_level & dest->level) == 0) + return NULL; + + /* check for flags if we want to override defaults */ + if (dest->flags & PRINT_FLAG_UNSET_TIMESTAMP) + return NULL; + + if ((dest->flags & PRINT_FLAG_SET_TIMESTAMP) == 0 && + (dest->level & (MSGLEVEL_NEVER|MSGLEVEL_LASTLOG)) != 0) + return NULL; + + + if (timestamp_timeout > 0) { + diff = t - dest->window->last_timestamp; + dest->window->last_timestamp = t; + if (diff < timestamp_timeout) + return NULL; + } + + tm = localtime(&t); + format = format_get_text_theme(theme, MODULE_NAME, dest, + TXT_TIMESTAMP); + if (strftime(str, sizeof(str), format, tm) <= 0) + str[0] = '\0'; + g_free(format); + return g_strdup(str); +} + +static char *get_server_tag(THEME_REC *theme, TEXT_DEST_REC *dest) +{ + int count = 0; + + if (dest->server_tag == NULL || hide_server_tags) + return NULL; + + /* check for flags if we want to override defaults */ + if (dest->flags & PRINT_FLAG_UNSET_SERVERTAG) + return NULL; + + if ((dest->flags & PRINT_FLAG_SET_SERVERTAG) == 0) { + if (dest->window->active != NULL && + dest->window->active->server == dest->server) + return NULL; + + if (servers != NULL) { + count++; + if (servers->next != NULL) + count++; + } + if (count < 2 && lookup_servers != NULL) { + count++; + if (lookup_servers->next != NULL) + count++; + } + + if (count < 2) + return NULL; + } + + return format_get_text_theme(theme, MODULE_NAME, dest, + TXT_SERVERTAG, dest->server_tag); +} + +char *format_get_line_start(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t) +{ + char *timestamp, *servertag; + char *linestart; + + timestamp = get_timestamp(theme, dest, t); + servertag = get_server_tag(theme, dest); + + if (timestamp == NULL && servertag == NULL) + return NULL; + + linestart = g_strconcat(timestamp != NULL ? timestamp : "", + servertag, NULL); + + g_free_not_null(timestamp); + g_free_not_null(servertag); + return linestart; +} + +void format_newline(TEXT_DEST_REC *dest) +{ + g_return_if_fail(dest != NULL); + g_return_if_fail(dest->window != NULL); + + signal_emit_id(signal_gui_print_text, 6, dest->window, GINT_TO_POINTER(-1), + GINT_TO_POINTER(-1), GINT_TO_POINTER(GUI_PRINT_FLAG_NEWLINE), "", dest); +} + +#ifndef TERM_TRUECOLOR +inline static int color_24bit_256_int(unsigned int color) +{ + unsigned char rgb[] = { color >> 16, color >> 8, color }; + return color_24bit_256(rgb); +} +#endif /* !TERM_TRUECOLOR */ + +/* parse ANSI color string */ +static const char *get_ansi_color(THEME_REC *theme, const char *str, + int *fg_ret, int *bg_ret, int *flags_ret) +{ + static char ansitab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + const char *start; + char *endptr; + int fg, bg, flags, i; + guint num, num2; + + if (*str != '[') + return str; + start = str++; + + fg = fg_ret == NULL || *fg_ret < 0 ? theme->default_color : *fg_ret; + bg = bg_ret == NULL || *bg_ret < 0 ? -1 : *bg_ret; + flags = flags_ret == NULL ? 0 : *flags_ret; + + num = 0; + for (;; str++) { + if (*str == '\0') return start; + + if (i_isdigit(*str)) { + if (!parse_uint(str, &endptr, 10, &num)) { + return start; + } + str = endptr; + } + + if (*str != ';' && *str != 'm') + return start; + + switch (num) { + case 0: + /* reset colors and attributes back to default */ + fg = theme->default_color; + bg = -1; + flags &= ~(GUI_PRINT_FLAG_INDENT | + GUI_PRINT_FLAG_BOLD | GUI_PRINT_FLAG_ITALIC | GUI_PRINT_FLAG_UNDERLINE | + GUI_PRINT_FLAG_BLINK | GUI_PRINT_FLAG_REVERSE | + GUI_PRINT_FLAG_COLOR_24_FG | GUI_PRINT_FLAG_COLOR_24_BG); + break; + case 1: + /* hilight */ + flags |= GUI_PRINT_FLAG_BOLD; + break; + case 22: + /* normal */ + flags &= ~GUI_PRINT_FLAG_BOLD; + break; + case 3: + /* italic */ + flags |= GUI_PRINT_FLAG_ITALIC; + break; + case 23: + /* not italic */ + flags &= ~GUI_PRINT_FLAG_ITALIC; + break; + case 4: + /* underline */ + flags |= GUI_PRINT_FLAG_UNDERLINE; + break; + case 24: + /* not underline */ + flags &= ~GUI_PRINT_FLAG_UNDERLINE; + break; + case 5: + /* blink */ + flags |= GUI_PRINT_FLAG_BLINK; + break; + case 25: + /* steady */ + flags &= ~GUI_PRINT_FLAG_BLINK; + break; + case 7: + /* reverse */ + flags |= GUI_PRINT_FLAG_REVERSE; + break; + case 27: + /* positive */ + flags &= ~GUI_PRINT_FLAG_REVERSE; + break; + case 39: + /* reset fg */ + flags &= ~GUI_PRINT_FLAG_COLOR_24_FG; + fg = theme->default_color; + break; + case 49: + /* reset bg */ + bg = -1; + flags &= ~(GUI_PRINT_FLAG_COLOR_24_BG | GUI_PRINT_FLAG_INDENT); + break; + case 38: + case 48: + /* ANSI indexed color or RGB color */ + if (*str != ';') break; + str++; + + if (!parse_uint(str, &endptr, 10, &num2)) { + return start; + } + str = endptr; + + if (*str == '\0') return start; + + switch (num2) { + case 2: + /* RGB */ + num2 = 0; + + for (i = 0; i < 3; ++i) { + num2 <<= 8; + + if (*str != ';' && *str != ':') { + i = -1; + break; + } + str++; + for (; i_isdigit(*str); str++) + num2 = (num2&~0xff) | + (((num2&0xff) * 10 + (*str-'0'))&0xff); + + if (*str == '\0') return start; + } + + if (i == -1) break; +#ifdef TERM_TRUECOLOR + if (num == 38) { + flags |= GUI_PRINT_FLAG_COLOR_24_FG; + fg = num2; + } else if (num == 48) { + flags |= GUI_PRINT_FLAG_COLOR_24_BG; + bg = num2; + } +#else /* !TERM_TRUECOLOR */ + if (num == 38) { + flags &= ~GUI_PRINT_FLAG_COLOR_24_FG; + fg = color_24bit_256_int(num2); + } else if (num == 48) { + flags &= ~GUI_PRINT_FLAG_COLOR_24_BG; + bg = color_24bit_256_int(num2); + } +#endif + + break; + case 5: + /* indexed */ + if (*str != ';') break; + str++; + + if (!parse_uint(str, &endptr, 10, &num2)) { + return start; + } + str = endptr; + + if (*str == '\0') return start; + + if (num == 38) { + flags &= ~GUI_PRINT_FLAG_COLOR_24_FG; + fg = num2; + } else if (num == 48) { + flags &= ~GUI_PRINT_FLAG_COLOR_24_BG; + bg = num2; + } + + break; + } + break; + default: + if (num >= 30 && num <= 37) { + flags &= ~GUI_PRINT_FLAG_COLOR_24_FG; + fg = ansitab[num-30]; + } else if (num >= 40 && num <= 47) { + flags &= ~GUI_PRINT_FLAG_COLOR_24_BG; + bg = ansitab[num-40]; + } else if (num >= 90 && num <= 97) { + flags &= ~GUI_PRINT_FLAG_COLOR_24_FG; + fg = 8 + ansitab[num-90]; + } else if (num >= 100 && num <= 107) { + flags &= ~GUI_PRINT_FLAG_COLOR_24_BG; + bg = 8 + ansitab[num-100]; + } + break; + } + num = 0; + + if (*str == 'm') { + if (fg_ret != NULL) *fg_ret = fg; + if (bg_ret != NULL) *bg_ret = bg; + if (flags_ret != NULL) *flags_ret = flags; + + str++; + break; + } + } + + return str; +} + +/* parse MIRC color string */ +static void get_mirc_color(const char **str, int *fg_ret, int *bg_ret) +{ + int fg, bg; + + fg = fg_ret == NULL ? -1 : *fg_ret; + bg = bg_ret == NULL ? -1 : *bg_ret; + + if (!i_isdigit(**str)) { + /* turn off color */ + fg = -1; + bg = -1; + } else { + /* foreground color */ + fg = **str-'0'; + (*str)++; + if (i_isdigit(**str)) { + fg = fg*10 + (**str-'0'); + (*str)++; + } + + if ((*str)[0] == ',' && i_isdigit((*str)[1])) { + /* background color */ + (*str)++; + bg = **str-'0'; + (*str)++; + if (i_isdigit(**str)) { + bg = bg*10 + (**str-'0'); + (*str)++; + } + } + } + + if (fg_ret) *fg_ret = fg; + if (bg_ret) *bg_ret = bg; +} + +#define IS_COLOR_CODE(c) \ + ((c) == 2 || (c) == 3 || (c) == 4 || (c) == 6 || (c) == 7 || \ + (c) == 15 || (c) == 17 || (c) == 22 || (c) == 27 || (c) == 29 || (c) == 31) + +/* Return how many characters in `str' must be skipped before `len' + characters of text is skipped. */ +int strip_real_length(const char *str, int len, + int *last_color_pos, int *last_color_len) +{ + const char *start = str; + + if (last_color_pos != NULL) + *last_color_pos = -1; + if (last_color_len != NULL) + *last_color_len = -1; + + while (*str != '\0') { + if (*str == 3) { /* mIRC color */ + const char *mircstart = str; + + if (last_color_pos != NULL) + *last_color_pos = (int) (str-start); + str++; + get_mirc_color(&str, NULL, NULL); + if (last_color_len != NULL) + *last_color_len = (int) (str-mircstart); + + } else if (*str == 4 && str[1] != '\0') { + /* We expect 4 to indicate an internal Irssi color code. However 4 + * also means hex color, an alternative to mIRC color codes. We + * don't support those. */ +#ifdef TERM_TRUECOLOR + if (str[1] == FORMAT_COLOR_24 && str[2] != '\0') { + if (str[3] == '\0') str++; + else if (str[4] == '\0') str += 2; + else if (str[5] == '\0') str += 3; + else { + if (last_color_pos != NULL) + *last_color_pos = (int) (str-start); + if (last_color_len != NULL) + *last_color_len = 6; + str+=4; + } + } else +#endif + if (str[1] < FORMAT_STYLE_SPECIAL && str[2] != '\0') { + if (last_color_pos != NULL) + *last_color_pos = (int) (str-start); + if (last_color_len != NULL) + *last_color_len = 3; + str++; + } else if (str[1] == FORMAT_STYLE_DEFAULTS) { + if (last_color_pos != NULL) + *last_color_pos = (int) (str-start); + if (last_color_len != NULL) + *last_color_len = 2; + } + str += 2; + } else { + if (!IS_COLOR_CODE(*str)) { + if (len-- == 0) + break; + } + str++; + } + } + + return (int) (str-start); +} + +char *strip_codes(const char *input) +{ + const char *p; + char *str, *out; + + out = str = g_strdup(input); + for (p = input; *p != '\0'; p++) { + if (*p == 3) { + p++; + + /* mirc color */ + get_mirc_color(&p, NULL, NULL); + p--; + continue; + } + + if (*p == 4 && p[1] != '\0') { + if (p[1] >= FORMAT_STYLE_SPECIAL) { + p++; + continue; + } + + /* irssi color */ + if (p[2] != '\0') { +#ifdef TERM_TRUECOLOR + if (p[1] == FORMAT_COLOR_24) { + if (p[3] == '\0') p += 2; + else if (p[4] == '\0') p += 3; + else if (p[5] == '\0') p += 4; + else p += 5; + } else +#endif /* TERM_TRUECOLOR */ + p += 2; + continue; + } + } + + if (*p == 27 && p[1] != '\0') { + p++; + p = get_ansi_color(current_theme, p, NULL, NULL, NULL); + p--; + } else if (!IS_COLOR_CODE(*p)) + *out++ = *p; + } + + *out = '\0'; + return str; +} + +/* parse text string into GUI_PRINT_FLAG_* separated pieces and emit them to handler + handler is a SIGNAL_FUNC with the following arguments: + + WINDOW_REC *window, void *fgcolor_int, void *bgcolor_int, + void *flags_int, const char *textpiece, TEXT_DEST_REC *dest + + */ +void format_send_as_gui_flags(TEXT_DEST_REC *dest, const char *text, SIGNAL_FUNC handler) +{ + THEME_REC *theme; + char *dup, *str, *ptr, type; + int fgcolor, bgcolor; + int flags; + + theme = window_get_theme(dest->window); + + dup = str = g_strdup(text); + + flags = 0; fgcolor = theme->default_color; bgcolor = -1; + + if (*str == '\0') { + /* empty line, write line info only */ + handler(dest->window, GINT_TO_POINTER(fgcolor), GINT_TO_POINTER(bgcolor), + GINT_TO_POINTER(flags), str, dest); + } + + while (*str != '\0') { + type = '\0'; + for (ptr = str; *ptr != '\0'; ptr++) { + if (IS_COLOR_CODE(*ptr) || *ptr == '\n') { + type = *ptr; + *ptr++ = '\0'; + break; + } + } + + if (type == 4 && *ptr == FORMAT_STYLE_CLRTOEOL) { + /* clear to end of line */ + flags |= GUI_PRINT_FLAG_CLRTOEOL; + } + + if (*str != '\0' || (flags & GUI_PRINT_FLAG_CLRTOEOL)) { + /* send the text to gui handler */ + handler(dest->window, GINT_TO_POINTER(fgcolor), GINT_TO_POINTER(bgcolor), + GINT_TO_POINTER(flags), str, dest); + flags &= ~(GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_CLRTOEOL); + } + + if (type == '\n') { + handler(dest->window, GINT_TO_POINTER(-1), GINT_TO_POINTER(-1), + GINT_TO_POINTER(GUI_PRINT_FLAG_NEWLINE), "", dest); + fgcolor = theme->default_color; + bgcolor = -1; + flags &= GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_MONOSPACE; + } + + if (*ptr == '\0') + break; + + switch (type) + { + case 2: + /* bold */ + if (!hide_text_style) + flags ^= GUI_PRINT_FLAG_BOLD; + break; + case 3: + /* MIRC color */ + get_mirc_color((const char **) &ptr, + hide_colors ? NULL : &fgcolor, + hide_colors ? NULL : &bgcolor); + if (!hide_colors) + flags |= GUI_PRINT_FLAG_MIRC_COLOR; + break; + case 4: + /* user specific colors */ + flags &= ~GUI_PRINT_FLAG_MIRC_COLOR; + switch (*ptr) { + case FORMAT_STYLE_BLINK: + flags ^= GUI_PRINT_FLAG_BLINK; + break; + case FORMAT_STYLE_UNDERLINE: + flags ^= GUI_PRINT_FLAG_UNDERLINE; + break; + case FORMAT_STYLE_BOLD: + flags ^= GUI_PRINT_FLAG_BOLD; + break; + case FORMAT_STYLE_REVERSE: + flags ^= GUI_PRINT_FLAG_REVERSE; + break; + case FORMAT_STYLE_ITALIC: + flags ^= GUI_PRINT_FLAG_ITALIC; + break; + case FORMAT_STYLE_MONOSPACE: + flags ^= GUI_PRINT_FLAG_MONOSPACE; + break; + case FORMAT_STYLE_INDENT: + flags |= GUI_PRINT_FLAG_INDENT; + break; + case FORMAT_STYLE_DEFAULTS: + fgcolor = theme->default_color; + bgcolor = -1; + flags &= GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_MONOSPACE; + break; + case FORMAT_STYLE_CLRTOEOL: + break; + case FORMAT_COLOR_EXT1: + fgcolor = 0x10 + *++ptr - FORMAT_COLOR_NOCHANGE; + flags &= ~GUI_PRINT_FLAG_COLOR_24_FG; + break; + case FORMAT_COLOR_EXT1_BG: + bgcolor = 0x10 + *++ptr - FORMAT_COLOR_NOCHANGE; + flags &= ~GUI_PRINT_FLAG_COLOR_24_BG; + break; + case FORMAT_COLOR_EXT2: + fgcolor = 0x60 + *++ptr - FORMAT_COLOR_NOCHANGE; + flags &= ~GUI_PRINT_FLAG_COLOR_24_FG; + break; + case FORMAT_COLOR_EXT2_BG: + bgcolor = 0x60 + *++ptr - FORMAT_COLOR_NOCHANGE; + flags &= ~GUI_PRINT_FLAG_COLOR_24_BG; + break; + case FORMAT_COLOR_EXT3: + fgcolor = 0xb0 + *++ptr - FORMAT_COLOR_NOCHANGE; + flags &= ~GUI_PRINT_FLAG_COLOR_24_FG; + break; + case FORMAT_COLOR_EXT3_BG: + bgcolor = 0xb0 + *++ptr - FORMAT_COLOR_NOCHANGE; + flags &= ~GUI_PRINT_FLAG_COLOR_24_BG; + break; +#ifdef TERM_TRUECOLOR + case FORMAT_COLOR_24: + unformat_24bit_color(&ptr, 1, &fgcolor, &bgcolor, &flags); + break; +#endif + default: + if (*ptr != FORMAT_COLOR_NOCHANGE) { + flags &= ~GUI_PRINT_FLAG_COLOR_24_FG; + fgcolor = *ptr==(char)0xff ? -1 : (unsigned char) *ptr-'0'; + } + if (ptr[1] == '\0') + break; + + ptr++; + if (*ptr != FORMAT_COLOR_NOCHANGE) { + flags &= ~GUI_PRINT_FLAG_COLOR_24_BG; + bgcolor = *ptr==(char)0xff ? -1 : *ptr-'0'; + } + } + if (*ptr == '\0') + break; + + ptr++; + break; + case 6: + /* blink */ + if (!hide_text_style) + flags ^= GUI_PRINT_FLAG_BLINK; + break; + case 15: + /* remove all styling */ + fgcolor = theme->default_color; + bgcolor = -1; + flags &= GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_MONOSPACE; + break; + case 17: + if (!hide_text_style) + flags ^= GUI_PRINT_FLAG_MONOSPACE; + break; + case 22: + /* reverse */ + if (!hide_text_style) + flags ^= GUI_PRINT_FLAG_REVERSE; + break; + case 29: + /* italic */ + if (!hide_text_style) + flags ^= GUI_PRINT_FLAG_ITALIC; + break; + case 31: + /* underline */ + if (!hide_text_style) + flags ^= GUI_PRINT_FLAG_UNDERLINE; + break; + case 27: + /* ansi color code */ + ptr = (char *) + get_ansi_color(theme, ptr, + hide_colors ? NULL : &fgcolor, + hide_colors ? NULL : &bgcolor, + hide_colors ? NULL : &flags); + break; + } + + str = ptr; + } + + g_free(dup); +} + +inline static void gui_print_text_emitter(WINDOW_REC *window, void *fgcolor_int, void *bgcolor_int, + void *flags_int, const char *textpiece, + TEXT_DEST_REC *dest) +{ + signal_emit_id(signal_gui_print_text, 6, window, fgcolor_int, bgcolor_int, flags_int, + textpiece, dest); +} + +/* send a fully parsed text string for GUI to print */ +void format_send_to_gui(TEXT_DEST_REC *dest, const char *text) +{ + format_send_as_gui_flags(dest, text, (SIGNAL_FUNC) gui_print_text_emitter); +} + +void format_gui_flags(GString *out, int *last_fg, int *last_bg, int *last_flags, int fg, int bg, + int flags) +{ + if (fg != *last_fg || + (flags & GUI_PRINT_FLAG_COLOR_24_FG) != (*last_flags & GUI_PRINT_FLAG_COLOR_24_FG)) { + *last_fg = fg; + +#ifdef TERM_TRUECOLOR + if (flags & GUI_PRINT_FLAG_COLOR_24_FG) { + *last_flags |= GUI_PRINT_FLAG_COLOR_24_FG; + format_24bit_color(out, 0, fg); + } else { + *last_flags &= ~GUI_PRINT_FLAG_COLOR_24_FG; +#endif + if (fg < 0) { + g_string_append_c(out, 4); + g_string_append_c(out, (char) -1); + g_string_append_c(out, FORMAT_COLOR_NOCHANGE); + } else { + format_ext_color(out, 0, fg); + } +#ifdef TERM_TRUECOLOR + } +#endif + } + if (bg != *last_bg || + (flags & GUI_PRINT_FLAG_COLOR_24_BG) != (*last_flags & GUI_PRINT_FLAG_COLOR_24_BG)) { + *last_bg = bg; +#ifdef TERM_TRUECOLOR + if (flags & GUI_PRINT_FLAG_COLOR_24_BG) { + *last_flags |= GUI_PRINT_FLAG_COLOR_24_BG; + format_24bit_color(out, 1, bg); + } else { + *last_flags &= ~GUI_PRINT_FLAG_COLOR_24_BG; +#endif + if (bg < 0) { + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_COLOR_NOCHANGE); + g_string_append_c(out, (char) -1); + } else { + format_ext_color(out, 1, bg); + } +#ifdef TERM_TRUECOLOR + } +#endif + } + + if ((flags & GUI_PRINT_FLAG_UNDERLINE) != (*last_flags & GUI_PRINT_FLAG_UNDERLINE)) { + *last_flags ^= GUI_PRINT_FLAG_UNDERLINE; + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_UNDERLINE); + } + if ((flags & GUI_PRINT_FLAG_REVERSE) != (*last_flags & GUI_PRINT_FLAG_REVERSE)) { + *last_flags ^= GUI_PRINT_FLAG_REVERSE; + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_REVERSE); + } + if ((flags & GUI_PRINT_FLAG_BLINK) != (*last_flags & GUI_PRINT_FLAG_BLINK)) { + *last_flags ^= GUI_PRINT_FLAG_BLINK; + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_BLINK); + } + if ((flags & GUI_PRINT_FLAG_BOLD) != (*last_flags & GUI_PRINT_FLAG_BOLD)) { + *last_flags ^= GUI_PRINT_FLAG_BOLD; + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_BOLD); + } + if ((flags & GUI_PRINT_FLAG_ITALIC) != (*last_flags & GUI_PRINT_FLAG_ITALIC)) { + *last_flags ^= GUI_PRINT_FLAG_ITALIC; + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_ITALIC); + } + if ((flags & GUI_PRINT_FLAG_MONOSPACE) != (*last_flags & GUI_PRINT_FLAG_MONOSPACE)) { + *last_flags ^= GUI_PRINT_FLAG_MONOSPACE; + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_MONOSPACE); + } + if (flags & GUI_PRINT_FLAG_INDENT) { + *last_flags ^= GUI_PRINT_FLAG_INDENT; + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_INDENT); + } +} + +static void read_settings(void) +{ + timestamp_level = settings_get_bool("timestamps") ? MSGLEVEL_ALL : 0; + if (timestamp_level > 0) + timestamp_level = settings_get_level("timestamp_level"); + timestamp_timeout = settings_get_time("timestamp_timeout")/1000; + + hide_server_tags = settings_get_bool("hide_server_tags"); + hide_text_style = settings_get_bool("hide_text_style"); + hide_colors = hide_text_style || settings_get_bool("hide_colors"); +} + +void formats_init(void) +{ + signal_gui_print_text = signal_get_uniq_id("gui print text"); + global_meta = + g_hash_table_new_full(g_str_hash, (GEqualFunc) g_str_equal, + (GDestroyNotify) i_refstr_release, (GDestroyNotify) g_free); + + read_settings(); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); + signal_add_last("gui print text finished", (SIGNAL_FUNC) clear_global_meta); +} + +void formats_deinit(void) +{ + g_hash_table_destroy(global_meta); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); + signal_remove("gui print text finished", (SIGNAL_FUNC) clear_global_meta); +} diff --git a/src/fe-common/core/formats.h b/src/fe-common/core/formats.h new file mode 100644 index 0000000..5666c73 --- /dev/null +++ b/src/fe-common/core/formats.h @@ -0,0 +1,184 @@ +#ifndef IRSSI_FE_COMMON_CORE_FORMATS_H +#define IRSSI_FE_COMMON_CORE_FORMATS_H + +#include <irssi/src/core/signals.h> +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/core/themes.h> + +#define GUI_PRINT_FLAG_BOLD 0x0001 +#define GUI_PRINT_FLAG_REVERSE 0x0002 +#define GUI_PRINT_FLAG_UNDERLINE 0x0004 +#define GUI_PRINT_FLAG_BLINK 0x0008 +#define GUI_PRINT_FLAG_MIRC_COLOR 0x0010 +#define GUI_PRINT_FLAG_INDENT 0x0020 +#define GUI_PRINT_FLAG_ITALIC 0x0040 +#define GUI_PRINT_FLAG_NEWLINE 0x0080 +#define GUI_PRINT_FLAG_CLRTOEOL 0x0100 +#define GUI_PRINT_FLAG_MONOSPACE 0x0200 +#define GUI_PRINT_FLAG_COLOR_24_FG 0x0400 +#define GUI_PRINT_FLAG_COLOR_24_BG 0x0800 + +#define MAX_FORMAT_PARAMS 10 +#define DEFAULT_FORMAT_ARGLIST_SIZE 200 + +enum { + FORMAT_STRING, + FORMAT_INT, + FORMAT_LONG, + FORMAT_FLOAT +}; + +struct _FORMAT_REC { + char *tag; + char *def; + + int params; + int paramtypes[MAX_FORMAT_PARAMS]; +}; + +/* clang-format off */ +#define PRINT_FLAG_SET_LINE_START 0x0001 +#define PRINT_FLAG_SET_LINE_START_IRSSI 0x0002 +#define PRINT_FLAG_UNSET_LINE_START 0x0040 + +#define PRINT_FLAG_SET_TIMESTAMP 0x0004 +#define PRINT_FLAG_UNSET_TIMESTAMP 0x0008 + +#define PRINT_FLAG_SET_SERVERTAG 0x0010 +#define PRINT_FLAG_UNSET_SERVERTAG 0x0020 + +#define PRINT_FLAG_FORMAT 0x0080 +/* clang-format on */ + +typedef struct _HILIGHT_REC HILIGHT_REC; + +typedef struct _TEXT_DEST_REC { + WINDOW_REC *window; + SERVER_REC *server; + const char *server_tag; /* if server is non-NULL, must be server->tag */ + const char *target; + const char *nick; + const char *address; + int level; + + int hilight_priority; + char *hilight_color; + int flags; /* PRINT_FLAG */ + GHashTable *meta; +} TEXT_DEST_REC; + +typedef struct _LINE_INFO_META_REC { + gint64 server_time; + GHashTable *hash; +} LINE_INFO_META_REC; + +#define window_get_theme(window) \ + (window != NULL && (window)->theme != NULL ? \ + (window)->theme : current_theme) + +int format_find_tag(const char *module, const char *tag); + +/* Return length of text part in string (ie. without % codes) */ +int format_get_length(const char *str); +/* Return how many characters in `str' must be skipped before `len' + characters of text is skipped. Like strip_real_length(), except this + handles %codes. */ +int format_real_length(const char *str, int len); + +char *format_string_expand(const char *text, int *flags); +char *format_string_unexpand(const char *text, int flags); + +char *format_get_text(const char *module, WINDOW_REC *window, + void *server, const char *target, + int formatnum, ...); + +/* good size for buffer is DEFAULT_FORMAT_ARGLIST_SIZE */ +void format_read_arglist(va_list va, FORMAT_REC *format, + char **arglist, int arglist_size, + char *buffer, int buffer_size); +char *format_get_text_theme(THEME_REC *theme, const char *module, + TEXT_DEST_REC *dest, int formatnum, ...); +char *format_get_text_theme_args(THEME_REC *theme, const char *module, + TEXT_DEST_REC *dest, int formatnum, + va_list va); +char *format_get_text_theme_charargs(THEME_REC *theme, const char *module, + TEXT_DEST_REC *dest, int formatnum, + char **args); + +/* add `linestart' to start/end of each line in `text'. `text' may contain + multiple lines separated with \n. */ +char *format_add_linestart(const char *text, const char *linestart); +char *format_add_lineend(const char *text, const char *linestart); + +/* return the "-!- " text at the start of the line */ +char *format_get_level_tag(THEME_REC *theme, TEXT_DEST_REC *dest); + +/* return timestamp + server tag */ +char *format_get_line_start(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t); + + +/* "private" functions for printtext */ +void format_create_dest(TEXT_DEST_REC *dest, + void *server, const char *target, + int level, WINDOW_REC *window); +void format_create_dest_tag(TEXT_DEST_REC *dest, void *server, const char *server_tag, + const char *target, int level, WINDOW_REC *window); + +void format_newline(TEXT_DEST_REC *dest); + +/* manipulate the meta table of a dest */ +void format_dest_meta_stash(TEXT_DEST_REC *dest, const char *meta_key, const char *meta_value); +const char *format_dest_meta_stash_find(TEXT_DEST_REC *dest, const char *meta_key); +void format_dest_meta_clear_all(TEXT_DEST_REC *dest); + +/* Return how many characters in `str' must be skipped before `len' + characters of text is skipped. */ +int strip_real_length(const char *str, int len, + int *last_color_pos, int *last_color_len); + +/* strip all color (etc.) codes from `input'. + Returns newly allocated string. */ +char *strip_codes(const char *input); + +/* send a fully parsed text string for GUI to print */ +void format_send_to_gui(TEXT_DEST_REC *dest, const char *text); +/* parse text string into GUI_PRINT_FLAG_* separated pieces and emit them to handler + handler is a SIGNAL_FUNC with the following arguments: + + WINDOW_REC *window, void *fgcolor_int, void *bgcolor_int, + void *flags_int, const char *textpiece, TEXT_DEST_REC *dest + + */ +void format_send_as_gui_flags(TEXT_DEST_REC *dest, const char *text, SIGNAL_FUNC handler); + +#define FORMAT_COLOR_NOCHANGE ('0'-1) /* don't change this, at least hilighting depends this value */ +#define FORMAT_COLOR_EXT1 ('0'-2) +#define FORMAT_COLOR_EXT2 ('0'-3) +#define FORMAT_COLOR_EXT3 ('0'-4) +#define FORMAT_COLOR_EXT1_BG ('0'-5) +#define FORMAT_COLOR_EXT2_BG ('0'-9) +#define FORMAT_COLOR_EXT3_BG ('0'-10) +#ifdef TERM_TRUECOLOR +#define FORMAT_COLOR_24 ('0'-13) +#endif + +#define FORMAT_STYLE_SPECIAL 0x60 +#define FORMAT_STYLE_BLINK (0x01 + FORMAT_STYLE_SPECIAL) +#define FORMAT_STYLE_UNDERLINE (0x02 + FORMAT_STYLE_SPECIAL) +#define FORMAT_STYLE_BOLD (0x03 + FORMAT_STYLE_SPECIAL) +#define FORMAT_STYLE_REVERSE (0x04 + FORMAT_STYLE_SPECIAL) +#define FORMAT_STYLE_INDENT (0x05 + FORMAT_STYLE_SPECIAL) +#define FORMAT_STYLE_ITALIC (0x06 + FORMAT_STYLE_SPECIAL) +#define FORMAT_STYLE_DEFAULTS (0x07 + FORMAT_STYLE_SPECIAL) +#define FORMAT_STYLE_CLRTOEOL (0x08 + FORMAT_STYLE_SPECIAL) +#define FORMAT_STYLE_MONOSPACE (0x09 + FORMAT_STYLE_SPECIAL) +int format_expand_styles(GString *out, const char **format, int *flags); +void format_ext_color(GString *out, int bg, int color); +void format_24bit_color(GString *out, int bg, unsigned int color); +void format_gui_flags(GString *out, int *last_fg, int *last_bg, int *last_flags, int fg, int bg, + int flags); + +void formats_init(void); +void formats_deinit(void); + +#endif diff --git a/src/fe-common/core/hilight-text.c b/src/fe-common/core/hilight-text.c new file mode 100644 index 0000000..66e2dfd --- /dev/null +++ b/src/fe-common/core/hilight-text.c @@ -0,0 +1,821 @@ +/* + hilight-text.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/lib-config/iconfig.h> +#include <irssi/src/core/settings.h> +#include <irssi/src/core/iregex.h> + +#include <irssi/src/core/servers.h> +#include <irssi/src/core/channels.h> +#include <irssi/src/core/nicklist.h> + +#include <irssi/src/fe-common/core/hilight-text.h> +#include <irssi/src/core/nickmatch-cache.h> +#include <irssi/src/fe-common/core/printtext.h> +#include <irssi/src/fe-common/core/formats.h> + +static NICKMATCH_REC *nickmatch; +static int never_hilight_level, default_hilight_level; +GSList *hilights; + +static void reset_level_cache(void) +{ + GSList *tmp; + + never_hilight_level = MSGLEVEL_ALL & ~default_hilight_level; + for (tmp = hilights; tmp != NULL; tmp = tmp->next) { + HILIGHT_REC *rec = tmp->data; + + if (never_hilight_level & rec->level) + never_hilight_level &= ~rec->level; + } +} + +static void reset_cache(void) +{ + reset_level_cache(); + nickmatch_rebuild(nickmatch); +} + +static void hilight_add_config(HILIGHT_REC *rec) +{ + CONFIG_NODE *node; + + g_return_if_fail(rec != NULL); + + node = iconfig_node_traverse("(hilights", TRUE); + node = iconfig_node_section(node, NULL, NODE_TYPE_BLOCK); + + iconfig_node_set_str(node, "text", rec->text); + if (rec->level > 0) iconfig_node_set_int(node, "level", rec->level); + if (rec->color) iconfig_node_set_str(node, "color", rec->color); + if (rec->act_color) iconfig_node_set_str(node, "act_color", rec->act_color); + if (rec->priority > 0) iconfig_node_set_int(node, "priority", rec->priority); + iconfig_node_set_bool(node, "nick", rec->nick); + iconfig_node_set_bool(node, "word", rec->word); + if (rec->nickmask) iconfig_node_set_bool(node, "mask", TRUE); + if (rec->fullword) iconfig_node_set_bool(node, "fullword", TRUE); + if (rec->regexp) iconfig_node_set_bool(node, "regexp", TRUE); + if (rec->case_sensitive) iconfig_node_set_bool(node, "matchcase", TRUE); + if (rec->servertag) iconfig_node_set_str(node, "servertag", rec->servertag); + + if (rec->channels != NULL && *rec->channels != NULL) { + node = iconfig_node_section(node, "channels", NODE_TYPE_LIST); + iconfig_node_add_list(node, rec->channels); + } +} + +static void hilight_remove_config(HILIGHT_REC *rec) +{ + CONFIG_NODE *node; + + g_return_if_fail(rec != NULL); + + node = iconfig_node_traverse("hilights", FALSE); + if (node != NULL) iconfig_node_list_remove(node, g_slist_index(hilights, rec)); +} + +static void hilight_destroy(HILIGHT_REC *rec) +{ + g_return_if_fail(rec != NULL); + + if (rec->preg != NULL) i_regex_unref(rec->preg); + if (rec->channels != NULL) g_strfreev(rec->channels); + g_free_not_null(rec->color); + g_free_not_null(rec->act_color); + g_free_not_null(rec->servertag); + g_free(rec->text); + g_free(rec); +} + +static void hilights_destroy_all(void) +{ + g_slist_foreach(hilights, (GFunc) hilight_destroy, NULL); + g_slist_free(hilights); + hilights = NULL; +} + +static void hilight_init_rec(HILIGHT_REC *rec) +{ + if (rec->preg != NULL) + i_regex_unref(rec->preg); + + rec->preg = i_regex_new(rec->text, G_REGEX_OPTIMIZE | G_REGEX_CASELESS, 0, NULL); +} + +void hilight_create(HILIGHT_REC *rec) +{ + if (g_slist_find(hilights, rec) != NULL) { + hilight_remove_config(rec); + hilights = g_slist_remove(hilights, rec); + } + + hilights = g_slist_append(hilights, rec); + hilight_add_config(rec); + + hilight_init_rec(rec); + + signal_emit("hilight created", 1, rec); +} + +void hilight_remove(HILIGHT_REC *rec) +{ + g_return_if_fail(rec != NULL); + + hilight_remove_config(rec); + hilights = g_slist_remove(hilights, rec); + + signal_emit("hilight destroyed", 1, rec); + hilight_destroy(rec); +} + +static HILIGHT_REC *hilight_find(const char *text, char **channels) +{ + GSList *tmp; + char **chan; + + g_return_val_if_fail(text != NULL, NULL); + + for (tmp = hilights; tmp != NULL; tmp = tmp->next) { + HILIGHT_REC *rec = tmp->data; + + if (g_ascii_strcasecmp(rec->text, text) != 0) + continue; + + if ((channels == NULL && rec->channels == NULL)) + return rec; /* no channels - ok */ + + if (channels != NULL && g_strcmp0(*channels, "*") == 0) + return rec; /* ignore channels */ + + if (channels == NULL || rec->channels == NULL) + continue; /* other doesn't have channels */ + + if (g_strv_length(channels) != g_strv_length(rec->channels)) + continue; /* different amount of channels */ + + /* check that channels match */ + for (chan = channels; *chan != NULL; chan++) { + if (strarray_find(rec->channels, *chan) == -1) + break; + } + + if (*chan == NULL) + return rec; /* channels ok */ + } + + return NULL; +} + +static gboolean hilight_match_text(HILIGHT_REC *rec, const char *text, + int *match_beg, int *match_end) +{ + gboolean ret = FALSE; + + if (rec->regexp) { + if (rec->preg != NULL) { + MatchInfo *match; + i_regex_match(rec->preg, text, 0, &match); + + if (i_match_info_matches(match)) + ret = i_match_info_fetch_pos(match, 0, match_beg, match_end); + + i_match_info_free(match); + } + } else { + char *match; + + if (rec->case_sensitive) { + match = rec->fullword ? + strstr_full(text, rec->text) : + strstr(text, rec->text); + } else { + match = rec->fullword ? + stristr_full(text, rec->text) : + stristr(text, rec->text); + } + if (match != NULL) { + if (match_beg != NULL && match_end != NULL) { + *match_beg = (int) (match-text); + *match_end = *match_beg + strlen(rec->text); + } + ret = TRUE; + } + } + + return ret; +} + +#define hilight_match_level(rec, level) \ + (level & (((rec)->level != 0 ? rec->level : default_hilight_level))) + +#define hilight_match_channel(rec, channel) \ + ((rec)->channels == NULL || ((channel) != NULL && \ + strarray_find((rec)->channels, (channel)) != -1)) + +HILIGHT_REC *hilight_match(SERVER_REC *server, const char *channel, + const char *nick, const char *address, + int level, const char *str, + int *match_beg, int *match_end) +{ + GSList *tmp; + CHANNEL_REC *chanrec; + NICK_REC *nickrec; + HILIGHT_REC *tmprec; + int priority = -1; + + g_return_val_if_fail(str != NULL, NULL); + tmprec = NULL; + + if ((never_hilight_level & level) == level) + return NULL; + + if (nick != NULL) { + /* check nick mask hilights */ + chanrec = channel_find(server, channel); + nickrec = chanrec == NULL ? NULL : + nicklist_find(chanrec, nick); + if (nickrec != NULL) { + HILIGHT_REC *rec; + + if (nickrec->host == NULL) + nicklist_set_host(chanrec, nickrec, address); + + rec = nickmatch_find(nickmatch, nickrec); + if (rec != NULL && hilight_match_level(rec, level)) + return rec; + } + } + + for (tmp = hilights; tmp != NULL; tmp = tmp->next) { + HILIGHT_REC *rec = tmp->data; + + if (rec->priority > priority && !rec->nickmask && hilight_match_level(rec, level) && + hilight_match_channel(rec, channel) && + (rec->servertag == NULL || + (server != NULL && g_ascii_strcasecmp(rec->servertag, server->tag) == 0)) && + hilight_match_text(rec, str, match_beg, match_end)) { + tmprec = rec; + priority = rec->priority; + } + } + + return tmprec; +} + +static char *hilight_get_act_color(HILIGHT_REC *rec) +{ + g_return_val_if_fail(rec != NULL, NULL); + + return g_strdup(rec->act_color != NULL ? rec->act_color : + rec->color != NULL ? rec->color : + settings_get_str("hilight_act_color")); +} + +char *hilight_get_color(HILIGHT_REC *rec) +{ + const char *color; + + g_return_val_if_fail(rec != NULL, NULL); + + color = rec->color != NULL ? rec->color : + settings_get_str("hilight_color"); + + return format_string_expand(color, NULL); +} + +void hilight_update_text_dest(TEXT_DEST_REC *dest, HILIGHT_REC *rec) +{ + dest->level |= MSGLEVEL_HILIGHT; + + if (rec->priority > 0) + dest->hilight_priority = rec->priority; + + g_free_and_null(dest->hilight_color); + if (rec->act_color != NULL && g_strcmp0(rec->act_color, "%n") == 0) + dest->level |= MSGLEVEL_NO_ACT; + else + dest->hilight_color = hilight_get_act_color(rec); +} + +static void hilight_print(int index, HILIGHT_REC *rec); + +static void sig_render_line_text(TEXT_DEST_REC *dest, GString *str, LINE_INFO_META_REC *meta) +{ + char *color, *tmp, *tmp2; + + if (meta == NULL || meta->hash == NULL) + return; + + color = g_hash_table_lookup(meta->hash, "hilight-color"); + + if ((tmp = g_hash_table_lookup(meta->hash, "hilight-line")) != NULL) { + /* hilight whole line */ + + tmp = strip_codes(str->str); + + color = format_string_expand( + color != NULL ? color : settings_get_str("hilight_color"), NULL); + + g_string_truncate(str, 0); + g_string_append(str, color); + g_string_append(str, tmp); + + g_free(color); + g_free(tmp); + } else if ((tmp = g_hash_table_lookup(meta->hash, "hilight-start")) != NULL && + (tmp2 = g_hash_table_lookup(meta->hash, "hilight-end")) != NULL) { + /* hilight part of the line */ + int hilight_start, hilight_end; + int pos, color_pos, color_len; + char *middle; + GString *str2; + + hilight_start = atoi(tmp); + hilight_end = atoi(tmp2); + + /* start of the line */ + pos = strip_real_length(str->str, hilight_start, NULL, NULL); + + str2 = g_string_new_len(str->str, pos); + + /* color */ + color = format_string_expand( + color != NULL ? color : settings_get_str("hilight_color"), NULL); + g_string_append(str2, color); + g_free(color); + + /* middle of the line, stripped */ + middle = strip_codes(str->str + pos); + g_string_append_len(str2, middle, hilight_end - hilight_start); + g_free(middle); + + /* end of the line */ + pos = strip_real_length(str->str, hilight_end, &color_pos, &color_len); + if (color_pos > 0) { + g_string_append_len(str2, str->str + color_pos, color_len); + } else { + /* no colors in line, change back to default */ + g_string_append_c(str2, 4); + g_string_append_c(str2, FORMAT_STYLE_DEFAULTS); + } + g_string_append(str2, str->str + pos); + + g_string_assign(str, g_string_free(str2, FALSE)); + } +} + +static void sig_print_text(TEXT_DEST_REC *dest, const char *text, + const char *stripped) +{ + HILIGHT_REC *hilight; + char *color, *newstr; + int old_level, hilight_start, hilight_end, hilight_len; + int nick_match; + + if (dest->level & MSGLEVEL_NOHILIGHT) + return; + + hilight_start = hilight_end = 0; + hilight = hilight_match(dest->server, dest->target, dest->nick, + dest->address, dest->level, stripped, + &hilight_start, &hilight_end); + + if (hilight == NULL) + return; + + nick_match = hilight->nick && (dest->level & (MSGLEVEL_PUBLIC|MSGLEVEL_ACTIONS)) == MSGLEVEL_PUBLIC; + + old_level = dest->level; + if (!nick_match || (dest->level & MSGLEVEL_HILIGHT)) { + /* Remove NO_ACT, this means explicitly defined hilights will bypass + * /IGNORE ... NO_ACT. + * (It's still possible to use /hilight -actcolor %n to hide + * hilight/beep). + */ + dest->level &= ~MSGLEVEL_NO_ACT; + /* update the level / hilight info */ + hilight_update_text_dest(dest, hilight); + } + + if (nick_match) + return; /* fe-messages.c should have taken care of this */ + + if (old_level & MSGLEVEL_HILIGHT) { + /* nick is highlighted, just set priority */ + return; + } + + color = hilight_get_color(hilight); + hilight_len = hilight_end-hilight_start; + + if (!hilight->word) { + /* hilight whole line */ + char *tmp = strip_codes(text); + newstr = g_strconcat(color, tmp, NULL); + g_free(tmp); + + format_dest_meta_stash(dest, "hilight-line", "\001"); + } else { + /* hilight part of the line */ + GString *str; + char *middle, *tmp; + int pos, color_pos, color_len; + + /* start of the line */ + pos = strip_real_length(text, hilight_start, NULL, NULL); + str = g_string_new_len(text, pos); + + /* color */ + g_string_append(str, color); + + /* middle of the line, stripped */ + middle = strip_codes(text + pos); + g_string_append_len(str, middle, hilight_len); + g_free(middle); + + /* end of the line */ + pos = strip_real_length(text, hilight_end, + &color_pos, &color_len); + if (color_pos > 0) { + g_string_append_len(str, text + color_pos, color_len); + } else { + /* no colors in line, change back to default */ + g_string_append_c(str, 4); + g_string_append_c(str, FORMAT_STYLE_DEFAULTS); + } + g_string_append(str, text + pos); + + newstr = str->str; + g_string_free(str, FALSE); + + format_dest_meta_stash(dest, "hilight-start", + tmp = g_strdup_printf("%d", hilight_start)); + g_free(tmp); + format_dest_meta_stash(dest, "hilight-end", + tmp = g_strdup_printf("%d", hilight_end)); + g_free(tmp); + } + if (hilight->color != NULL) + format_dest_meta_stash(dest, "hilight-color", hilight->color); + + signal_emit("print text", 3, dest, newstr, stripped); + + g_free(color); + g_free(newstr); + + signal_stop(); +} + +HILIGHT_REC *hilight_match_nick(SERVER_REC *server, const char *channel, + const char *nick, const char *address, + int level, const char *msg) +{ + HILIGHT_REC *rec; + + rec = hilight_match(server, channel, nick, address, + level, msg, NULL, NULL); + return (rec == NULL || !rec->nick) ? NULL : rec; +} + +static void read_hilight_config(void) +{ + CONFIG_NODE *node; + HILIGHT_REC *rec; + GSList *tmp; + char *text, *color, *servertag; + + hilights_destroy_all(); + + node = iconfig_node_traverse("hilights", FALSE); + if (node == NULL) { + reset_cache(); + return; + } + + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) { + node = tmp->data; + + if (node->type != NODE_TYPE_BLOCK) + continue; + + text = config_node_get_str(node, "text", NULL); + if (text == NULL || *text == '\0') + continue; + + rec = g_new0(HILIGHT_REC, 1); + hilights = g_slist_append(hilights, rec); + + rec->text = g_strdup(text); + + color = config_node_get_str(node, "color", NULL); + rec->color = color == NULL || *color == '\0' ? NULL : + g_strdup(color); + + color = config_node_get_str(node, "act_color", NULL); + rec->act_color = color == NULL || *color == '\0' ? NULL : + g_strdup(color); + + rec->level = config_node_get_int(node, "level", 0); + rec->priority = config_node_get_int(node, "priority", 0); + rec->nick = config_node_get_bool(node, "nick", TRUE); + rec->word = config_node_get_bool(node, "word", TRUE); + rec->case_sensitive = config_node_get_bool(node, "matchcase", FALSE); + + rec->nickmask = config_node_get_bool(node, "mask", FALSE); + rec->fullword = config_node_get_bool(node, "fullword", FALSE); + rec->regexp = config_node_get_bool(node, "regexp", FALSE); + servertag = config_node_get_str(node, "servertag", NULL); + rec->servertag = servertag == NULL || *servertag == '\0' ? NULL : + g_strdup(servertag); + hilight_init_rec(rec); + + node = iconfig_node_section(node, "channels", -1); + if (node != NULL) rec->channels = config_node_get_list(node); + } + + reset_cache(); +} + +static void hilight_print(int index, HILIGHT_REC *rec) +{ + char *chans, *levelstr; + GString *options; + + options = g_string_new(NULL); + + if (rec->nick && rec->word) { /* default case, no option */ } + else if (rec->nick) + g_string_append(options, "-nick "); + else if (rec->word) + g_string_append(options, "-word "); + else + g_string_append(options, "-line "); + + if (rec->nickmask) g_string_append(options, "-mask "); + if (rec->fullword) g_string_append(options, "-full "); + if (rec->case_sensitive) g_string_append(options, "-matchcase "); + if (rec->regexp) { + g_string_append(options, "-regexp "); + if (rec->preg == NULL) + g_string_append(options, "[INVALID!] "); + } + + if (rec->priority != 0) + g_string_append_printf(options, "-priority %d ", rec->priority); + if (rec->servertag != NULL) + g_string_append_printf(options, "-network %s ", rec->servertag); + if (rec->color != NULL) + g_string_append_printf(options, "-color %s ", rec->color); + if (rec->act_color != NULL) + g_string_append_printf(options, "-actcolor %s ", rec->act_color); + + chans = rec->channels == NULL ? NULL : + g_strjoinv(",", rec->channels); + levelstr = rec->level == 0 ? NULL : + bits2level(rec->level); + if (levelstr != NULL) + levelstr = g_strconcat(levelstr, " ", NULL); + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, + TXT_HILIGHT_LINE, index, rec->text, + chans != NULL ? chans : "", + levelstr != NULL ? levelstr : "", + options->str); + g_free_not_null(chans); + g_free_not_null(levelstr); + g_string_free(options, TRUE); +} + +static void cmd_hilight_show(void) +{ + GSList *tmp; + int index; + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_HILIGHT_HEADER); + index = 1; + for (tmp = hilights; tmp != NULL; tmp = tmp->next, index++) { + HILIGHT_REC *rec = tmp->data; + + hilight_print(index, rec); + } + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_HILIGHT_FOOTER); +} + +/* SYNTAX: HILIGHT [-nick | -word | -line] [-mask | -full | -matchcase | -regexp] + [-color <color>] [-actcolor <color>] [-level <level>] + [-network <network>] [-channels <channels>] <text> */ +static void cmd_hilight(const char *data) +{ + GHashTable *optlist; + HILIGHT_REC *rec; + char *colorarg, *actcolorarg, *levelarg, *priorityarg, *chanarg, *text, *servertag; + char **channels; + void *free_arg; + + g_return_if_fail(data != NULL); + + if (*data == '\0') { + cmd_hilight_show(); + return; + } + + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS | + PARAM_FLAG_GETREST, "hilight", &optlist, &text)) + return; + + chanarg = g_hash_table_lookup(optlist, "channels"); + levelarg = g_hash_table_lookup(optlist, "level"); + priorityarg = g_hash_table_lookup(optlist, "priority"); + colorarg = g_hash_table_lookup(optlist, "color"); + actcolorarg = g_hash_table_lookup(optlist, "actcolor"); + servertag = g_hash_table_lookup(optlist, "network"); + + if (*text == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + channels = (chanarg == NULL || *chanarg == '\0') ? NULL : + g_strsplit(chanarg, ",", -1); + + rec = hilight_find(text, channels); + if (rec == NULL) { + rec = g_new0(HILIGHT_REC, 1); + + /* default to nick/word hilighting */ + rec->nick = TRUE; + rec->word = TRUE; + + rec->text = g_strdup(text); + rec->channels = channels; + } else { + g_strfreev(channels); + } + + rec->level = (levelarg == NULL || *levelarg == '\0') ? 0 : + level2bits(replace_chars(levelarg, ',', ' '), NULL); + rec->priority = priorityarg == NULL ? 0 : atoi(priorityarg); + + if (g_hash_table_lookup(optlist, "line") != NULL) { + rec->word = FALSE; + rec->nick = FALSE; + } + + if (g_hash_table_lookup(optlist, "word") != NULL) { + rec->word = TRUE; + rec->nick = FALSE; + } + + if (g_hash_table_lookup(optlist, "nick") != NULL) + rec->nick = TRUE; + + rec->nickmask = g_hash_table_lookup(optlist, "mask") != NULL; + rec->fullword = g_hash_table_lookup(optlist, "full") != NULL; + rec->regexp = g_hash_table_lookup(optlist, "regexp") != NULL; + rec->case_sensitive = g_hash_table_lookup(optlist, "matchcase") != NULL; + + if (colorarg != NULL) { + g_free_and_null(rec->color); + if (*colorarg != '\0') + rec->color = g_strdup(colorarg); + } + if (actcolorarg != NULL) { + g_free_and_null(rec->act_color); + if (*actcolorarg != '\0') + rec->act_color = g_strdup(actcolorarg); + } + if (servertag != NULL) { + g_free_and_null(rec->servertag); + if (*servertag != '\0') + rec->servertag = g_strdup(servertag); + } + + hilight_create(rec); + + hilight_print(g_slist_index(hilights, rec)+1, rec); + cmd_params_free(free_arg); + + reset_cache(); +} + +/* SYNTAX: DEHILIGHT <id>|<mask> */ +static void cmd_dehilight(const char *data) +{ + HILIGHT_REC *rec; + GSList *tmp; + + if (is_numeric(data, ' ')) { + /* with index number */ + tmp = g_slist_nth(hilights, atoi(data)-1); + rec = tmp == NULL ? NULL : tmp->data; + } else { + /* with mask */ + char *chans[2] = { "*", NULL }; + rec = hilight_find(data, chans); + } + + if (rec == NULL) + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_HILIGHT_NOT_FOUND, data); + else { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_HILIGHT_REMOVED, rec->text); + hilight_remove(rec); + reset_cache(); + } +} + +static void hilight_nick_cache(GHashTable *list, CHANNEL_REC *channel, + NICK_REC *nick) +{ + GSList *tmp; + HILIGHT_REC *match; + char *nickmask; + int len, best_match; + int priority = -1; + + if (nick->host == NULL) + return; /* don't check until host is known */ + + nickmask = g_strconcat(nick->nick, "!", nick->host, NULL); + + best_match = 0; match = NULL; + for (tmp = hilights; tmp != NULL; tmp = tmp->next) { + HILIGHT_REC *rec = tmp->data; + + if (rec->priority > priority && rec->nickmask && + hilight_match_channel(rec, channel->name) && + match_wildcards(rec->text, nickmask)) { + len = strlen(rec->text); + if (best_match < len) { + priority = rec->priority; + best_match = len; + match = rec; + } + } + } + g_free_not_null(nickmask); + + if (match != NULL) + g_hash_table_insert(list, nick, match); +} + +static void read_settings(void) +{ + default_hilight_level = settings_get_level("hilight_level"); + reset_level_cache(); +} + +void hilight_text_init(void) +{ + settings_add_str("lookandfeel", "hilight_color", "%Y"); + settings_add_str("lookandfeel", "hilight_act_color", "%M"); + settings_add_level("lookandfeel", "hilight_level", "PUBLIC DCCMSGS"); + + read_settings(); + + nickmatch = nickmatch_init(hilight_nick_cache, NULL); + read_hilight_config(); + + signal_add_first("print text", (SIGNAL_FUNC) sig_print_text); + signal_add("gui render line text", (SIGNAL_FUNC) sig_render_line_text); + signal_add("setup reread", (SIGNAL_FUNC) read_hilight_config); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); + + command_bind("hilight", NULL, (SIGNAL_FUNC) cmd_hilight); + command_bind("dehilight", NULL, (SIGNAL_FUNC) cmd_dehilight); + command_set_options("hilight", "-color -actcolor -level -priority -network -channels nick word line mask full regexp matchcase"); +} + +void hilight_text_deinit(void) +{ + hilights_destroy_all(); + nickmatch_deinit(nickmatch); + + signal_remove("print text", (SIGNAL_FUNC) sig_print_text); + signal_remove("gui render line text", (SIGNAL_FUNC) sig_render_line_text); + signal_remove("setup reread", (SIGNAL_FUNC) read_hilight_config); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); + + command_unbind("hilight", (SIGNAL_FUNC) cmd_hilight); + command_unbind("dehilight", (SIGNAL_FUNC) cmd_dehilight); +} diff --git a/src/fe-common/core/hilight-text.h b/src/fe-common/core/hilight-text.h new file mode 100644 index 0000000..0dfe78f --- /dev/null +++ b/src/fe-common/core/hilight-text.h @@ -0,0 +1,48 @@ +#ifndef IRSSI_FE_COMMON_CORE_HILIGHT_TEXT_H +#define IRSSI_FE_COMMON_CORE_HILIGHT_TEXT_H + +#include <irssi/src/core/iregex.h> +#include <irssi/src/fe-common/core/formats.h> + +struct _HILIGHT_REC { + char *text; + + char **channels; /* if non-NULL, check the text only from these channels */ + int level; /* match only messages with this level, 0=default */ + char *color; /* if starts with number, \003 is automatically + inserted before it. */ + char *act_color; /* color for window activity */ + int priority; + + unsigned int nick:1; /* hilight only nick if possible */ + unsigned int word:1; /* hilight only word, not full line */ + + unsigned int nickmask:1; /* `text' is a nick mask */ + unsigned int fullword:1; /* match `text' only for full words */ + unsigned int regexp:1; /* `text' is a regular expression */ + unsigned int case_sensitive:1;/* `text' must match case */ + Regex *preg; + char *servertag; +}; + +extern GSList *hilights; + +HILIGHT_REC *hilight_match(SERVER_REC *server, const char *channel, + const char *nick, const char *address, + int level, const char *str, + int *match_beg, int *match_end); + +HILIGHT_REC *hilight_match_nick(SERVER_REC *server, const char *channel, + const char *nick, const char *address, + int level, const char *msg); + +char *hilight_get_color(HILIGHT_REC *rec); +void hilight_update_text_dest(TEXT_DEST_REC *dest, HILIGHT_REC *rec); + +void hilight_create(HILIGHT_REC *rec); +void hilight_remove(HILIGHT_REC *rec); + +void hilight_text_init(void); +void hilight_text_deinit(void); + +#endif diff --git a/src/fe-common/core/keyboard.c b/src/fe-common/core/keyboard.c new file mode 100644 index 0000000..e0e061d --- /dev/null +++ b/src/fe-common/core/keyboard.c @@ -0,0 +1,1007 @@ +/* + keyboard.c : irssi + + Copyright (C) 1999-2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/lib-config/iconfig.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/fe-common/core/keyboard.h> +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/core/printtext.h> + +#define MAX_EXPAND_RECURSION 100 + +GSList *keyinfos; +static GHashTable *keys, *default_keys; +static int key_timeout; + +/* A cache of some sort for key presses that generate a single char only. + If the key isn't used, used_keys[key] is zero. */ +static char used_keys[256]; + +/* Contains a list of all possible executable key bindings (not "key" keys). + Format is _always_ in key1-key2-key3 format and fully extracted, like + ^[-[-A, not meta-A */ +static GTree *key_states; +static int key_config_frozen; + +struct _KEYBOARD_REC { + char *key_state; /* the ongoing key combo */ + guint timer_tag; /* used to check when a pending combo has expired */ + void *gui_data; /* GUI specific data sent in "key pressed" signal */ +}; + +/* Creates a new "keyboard" - this is used only for keeping track of + key combo states and sending the gui_data parameter in "key pressed" + signal */ +KEYBOARD_REC *keyboard_create(void *data) +{ + KEYBOARD_REC *rec; + + rec = g_new0(KEYBOARD_REC, 1); + rec->gui_data = data; + rec->timer_tag = 0; + + signal_emit("keyboard created", 1, rec); + return rec; +} + +/* Destroys a keyboard */ +void keyboard_destroy(KEYBOARD_REC *keyboard) +{ + if (keyboard->timer_tag > 0) { + g_source_remove(keyboard->timer_tag); + keyboard->timer_tag = 0; + } + + signal_emit("keyboard destroyed", 1, keyboard); + + g_free_not_null(keyboard->key_state); + g_free(keyboard); +} + +static void key_destroy(KEY_REC *rec, GHashTable *hash) +{ + g_hash_table_remove(hash, rec->key); + + g_free_not_null(rec->data); + g_free(rec->key); + g_free(rec); +} + +static void key_default_add(const char *id, const char *key, const char *data) +{ + KEYINFO_REC *info; + KEY_REC *rec; + + info = key_info_find(id); + if (info == NULL) + return; + + rec = g_hash_table_lookup(default_keys, key); + if (rec != NULL) { + /* key already exists, replace */ + rec->info->default_keys = + g_slist_remove(rec->info->default_keys, rec); + key_destroy(rec, default_keys); + } + + rec = g_new0(KEY_REC, 1); + rec->key = g_strdup(key); + rec->info = info; + rec->data = g_strdup(data); + info->default_keys = g_slist_append(info->default_keys, rec); + g_hash_table_insert(default_keys, rec->key, rec); +} + +static CONFIG_NODE *key_config_find(const char *key) +{ + CONFIG_NODE *node; + GSList *tmp; + + /* remove old keyboard settings */ + node = iconfig_node_traverse("(keyboard", TRUE); + + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) { + node = tmp->data; + + if (g_strcmp0(config_node_get_str(node, "key", ""), key) == 0) + return node; + } + + return NULL; +} + +static void keyconfig_save(const char *id, const char *key, const char *data) +{ + CONFIG_NODE *node; + + g_return_if_fail(id != NULL); + g_return_if_fail(key != NULL); + + node = key_config_find(key); + if (node == NULL) { + node = iconfig_node_traverse("(keyboard", TRUE); + node = iconfig_node_section(node, NULL, NODE_TYPE_BLOCK); + } + + iconfig_node_set_str(node, "key", key); + iconfig_node_set_str(node, "id", id); + iconfig_node_set_str(node, "data", data); +} + +static void keyconfig_clear(const char *key) +{ + CONFIG_NODE *node; + KEY_REC *rec; + + g_return_if_fail(key != NULL); + + /* remove old keyboard settings */ + node = key_config_find(key); + if (node != NULL) { + iconfig_node_remove(iconfig_node_traverse("(keyboard", FALSE), + node); + } + if ((rec = g_hash_table_lookup(default_keys, key)) != NULL) { + node = iconfig_node_traverse("(keyboard", TRUE); + node = iconfig_node_section(node, NULL, NODE_TYPE_BLOCK); + iconfig_node_set_str(node, "key", key); + } +} + +KEYINFO_REC *key_info_find(const char *id) +{ + GSList *tmp; + + for (tmp = keyinfos; tmp != NULL; tmp = tmp->next) { + KEYINFO_REC *rec = tmp->data; + + if (g_ascii_strcasecmp(rec->id, id) == 0) + return rec; + } + + return NULL; +} + +static int expand_key(const char *key, GSList **out, int *limit); + +#define expand_out_char(out, c) \ + { \ + GSList *tmp; \ + for (tmp = out; tmp != NULL; tmp = tmp->next) \ + g_string_append_c(tmp->data, c); \ + } + +#define expand_out_free(out) \ + { \ + GSList *tmp; \ + for (tmp = out; tmp != NULL; tmp = tmp->next) \ + g_string_free(tmp->data, TRUE); \ + g_slist_free(out); out = NULL; \ + } + +static int expand_combo(const char *start, const char *end, GSList **out, int *limit) +{ + KEY_REC *rec; + KEYINFO_REC *info; + GSList *tmp, *tmp2, *list, *copy, *newout; + char *str, *p; + + if ((*limit)-- < 0) { + return FALSE; + } + + if (start == end) { + /* single key */ + expand_out_char(*out, *start); + return TRUE; + } + + info = key_info_find("key"); + if (info == NULL) + return FALSE; + + /* get list of all key combos that generate the named combo.. */ + list = NULL; + str = g_strndup(start, (int) (end-start)+1); + for (tmp = info->keys; tmp != NULL; tmp = tmp->next) { + KEY_REC *rec = tmp->data; + + if (g_strcmp0(rec->data, str) == 0) + list = g_slist_append(list, rec); + } + + if (list == NULL) { + /* unknown keycombo - add it as-is, maybe the GUI will + feed it to us as such */ + for (p = str; *p != '\0'; p++) + expand_out_char(*out, *p); + g_free(str); + return TRUE; + } + g_free(str); + + if (list->next == NULL) { + /* only one way to generate the combo, good */ + rec = list->data; + g_slist_free(list); + return expand_key(rec->key, out, limit); + } + + /* multiple ways to generate the combo - + we'll need to include all of them in output */ + newout = NULL; + for (tmp = list->next; tmp != NULL; tmp = tmp->next) { + KEY_REC *rec = tmp->data; + + copy = NULL; + for (tmp2 = *out; tmp2 != NULL; tmp2 = tmp2->next) { + GString *str = tmp2->data; + copy = g_slist_append(copy, g_string_new(str->str)); + } + + if (!expand_key(rec->key, ©, limit)) { + if (*limit < 0) { + return FALSE; + } + + /* illegal key combo, remove from list */ + expand_out_free(copy); + } else { + newout = g_slist_concat(newout, copy); + } + } + + rec = list->data; + g_slist_free(list); + if (!expand_key(rec->key, out, limit)) { + if (*limit < 0) { + return FALSE; + } + + /* illegal key combo, remove from list */ + expand_out_free(*out); + } + + *out = g_slist_concat(*out, newout); + return *out != NULL; +} + +/* Expand key code - returns TRUE if successful. */ +static int expand_key(const char *key, GSList **out, int *limit) +{ + GSList *tmp; + const char *start; + int last_hyphen; + + if ((*limit)-- < 0) { + return FALSE; + } + + /* meta-^W^Gf -> ^[-^W-^G-f */ + start = NULL; last_hyphen = TRUE; + for (; *key != '\0'; key++) { + if (start != NULL) { + if (i_isalnum(*key) || *key == '_') { + /* key combo continues */ + continue; + } + + if (!expand_combo(start, key-1, out, limit)) + return FALSE; + expand_out_char(*out, '-'); + start = NULL; + } + + if (*key == '-') { + if (last_hyphen) { + expand_out_char(*out, '-'); + expand_out_char(*out, '-'); + } + last_hyphen = !last_hyphen; + } else if (*key == '^') { + expand_out_char(*out, '^'); + + /* ctrl-code */ + if (key[1] != '\0' && key[1] != '-') { + key++; + expand_out_char(*out, *key); + } + else { + /* escaped syntax for ^, see gui-readline.c */ + expand_out_char(*out, '-'); + } + + expand_out_char(*out, '-'); + last_hyphen = FALSE; /* optional */ + } else if (last_hyphen && i_isalpha(*key)) { + /* possibly beginning of keycombo */ + start = key; + last_hyphen = FALSE; + } else if (g_utf8_validate(key, -1, NULL)) { + /* Assume we are looking at the start of a + * multibyte sequence we will receive as-is, + * so add it to the list as-is. + */ + const char *p, *end = g_utf8_next_char(key); + for (p = key; p != end; p++) + expand_out_char(*out, *p); + expand_out_char(*out, '-'); + /* The for loop skips past the remaining character. + * Nasty, I know... + */ + key = end - 1; + last_hyphen = FALSE; + } else { + expand_out_char(*out, *key); + expand_out_char(*out, '-'); + last_hyphen = FALSE; /* optional */ + } + } + + if (start != NULL) + return expand_combo(start, key-1, out, limit); + + for (tmp = *out; tmp != NULL; tmp = tmp->next) { + GString *str = tmp->data; + + g_string_truncate(str, str->len-1); + } + + return TRUE; +} + +static void key_states_scan_key(const char *key, KEY_REC *rec) +{ + GSList *tmp, *out; + int limit = MAX_EXPAND_RECURSION; + + if (g_strcmp0(rec->info->id, "key") == 0) + return; + + out = g_slist_append(NULL, g_string_new(NULL)); + if (expand_key(key, &out, &limit)) { + for (tmp = out; tmp != NULL; tmp = tmp->next) { + GString *str = tmp->data; + + if (str->str[1] == '-' || str->str[1] == '\0') + used_keys[(int)(unsigned char)str->str[0]] = 1; + + g_tree_insert(key_states, g_strdup(str->str), rec); + } + } + + expand_out_free(out); +} + +static int key_state_destroy(char *key) +{ + g_free(key); + return FALSE; +} + +/* Rescan all the key combos and figure out which characters are supposed + to be treated as characters and which as key combos. + Yes, this is pretty slow function... */ +static void key_states_rescan(void) +{ + GString *temp; + + memset(used_keys, 0, sizeof(used_keys)); + + g_tree_foreach(key_states, (GTraverseFunc) key_state_destroy, + NULL); + g_tree_destroy(key_states); + key_states = g_tree_new((GCompareFunc) g_strcmp0); + + temp = g_string_new(NULL); + g_hash_table_foreach(keys, (GHFunc) key_states_scan_key, temp); + g_string_free(temp, TRUE); +} + +void key_configure_freeze(void) +{ + key_config_frozen++; +} + +void key_configure_thaw(void) +{ + g_return_if_fail(key_config_frozen > 0); + + if (--key_config_frozen == 0) + key_states_rescan(); +} + +static void key_configure_destroy(KEY_REC *rec) +{ + g_return_if_fail(rec != NULL); + + rec->info->keys = g_slist_remove(rec->info->keys, rec); + g_hash_table_remove(keys, rec->key); + + signal_emit("key destroyed", 1, rec); + + if (!key_config_frozen) + key_states_rescan(); + + g_free_not_null(rec->data); + g_free(rec->key); + g_free(rec); +} + +/* Configure new key */ +static void key_configure_create(const char *id, const char *key, + const char *data) +{ + KEYINFO_REC *info; + KEY_REC *rec; + + g_return_if_fail(id != NULL); + g_return_if_fail(key != NULL && *key != '\0'); + + info = key_info_find(id); + if (info == NULL) + return; + + rec = g_hash_table_lookup(keys, key); + if (rec != NULL) + key_configure_destroy(rec); + + rec = g_new0(KEY_REC, 1); + rec->key = g_strdup(key); + rec->info = info; + rec->data = g_strdup(data); + info->keys = g_slist_append(info->keys, rec); + g_hash_table_insert(keys, rec->key, rec); + + signal_emit("key created", 1, rec); + + if (!key_config_frozen) + key_states_rescan(); +} + +/* Bind a key for function */ +void key_bind(const char *id, const char *description, + const char *key_default, const char *data, SIGNAL_FUNC func) +{ + KEYINFO_REC *info; + char *key; + + g_return_if_fail(id != NULL); + + /* create key info record */ + info = key_info_find(id); + if (info == NULL) { + g_return_if_fail(func != NULL); + + if (description == NULL) + g_warning("key_bind(%s) should have description!", id); + info = g_new0(KEYINFO_REC, 1); + info->id = g_strdup(id); + info->description = g_strdup(description); + keyinfos = g_slist_append(keyinfos, info); + + /* add the signal */ + key = g_strconcat("key ", id, NULL); + signal_add(key, func); + g_free(key); + + signal_emit("keyinfo created", 1, info); + } + + if (key_default != NULL && *key_default != '\0') { + key_default_add(id, key_default, data); + key_configure_create(id, key_default, data); + } +} + +static void keyinfo_remove(KEYINFO_REC *info) +{ + g_return_if_fail(info != NULL); + + keyinfos = g_slist_remove(keyinfos, info); + signal_emit("keyinfo destroyed", 1, info); + + /* destroy all keys */ + g_slist_foreach(info->keys, (GFunc) key_destroy, keys); + g_slist_foreach(info->default_keys, (GFunc) key_destroy, default_keys); + + /* destroy key info */ + g_slist_free(info->keys); + g_slist_free(info->default_keys); + g_free_not_null(info->description); + g_free(info->id); + g_free(info); +} + +/* Unbind key */ +void key_unbind(const char *id, SIGNAL_FUNC func) +{ + KEYINFO_REC *info; + char *key; + + g_return_if_fail(id != NULL); + g_return_if_fail(func != NULL); + + /* remove keys */ + info = key_info_find(id); + if (info != NULL) + keyinfo_remove(info); + + /* remove signal */ + key = g_strconcat("key ", id, NULL); + signal_remove(key, func); + g_free(key); +} + +/* Configure new key */ +void key_configure_add(const char *id, const char *key, const char *data) +{ + g_return_if_fail(id != NULL); + g_return_if_fail(key != NULL && *key != '\0'); + + key_configure_create(id, key, data); + keyconfig_save(id, key, data); +} + +/* Remove key */ +void key_configure_remove(const char *key) +{ + KEY_REC *rec; + + g_return_if_fail(key != NULL); + + keyconfig_clear(key); + + rec = g_hash_table_lookup(keys, key); + if (rec == NULL) return; + + key_configure_destroy(rec); +} + +/* Reset key to default */ +void key_configure_reset(const char *key) +{ + KEY_REC *rec; + CONFIG_NODE *node; + + g_return_if_fail(key != NULL); + + node = key_config_find(key); + if (node != NULL) { + iconfig_node_remove(iconfig_node_traverse("(keyboard", FALSE), node); + } + + if ((rec = g_hash_table_lookup(default_keys, key)) != NULL) { + key_configure_create(rec->info->id, rec->key, rec->data); + } else { + rec = g_hash_table_lookup(keys, key); + if (rec == NULL) + return; + + key_configure_destroy(rec); + } +} + +static int key_emit_signal(KEYBOARD_REC *keyboard, KEY_REC *key) +{ + int consumed; + char *str; + + str = g_strconcat("key ", key->info->id, NULL); + consumed = signal_emit(str, 3, key->data, keyboard->gui_data, key->info); + g_free(str); + + return consumed; +} + +static int key_states_search(const unsigned char *combo, + const unsigned char *search) +{ + while (*search != '\0') { + if (*combo != *search) + return *search - *combo; + search++; combo++; + } + + return 0; +} + +static gboolean key_timeout_expired(KEYBOARD_REC *keyboard) +{ + KEY_REC *rec; + + keyboard->timer_tag = 0; + + /* So, the timeout has expired with the input queue full, let's see if + * what we've got is bound to some action. */ + rec = g_tree_lookup(key_states, keyboard->key_state); + /* Drain the queue anyway. */ + g_free_and_null(keyboard->key_state); + + if (rec != NULL) { + (void)key_emit_signal(keyboard, rec); + } + + return FALSE; +} + +int key_pressed(KEYBOARD_REC *keyboard, const char *key) +{ + KEY_REC *rec; + char *combo; + int first_key, consumed; + + g_return_val_if_fail(keyboard != NULL, FALSE); + g_return_val_if_fail(key != NULL && *key != '\0', FALSE); + + if (keyboard->timer_tag > 0) { + g_source_remove(keyboard->timer_tag); + keyboard->timer_tag = 0; + } + + if (keyboard->key_state == NULL && key[1] == '\0' && + !used_keys[(int) (unsigned char) key[0]]) { + /* fast check - key not used */ + return -1; + } + + first_key = keyboard->key_state == NULL; + combo = keyboard->key_state == NULL ? g_strdup(key) : + g_strconcat(keyboard->key_state, "-", key, NULL); + g_free_and_null(keyboard->key_state); + + rec = g_tree_search(key_states, + (GCompareFunc) key_states_search, + combo); + if (rec == NULL) { + /* unknown key combo, eat the invalid key + unless it was the first key pressed */ + g_free(combo); + return first_key ? -1 : 1; + } + + if (g_tree_lookup(key_states, combo) != rec) { + /* key combo continues.. */ + keyboard->key_state = combo; + /* respect the timeout if specified by the user */ + if (key_timeout > 0) { + keyboard->timer_tag = + g_timeout_add(key_timeout, + (GSourceFunc) key_timeout_expired, + keyboard); + } + return 0; + } + + /* finished key combo, execute */ + g_free(combo); + consumed = key_emit_signal(keyboard, rec); + + /* never consume non-control characters */ + return consumed ? 1 : -1; +} + +void keyboard_entry_redirect(SIGNAL_FUNC func, const char *entry, + int flags, void *data) +{ + signal_emit("gui entry redirect", 4, func, entry, + GINT_TO_POINTER(flags), data); +} + +static void sig_command(const char *data) +{ + const char *cmdchars; + char *str; + + cmdchars = settings_get_str("cmdchars"); + str = strchr(cmdchars, *data) != NULL ? g_strdup(data) : + g_strdup_printf("%c%s", *cmdchars, data); + + signal_emit("send command", 3, str, active_win->active_server, active_win->active); + + g_free(str); +} + +static void sig_key(const char *data) +{ + /* we should never get here */ +} + +static void sig_multi(const char *data, void *gui_data) +{ + KEYINFO_REC *info; + char **list, **tmp, *p, *str; + + list = g_strsplit(data, ";", -1); + for (tmp = list; *tmp != NULL; tmp++) { + p = strchr(*tmp, ' '); + if (p != NULL) *p++ = '\0'; else p = ""; + + info = key_info_find(*tmp); + if (info != NULL) { + str = g_strconcat("key ", info->id, NULL); + signal_emit(str, 3, p, gui_data, info); + g_free(str); + } + } + g_strfreev(list); +} + +static void sig_nothing(const char *data) +{ +} + +static void cmd_show_keys(const char *searchkey, int full) +{ + GSList *info, *key; + int len; + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_BIND_HEADER); + + len = searchkey == NULL ? 0 : strlen(searchkey); + for (info = keyinfos; info != NULL; info = info->next) { + KEYINFO_REC *rec = info->data; + + for (key = rec->keys; key != NULL; key = key->next) { + KEY_REC *rec = key->data; + + if ((len == 0 || (full ? strncmp(rec->key, searchkey, len) == 0 : + strstr(rec->key, searchkey) != NULL)) && + (!full || rec->key[len] == '\0')) { + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_BIND_LIST, + rec->key, rec->info->id, rec->data == NULL ? "" : rec->data); + } + } + } + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_BIND_FOOTER); +} + +/* SYNTAX: BIND [-list] [-delete | -reset] [<key> [<command> [<data>]]] */ +static void cmd_bind(const char *data) +{ + GHashTable *optlist; + char *key, *id, *keydata; + void *free_arg; + int command_id; + + if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST | PARAM_FLAG_OPTIONS, + "bind", &optlist, &key, &id, &keydata)) + return; + + if (g_hash_table_lookup(optlist, "list")) { + GSList *tmp; + + for (tmp = keyinfos; tmp != NULL; tmp = tmp->next) { + KEYINFO_REC *rec = tmp->data; + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_BIND_COMMAND_LIST, + rec->id, rec->description ? rec->description : ""); + } + cmd_params_free(free_arg); + return; + } + + if (*key != '\0' && g_hash_table_lookup(optlist, "delete")) { + /* delete key */ + key_configure_remove(key); + cmd_params_free(free_arg); + return; + } else if (*key != '\0' && g_hash_table_lookup(optlist, "reset")) { + /* reset key */ + key_configure_reset(key); + cmd_show_keys(key, TRUE); + cmd_params_free(free_arg); + return; + } + + if (*id == '\0') { + /* show some/all keys */ + cmd_show_keys(key, FALSE); + cmd_params_free(free_arg); + return; + } + + command_id = strchr(settings_get_str("cmdchars"), *id) != NULL; + if (command_id) { + /* using shortcut to command id */ + keydata = g_strconcat(id+1, " ", keydata, NULL); + id = "command"; + } + + if (key_info_find(id) == NULL) + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_BIND_UNKNOWN_ID, id); + else { + key_configure_add(id, key, keydata); + cmd_show_keys(key, TRUE); + } + + if (command_id) g_free(keydata); + cmd_params_free(free_arg); +} + +static GList *completion_get_keyinfos(const char *info) +{ + GList *list; + GSList *tmp; + int len; + + list = NULL; len = strlen(info); + for (tmp = keyinfos; tmp != NULL; tmp = tmp->next) { + KEYINFO_REC *rec = tmp->data; + + if (g_ascii_strncasecmp(rec->id, info, len) == 0) + list = g_list_append(list, g_strdup(rec->id)); + } + + return list; +} + +static void sig_complete_bind(GList **list, WINDOW_REC *window, + const char *word, const char *line, + int *want_space) +{ + g_return_if_fail(list != NULL); + g_return_if_fail(word != NULL); + g_return_if_fail(line != NULL); + + if (*line == '\0' || strchr(line, ' ') != NULL) + return; + + *list = completion_get_keyinfos(word); + if (*list != NULL) signal_stop(); +} + +static int key_destroy_hash(const char *key, KEY_REC *rec) +{ + rec->info->keys = g_slist_remove(rec->info->keys, rec); + + g_free_not_null(rec->data); + g_free(rec->key); + g_free(rec); + return TRUE; +} + +static void key_copy_default(const char *key, KEY_REC *orig) +{ + KEY_REC *rec; + + rec = g_new0(KEY_REC, 1); + rec->key = g_strdup(orig->key); + rec->info = orig->info; + rec->data = g_strdup(orig->data); + + rec->info->keys = g_slist_append(rec->info->keys, rec); + g_hash_table_insert(keys, rec->key, rec); +} + +static void keyboard_reset_defaults(void) +{ + g_hash_table_foreach_remove(keys, (GHRFunc) key_destroy_hash, NULL); + g_hash_table_foreach(default_keys, (GHFunc) key_copy_default, NULL); +} + +static void key_config_read(CONFIG_NODE *node) +{ + char *key, *id, *data; + + g_return_if_fail(node != NULL); + + key = config_node_get_str(node, "key", NULL); + id = config_node_get_str(node, "id", NULL); + data = config_node_get_str(node, "data", NULL); + + if (key != NULL && id != NULL) { + key_configure_create(id, key, data); + } else if (key != NULL && id == NULL && data == NULL) { + KEY_REC *rec = g_hash_table_lookup(keys, key); + if (rec != NULL) + key_configure_destroy(rec); + } +} + +static void read_keyboard_config(void) +{ + CONFIG_NODE *node; + GSList *tmp; + + key_configure_freeze(); + + keyboard_reset_defaults(); + + node = iconfig_node_traverse("keyboard", FALSE); + if (node == NULL) { + key_configure_thaw(); + return; + } + + /* FIXME: backward "compatibility" - remove after irssi .99 */ + if (node->type != NODE_TYPE_LIST) { + iconfig_node_remove(NULL, node); + key_configure_thaw(); + return; + } + + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) + key_config_read(tmp->data); + + key_configure_thaw(); + + /* any positive value other than 0 enables the timeout (in ms). */ + key_timeout = settings_get_int("key_timeout"); +} + +void keyboard_init(void) +{ + keys = g_hash_table_new((GHashFunc) g_str_hash, + (GCompareFunc) g_str_equal); + default_keys = g_hash_table_new((GHashFunc) g_str_hash, + (GCompareFunc) g_str_equal); + keyinfos = NULL; + key_states = g_tree_new((GCompareFunc) g_strcmp0); + key_config_frozen = 0; + memset(used_keys, 0, sizeof(used_keys)); + + settings_add_int("misc", "key_timeout", 0); + + key_bind("command", "Run any command", NULL, NULL, (SIGNAL_FUNC) sig_command); + key_bind("key", "Specify name for key binding", NULL, NULL, (SIGNAL_FUNC) sig_key); + key_bind("multi", "Run multiple commands", NULL, NULL, (SIGNAL_FUNC) sig_multi); + key_bind("nothing", "Do nothing", NULL, NULL, (SIGNAL_FUNC) sig_nothing); + + /* read the keyboard config when all key binds are known */ + signal_add("irssi init read settings", (SIGNAL_FUNC) read_keyboard_config); + signal_add("setup reread", (SIGNAL_FUNC) read_keyboard_config); + signal_add("complete command bind", (SIGNAL_FUNC) sig_complete_bind); + + command_bind("bind", NULL, (SIGNAL_FUNC) cmd_bind); + command_set_options("bind", "delete reset list"); +} + +void keyboard_deinit(void) +{ + key_unbind("command", (SIGNAL_FUNC) sig_command); + key_unbind("key", (SIGNAL_FUNC) sig_key); + key_unbind("multi", (SIGNAL_FUNC) sig_multi); + key_unbind("nothing", (SIGNAL_FUNC) sig_nothing); + + while (keyinfos != NULL) + keyinfo_remove(keyinfos->data); + g_hash_table_destroy(keys); + g_hash_table_destroy(default_keys); + + g_tree_foreach(key_states, (GTraverseFunc) key_state_destroy, + NULL); + g_tree_destroy(key_states); + + signal_remove("irssi init read settings", (SIGNAL_FUNC) read_keyboard_config); + signal_remove("setup reread", (SIGNAL_FUNC) read_keyboard_config); + signal_remove("complete command bind", (SIGNAL_FUNC) sig_complete_bind); + command_unbind("bind", (SIGNAL_FUNC) cmd_bind); +} diff --git a/src/fe-common/core/keyboard.h b/src/fe-common/core/keyboard.h new file mode 100644 index 0000000..b19536a --- /dev/null +++ b/src/fe-common/core/keyboard.h @@ -0,0 +1,58 @@ +#ifndef IRSSI_FE_COMMON_CORE_KEYBOARD_H +#define IRSSI_FE_COMMON_CORE_KEYBOARD_H + +#include <irssi/src/core/signals.h> + +typedef struct _KEYBOARD_REC KEYBOARD_REC; +typedef struct _KEYINFO_REC KEYINFO_REC; +typedef struct _KEY_REC KEY_REC; + +struct _KEYINFO_REC { + char *id; + char *description; + + GSList *keys, *default_keys; +}; + +struct _KEY_REC { + KEYINFO_REC *info; + + char *key; + char *data; +}; + +extern GSList *keyinfos; + +/* Creates a new "keyboard" - this is used only for keeping track of + key combo states and sending the gui_data parameter in "key pressed" + signal */ +KEYBOARD_REC *keyboard_create(void *gui_data); +/* Destroys a keyboard */ +void keyboard_destroy(KEYBOARD_REC *keyboard); +/* Returns 1 if key press was consumed, -1 if not, 0 if it's beginning of a + key combo. Control characters should be sent as "^@" .. "^_" instead of + #0..#31 chars, #127 should be sent as ^? */ +int key_pressed(KEYBOARD_REC *keyboard, const char *key); + +void key_bind(const char *id, const char *description, + const char *key_default, const char *data, SIGNAL_FUNC func); +void key_unbind(const char *id, SIGNAL_FUNC func); + +void key_configure_freeze(void); +void key_configure_thaw(void); + +void key_configure_add(const char *id, const char *key, const char *data); +void key_configure_remove(const char *key); + +KEYINFO_REC *key_info_find(const char *id); + +#define ENTRY_REDIRECT_FLAG_HOTKEY 0x01 +#define ENTRY_REDIRECT_FLAG_HIDDEN 0x02 + +void keyboard_entry_redirect(SIGNAL_FUNC func, const char *entry, + int flags, void *data); + +void keyboard_init(void); +void keyboard_deinit(void); + +#endif diff --git a/src/fe-common/core/meson.build b/src/fe-common/core/meson.build new file mode 100644 index 0000000..73cb156 --- /dev/null +++ b/src/fe-common/core/meson.build @@ -0,0 +1,99 @@ +# this file is part of irssi + +if have_capsicum + fe_common_core_capsicum_source = files('fe-capsicum.c') +else + fe_common_core_capsicum_source = [] +endif + +fe_common_core_sources = [ + files( + 'chat-completion.c', + 'command-history.c', + 'completion.c', + 'fe-channels.c', + 'fe-common-core.c', + 'fe-core-commands.c', + 'fe-exec.c', + 'fe-expandos.c', + 'fe-help.c', + 'fe-ignore-messages.c', + 'fe-ignore.c', + 'fe-log.c', + 'fe-messages.c', + 'fe-modules.c', + 'fe-queries.c', + 'fe-recode.c', + 'fe-server.c', + 'fe-settings.c', + 'fe-tls.c', + 'fe-windows.c', + 'formats.c', + 'hilight-text.c', + 'keyboard.c', + 'module-formats.c', + 'printtext.c', + 'themes.c', + 'window-activity.c', + 'window-commands.c', + 'window-items.c', + 'windows-layout.c', + ) + + fe_common_core_capsicum_source + + [ + default_theme_h, + irssi_version_h, + ] +] + +libfe_common_core_a = static_library('fe_common_core', + fe_common_core_sources, + include_directories : rootinc, + implicit_include_directories : false, + c_args : [ + def_helpdir, + def_themesdir, + ], + dependencies : dep) + +if want_fuzzer + libfuzzer_fe_common_core_a = static_library('fuzzer_fe_common_core', + fe_common_core_sources, + include_directories : rootinc, + implicit_include_directories : false, + c_args : [ + def_helpdir, + def_themesdir, + def_suppress_printf_fallback, + ], + dependencies : dep) +endif + +install_headers( + files( + 'chat-completion.h', + 'command-history.h', + 'completion.h', + 'fe-capsicum.h', + 'fe-channels.h', + 'fe-common-core.h', + 'fe-core-commands.h', + 'fe-exec.h', + 'fe-messages.h', + 'fe-queries.h', + 'fe-recode.h', + 'fe-settings.h', + 'fe-tls.h', + 'fe-windows.h', + 'formats.h', + 'hilight-text.h', + 'keyboard.h', + 'module-formats.h', + 'module.h', + 'printtext.h', + 'themes.h', + 'window-activity.h', + 'window-items.h', + 'windows-layout.h', + ), + subdir : incdir / 'src' / 'fe-common' / 'core') diff --git a/src/fe-common/core/module-formats.c b/src/fe-common/core/module-formats.c new file mode 100644 index 0000000..c4d9be9 --- /dev/null +++ b/src/fe-common/core/module-formats.c @@ -0,0 +1,321 @@ +/* + module-formats.c : irssi + + Copyright (C) 2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/core/formats.h> + +FORMAT_REC fecommon_core_formats[] = { + /* clang-format off */ + { MODULE_NAME, "Core", 0 }, + + /* ---- */ + { NULL, "Windows", 0 }, + + { "line_start", "{line_start}", 0 }, + { "line_start_irssi", "{line_start}{hilight Irssi:} ", 0 }, + { "timestamp", "{timestamp $Z} ", 0 }, + { "servertag", "[$0] ", 1, { 0 } }, + { "daychange", "Day changed to %%d %%b %%Y", 0 }, + { "talking_with", "You are now talking with {nick $0}", 1, { 0 } }, + { "refnum_too_low", "Window number must be greater than 1", 0 }, + { "error_server_sticky", "Window's server is sticky and it cannot be changed without -unsticky option", 0 }, + { "set_server_sticky", "Window's server set sticky", 1, { 0 } }, + { "unset_server_sticky", "Window's server isn't sticky anymore", 0 }, + { "window_name_not_unique", "Window names must be unique", 1, { 0 } }, + { "window_level", "Window level is $0", 1, { 0 } }, + { "window_set_immortal", "Window is immortal", 0 }, + { "window_unset_immortal", "Window isn't immortal", 0 }, + { "window_immortal_error", "Window is immortal, if you really want to close it, say /WINDOW IMMORTAL OFF", 0 }, + { "windowlist_header", "%#Ref Name Active item Server Level", 0 }, + { "windowlist_line", "%#$[4]0 %|$[20]1 $[15]2 $[15]3 $4", 5, { 1, 0, 0, 0, 0 } }, + { "windowlist_footer", "", 0 }, + { "windows_layout_saved", "Layout of windows is now remembered", 0 }, + { "windows_layout_reset", "Layout of windows reset to defaults", 0 }, + { "window_info_header", "", 0 }, + { "window_info_footer", "", 0 }, + { "window_info_refnum", "%#Window : {hilight #$0}", 1, { 1 } }, + { "window_info_refnum_sticky", "%#Window : {hilight #$0 (sticky)}", 1, { 1 } }, + { "window_info_name", "%#Name : $0", 1, { 0 } }, + { "window_info_history", "%#History : $0", 1, { 0 } }, + { "window_info_immortal", "%#Immortal: yes", 0 }, + { "window_info_size", "%#Size : $0x$1", 2, { 1, 1 } }, + { "window_info_level", "%#Level : $0", 1, { 0 } }, + { "window_info_server", "%#Server : $0", 1, { 0 } }, + { "window_info_server_sticky", "%#Server : $0 (sticky)", 1, { 0 } }, + { "window_info_theme", "%#Theme : $0$1", 2, { 0, 0 } }, + { "window_info_bound_items_header", "%#Bounds : {hilight Name Server tag}", 0 }, + { "window_info_bound_item", "%# : $[!30]0 $[!15]1 $2", 3, { 0, 0, 0 } }, + { "window_info_bound_items_footer", "", 0 }, + { "window_info_items_header", "%#Items : {hilight Name Server tag}", 0 }, + { "window_info_item", "%# $[7]0: $[!30]1 $2", 3, { 0, 0, 0 } }, + { "window_info_items_footer", "", 0 }, + + /* ---- */ + { NULL, "Server", 0 }, + + { "looking_up", "Looking up {server $0}", 1, { 0 } }, + { "connecting", "Connecting to {server $0} [$1] port {hilight $2}", 3, { 0, 0, 1 } }, + { "reconnecting", "Reconnecting to {server $0} [$1] port {hilight $2} - use /RMRECONNS to abort", 3, { 0, 0, 1 } }, + { "connection_established", "Connection to {server $0} established", 1, { 0 } }, + { "cant_connect", "Unable to connect server {server $0} port {hilight $1} {reason $2}", 3, { 0, 1, 0 } }, + { "connection_lost", "Connection lost to {server $0}", 1, { 0 } }, + { "lag_disconnected", "No PONG reply from server {server $0} in $1 seconds, disconnecting", 2, { 0, 1 } }, + { "disconnected", "Disconnected from {server $0} {reason $1}", 2, { 0, 0 } }, + { "server_quit", "Disconnecting from server {server $0}: {reason $1}", 2, { 0, 0 } }, + { "server_changed", "Changed to {hilight $2} server {server $1}", 3, { 0, 0, 0 } }, + { "unknown_server_tag", "Unknown server tag {server $0}", 1, { 0 } }, + { "no_connected_servers", "Not connected to any servers", 0 }, + { "server_list", "{server $0}: $1:$2 ($3)", 5, { 0, 0, 1, 0, 0 } }, + { "server_lookup_list", "{server $0}: $1:$2 ($3) (connecting...)", 5, { 0, 0, 1, 0, 0 } }, + { "server_reconnect_list", "{server $0}: $1:$2 ($3) ($5 left before reconnecting)", 6, { 0, 0, 1, 0, 0, 0 } }, + { "server_reconnect_removed", "Removed reconnection to server {server $0} port {hilight $1}", 3, { 0, 1, 0 } }, + { "server_reconnect_not_found", "Reconnection tag {server $0} not found", 1, { 0 } }, + { "setupserver_added", "Server {server $0} saved", 2, { 0, 1 } }, + { "setupserver_removed", "Server {server $0} {hilight $1} removed", 2, { 0, 1 } }, + { "setupserver_not_found", "Server {server $0} {hilight $1} not found", 2, { 0, 1 } }, + { "your_nick", "Your nickname is {nick $0}", 1, { 0 } }, + + /* ---- */ + { NULL, "Channels", 0 }, + + { "join", "{channick_hilight $0} {chanhost_hilight $1} has joined {channel $2}", 5, { 0, 0, 0, 0, 0 } }, + { "join_extended", "{channick_hilight $0} {chanhost_hilight $1} has joined {channel $2} {comment realname {reason $4}}", 5, { 0, 0, 0, 0, 0 } }, + { "join_extended_account", "{channick_hilight $0} {chanhost_hilight $1} has joined {channel $2} {reason account {hilight $3}} {comment realname {reason $4}}", 5, { 0, 0, 0, 0, 0 } }, + { "host_changed", "{channick_hilight $0} {chanhost_hilight $1} has changed host", 4, { 0, 0, 0, 0 } }, + { "logged_out", "{channick $0} {chanhost $1} has logged out of their account", 4, { 0, 0, 0, 0 } }, + { "logged_in", "{channick_hilight $0} {chanhost_hilight $1} has logged in to account {hilight $2}", 4, { 0, 0, 0, 0 } }, + { "part", "{channick $0} {chanhost $1} has left {channel $2} {reason $3}", 4, { 0, 0, 0, 0 } }, + { "kick", "{channick $0} was kicked from {channel $1} by {nick $2} {reason $3}", 5, { 0, 0, 0, 0, 0 } }, + { "quit", "{channick $0} {chanhost $1} has quit {reason $2}", 4, { 0, 0, 0, 0 } }, + { "quit_once", "{channel $3} {channick $0} {chanhost $1} has quit {reason $2}", 4, { 0, 0, 0, 0 } }, + { "invite", "{nick $0} invites you to {channel $1}", 3, { 0, 0, 0 } }, + { "not_invited", "You have not been invited to a channel!", 0 }, + { "invite_other", "{nick $0} has been invited to {channel $2} by {channick_hilight $1}", 4, { 0, 0, 0, 0 } }, + { "new_topic", "{nick $0} changed the topic of {channel $1} to: $2", 4, { 0, 0, 0, 0 } }, + { "topic_unset", "Topic unset by {nick $0} on {channel $1}", 4, { 0, 0, 0, 0 } }, + { "your_nick_changed", "You're now known as {nick $1}", 4, { 0, 0, 0, 0 } }, + { "nick_changed", "{channick $0} is now known as {channick_hilight $1}", 4, { 0, 0, 0, 0 } }, + { "notify_away_channel", "{channick $0} {chanhost $1} is now away: {reason $2}", 4, { 0, 0, 0, 0 } }, + { "notify_unaway_channel", "{channick_hilight $0} {chanhost $1} is no longer away", 4, { 0, 0, 0, 0 } }, + { "talking_in", "You are now talking in {channel $0}", 1, { 0 } }, + { "not_in_channels", "You are not on any channels", 0 }, + { "current_channel", "Current channel {channel $0}", 1, { 0 } }, + { "names", "{names_users Users {names_channel $0}}", 6, { 0, 1, 1, 1, 1, 1 } }, + { "names_prefix", "%#{names_prefix $0}", 1, { 0 } }, + { "names_nick_op", "{names_nick_op $0 $1}", 2, { 0, 0 } }, + { "names_nick_halfop", "{names_nick_halfop $0 $1}", 2, { 0, 0 } }, + { "names_nick_voice", "{names_nick_voice $0 $1}", 2, { 0, 0 } }, + { "names_nick", "{names_nick $0 $1}", 2, { 0, 0 } }, + { "endofnames", "{channel $0}: Total of {hilight $1} nicks {comment {hilight $2} ops, {hilight $3} halfops, {hilight $4} voices, {hilight $5} normal}", 6, { 0, 1, 1, 1, 1, 1 } }, + { "chanlist_header", "%#You are on the following channels:", 0 }, + { "chanlist_line", "%#{channel $[-10]0} %|+$1 ($2): $3", 4, { 0, 0, 0, 0 } }, + { "chansetup_not_found", "Channel {channel $0} not found", 2, { 0, 0 } }, + { "chansetup_added", "Channel {channel $0} saved", 2, { 0, 0 } }, + { "chansetup_removed", "Channel {channel $0} removed", 2, { 0, 0 } }, + { "chansetup_header", "%#Channel Network Password Settings", 0 }, + { "chansetup_line", "%#{channel $[15]0} %|$[10]1 $[10]2 $3", 4, { 0, 0, 0, 0 } }, + { "chansetup_footer", "", 0 }, + + /* ---- */ + { NULL, "Messages", 0 }, + + { "own_msg", "{ownmsgnick $2 {ownnick $0}}$1", 3, { 0, 0, 0 } }, + { "own_msg_channel", "{ownmsgnick $3 {ownnick $0}{msgchannel $1}}$2", 4, { 0, 0, 0, 0 } }, + { "own_msg_private", "{ownprivmsg msg $0}$1", 2, { 0, 0 } }, + { "own_msg_private_query", "{ownprivmsgnick {ownprivnick $2}}$1", 3, { 0, 0, 0 } }, + { "pubmsg_me", "{pubmsgmenick $2 {menick $0}}$1", 3, { 0, 0, 0 } }, + { "pubmsg_me_channel", "{pubmsgmenick $3 {menick $0}{msgchannel $1}}$2", 4, { 0, 0, 0, 0 } }, + { "pubmsg_hilight", "{pubmsghinick $0 $3 $1}$2", 4, { 0, 0, 0, 0 } }, + { "pubmsg_hilight_channel", "{pubmsghinick $0 $4 $1{msgchannel $2}}$3", 5, { 0, 0, 0, 0, 0 } }, + { "pubmsg", "{pubmsgnick $2 {pubnick $0}}$1", 3, { 0, 0, 0 } }, + { "pubmsg_channel", "{pubmsgnick $3 {pubnick $0}{msgchannel $1}}$2", 4, { 0, 0, 0, 0 } }, + { "msg_private", "{privmsg $0 $1}$2", 3, { 0, 0, 0 } }, + { "msg_private_query", "{privmsgnick $0}$2", 3, { 0, 0, 0 } }, + { "no_msgs_got", "You have not received a message from anyone yet", 0 }, + { "no_msgs_sent", "You have not sent a message to anyone yet", 0 }, + + /* ---- */ + { NULL, "Queries", 0 }, + + { "query_start", "Starting query in {server $1} with {nick $0}", 2, { 0, 0 } }, + { "query_stop", "Closing query with {nick $0}", 1, { 0 } }, + { "no_query", "No query with {nick $0}", 1, { 0 } }, + { "query_server_changed", "Query with {nick $0} changed to server {server $1}", 2, { 0, 0 } }, + + /* ---- */ + { NULL, "Highlighting", 0 }, + + { "hilight_header", "%#Highlights:", 0 }, + { "hilight_line", "%#$[-4]0 $1 $2 $3$4", 5, { 1, 0, 0, 0, 0 } }, + { "hilight_footer", "", 0 }, + { "hilight_not_found", "Highlight not found: $0", 1, { 0 } }, + { "hilight_removed", "Highlight removed: $0", 1, { 0 } }, + + /* ---- */ + { NULL, "Aliases", 0 }, + + { "alias_added", "Alias $0 added", 1, { 0 } }, + { "alias_removed", "Alias $0 removed", 1, { 0 } }, + { "alias_not_found", "No such alias: $0", 1, { 0 } }, + { "aliaslist_header", "%#Aliases:", 0 }, + { "aliaslist_line", "%#$[10]0 $1", 2, { 0, 0 } }, + { "aliaslist_footer", "", 0 }, + + /* ---- */ + { NULL, "Logging", 0 }, + + { "log_opened", "Log file {hilight $0} opened", 1, { 0 } }, + { "log_closed", "Log file {hilight $0} closed", 1, { 0 } }, + { "log_create_failed", "Couldn't create log file {hilight $0}: $1", 2, { 0, 0 } }, + { "log_locked", "Log file {hilight $0} is locked, probably by another running Irssi", 1, { 0 } }, + { "log_not_open", "Log file {hilight $0} not open", 1, { 0 } }, + { "log_started", "Started logging to file {hilight $0}", 1, { 0 } }, + { "log_stopped", "Stopped logging to file {hilight $0}", 1, { 0 } }, + { "log_list_header", "%#Logs:", 0 }, + { "log_list", "%#$0 $1: $2 $3$4$5", 6, { 1, 0, 0, 0, 0, 0 } }, + { "log_list_footer", "", 0 }, + { "windowlog_file", "Window LOGFILE set to $0", 1, { 0 } }, + { "windowlog_file_logging", "Can't change window's logfile while log is on", 0 }, + { "no_away_msgs", "No new messages in awaylog", 1, { 0 } }, + { "away_msgs", "{hilight $1} new messages in awaylog:", 2, { 0, 1 } }, + + /* ---- */ + { NULL, "Modules", 0 }, + + { "module_header", "%#Module Type Submodules", 0, }, + { "module_line", "%#$[!20]0 $[7]1 $2", 3, { 0, 0, 0 } }, + { "module_footer", "", 0, }, + { "module_already_loaded", "Module {hilight $0/$1} already loaded", 2, { 0, 0 } }, + { "module_not_loaded", "Module {hilight $0/$1} is not loaded", 2, { 0, 0 } }, + { "module_load_error", "Error loading module {hilight $0/$1}: $2", 3, { 0, 0, 0 } }, + { "module_version_mismatch", "{hilight $0/$1} is ABI version $2 but Irssi is version $abiversion, cannot load", 3, { 0, 0, 0 } }, + { "module_invalid", "{hilight $0/$1} isn't Irssi module", 2, { 0, 0 } }, + { "module_loaded", "Loaded module {hilight $0/$1}", 2, { 0, 0 } }, + { "module_unloaded", "Unloaded module {hilight $0/$1}", 2, { 0, 0 } }, + + /* ---- */ + { NULL, "Commands", 0 }, + + { "command_unknown", "Unknown command: $0", 1, { 0 } }, + { "command_ambiguous", "Ambiguous command: $0", 1, { 0 } }, + { "option_unknown", "Unknown option: $0", 1, { 0 } }, + { "option_ambiguous", "Ambiguous option: $0", 1, { 0 } }, + { "option_missing_arg", "Missing required argument for: $0", 1, { 0 } }, + { "not_enough_params", "Not enough parameters given", 0 }, + { "not_connected", "Not connected to server", 0 }, + { "not_joined", "Not joined to any channel", 0 }, + { "chan_not_found", "Not joined to such channel", 0 }, + { "chan_not_synced", "Channel not fully synchronized yet, try again after a while", 0 }, + { "illegal_proto", "Command isn't designed for the chat protocol of the active server", 0 }, + { "not_good_idea", "Doing this is not a good idea. Add -YES option to command if you really mean it", 0 }, + { "invalid_number", "Invalid number", 0 }, + { "invalid_time", "Invalid timestamp", 0 }, + { "invalid_level", "Invalid message level", 0 }, + { "invalid_size", "Invalid size", 0 }, + { "invalid_charset", "Invalid charset: $0", 1, { 0 } }, + { "invalid_choice", "Invalid choice, must be one of $0", 1, { 0 } }, + { "eval_max_recurse", "/eval hit maximum recursion limit", 0 }, + { "program_not_found", "Could not find file or file is not executable", 0 }, + { "no_server_defined", "No servers defined for this network, see /help server for how to add one", 0 }, + + /* ---- */ + { NULL, "Themes", 0 }, + + { "theme_saved", "Theme saved to $0", 1, { 0 } }, + { "theme_save_failed", "Error saving theme to $0: $1", 2, { 0, 0 } }, + { "theme_not_found", "Theme {hilight $0} not found", 1, { 0 } }, + { "theme_changed", "Now using theme {hilight $0} ($1)", 2, { 0, 0 } }, + { "window_theme", "Using theme {hilight $0} in this window", 2, { 0, 0 } }, + { "window_theme_default", "No theme is set for this window", 0 }, + { "window_theme_changed", "Now using theme {hilight $0} ($1) in this window", 2, { 0, 0 } }, + { "window_theme_removed", "Removed theme from this window", 0 }, + { "format_title", "%:[{hilight $0}] - [{hilight $1}]%:", 2, { 0, 0 } }, + { "format_subtitle", "[{hilight $0}]", 1, { 0 } }, + { "format_item", "$0 = $1", 2, { 0, 0 } }, + + /* ---- */ + { NULL, "Ignores", 0 }, + + { "ignored", "Ignoring {hilight $1} from {nick $0}", 2, { 0, 0 } }, + { "ignored_options", "Ignoring {hilight $1} from {nick $0} {comment $2}", 3, { 0, 0, 0 } }, + { "unignored", "Unignored {nick $0}", 1, { 0 } }, + { "ignore_not_found", "{nick $0} is not being ignored", 1, { 0 } }, + { "ignore_no_ignores", "There are no ignores", 0 }, + { "ignore_header", "%#Ignore List:", 0 }, + { "ignore_line", "%#$[-4]0 $1: $2 $3 $4", 4, { 1, 0, 0, 0 } }, + { "ignore_footer", "", 0 }, + + /* ---- */ + { NULL, "Recode", 0 }, + + { "not_channel_or_query", "The current window is not a channel or query window", 0 }, + { "conversion_added", "Added {hilight $0}/{hilight $1} to conversion database", 2, { FORMAT_STRING, FORMAT_STRING } }, + { "conversion_removed", "Removed {hilight $0} from conversion database", 1, { FORMAT_STRING } }, + { "conversion_not_found", "{hilight $0} not found in conversion database", 1, { FORMAT_STRING } }, + { "conversion_no_translits", "Transliterations not supported in this system", 0 }, + { "recode_header", "%#Target Character set", 0 }, + { "recode_line", "%#%|$[!30]0 $1", 2, { FORMAT_STRING, FORMAT_STRING } }, + + /* ---- */ + { NULL, "Misc", 0 }, + + { "unknown_chat_protocol", "Unknown chat protocol: $0", 1, { 0 } }, + { "unknown_chatnet", "Unknown chat network: $0 (create it with /NETWORK ADD)", 1, { 0 } }, + { "not_toggle", "Value must be either ON, OFF or TOGGLE", 0 }, + { "perl_error", "Perl error: $0", 1, { 0 } }, + { "bind_header", "%#Key Action", 0 }, + { "bind_list", "%#$[!20]0 $1 $2", 3, { 0, 0, 0 } }, + { "bind_command_list", "$[!30]0 $1", 2, { 0, 0 } }, + { "bind_footer", "", 0 }, + { "bind_unknown_id", "Unknown bind action: $0", 1, { 0 } }, + { "config_saved", "Saved configuration to file $0", 1, { 0 } }, + { "config_reloaded", "Reloaded configuration", 1, { 0 } }, + { "config_modified", "Configuration file was modified since irssi was last started - do you want to overwrite the possible changes?", 1, { 0 } }, + { "glib_error", "{error ($0) $1} $2", 3, { 0, 0, 0 } }, + { "overwrite_config", "Overwrite config (y/N)?", 0 }, + { "set_title", "[{hilight $0}]", 1, { 0 } }, + { "set_item", "$[-!32]0 %_$1", 2, { 0, 0 } }, + { "set_unknown", "Unknown setting $0", 1, { 0 } }, + { "set_not_boolean", "Setting {hilight $0} isn't boolean, use /SET", 1, { 0 } }, + { "no_completions", "There are no completions", 0 }, + { "completion_removed", "Removed completion $0", 1, { 0 } }, + { "completion_header", "%#Key Value Auto", 0 }, + { "completion_line", "%#$[10]0 $[!40]1 $2", 3, { 0, 0, 0 } }, + { "completion_footer", "", 0 }, + { "capsicum_enabled", "Capability mode enabled", 0 }, + { "capsicum_disabled", "Capability mode not enabled", 0 }, + { "capsicum_failed", "Capability mode failed: $0", 1, { 0 } }, + + /* ---- */ + { NULL, "TLS", 0 }, + + { "tls_ephemeral_key", "EDH Key: {hilight $0} bit {hilight $1}", 2, { 1, 0 } }, + { "tls_ephemeral_key_unavailable", "EDH Key: {error N/A}", 0 }, + { "tls_pubkey", "Public Key: {hilight $0} bit {hilight $1}, valid from {hilight $2} to {hilight $3}", 4, { 1, 0, 0, 0 } }, + { "tls_cert_header", "Certificate Chain:", 0 }, + { "tls_cert_subject", " Subject: {hilight $0}", 1, { 0 } }, + { "tls_cert_issuer", " Issuer: {hilight $0}", 1, { 0 } }, + { "tls_pubkey_fingerprint", "Public Key Fingerprint: {hilight $0} ({hilight $1})", 2, { 0, 0 } }, + { "tls_cert_fingerprint", "Certificate Fingerprint: {hilight $0} ({hilight $1})", 2, { 0, 0 } }, + { "tls_protocol_version", "Protocol: {hilight $0} ({hilight $1} bit, {hilight $2})", 3, { 0, 1, 0 } }, + + { NULL, NULL, 0 } + /* clang-format on */ +}; diff --git a/src/fe-common/core/module-formats.h b/src/fe-common/core/module-formats.h new file mode 100644 index 0000000..1b1927b --- /dev/null +++ b/src/fe-common/core/module-formats.h @@ -0,0 +1,283 @@ +#include <irssi/src/fe-common/core/formats.h> + +enum { + TXT_MODULE_NAME, + + TXT_FILL_1, + + TXT_LINE_START, + TXT_LINE_START_IRSSI, + TXT_TIMESTAMP, + TXT_SERVERTAG, + TXT_DAYCHANGE, + TXT_TALKING_WITH, + TXT_REFNUM_TOO_LOW, + TXT_ERROR_SERVER_STICKY, + TXT_SET_SERVER_STICKY, + TXT_UNSET_SERVER_STICKY, + TXT_WINDOW_NAME_NOT_UNIQUE, + TXT_WINDOW_LEVEL, + TXT_WINDOW_SET_IMMORTAL, + TXT_WINDOW_UNSET_IMMORTAL, + TXT_WINDOW_IMMORTAL_ERROR, + TXT_WINDOWLIST_HEADER, + TXT_WINDOWLIST_LINE, + TXT_WINDOWLIST_FOOTER, + TXT_WINDOWS_LAYOUT_SAVED, + TXT_WINDOWS_LAYOUT_RESET, + TXT_WINDOW_INFO_HEADER, + TXT_WINDOW_INFO_FOOTER, + TXT_WINDOW_INFO_REFNUM, + TXT_WINDOW_INFO_REFNUM_STICKY, + TXT_WINDOW_INFO_NAME, + TXT_WINDOW_INFO_HISTORY, + TXT_WINDOW_INFO_IMMORTAL, + TXT_WINDOW_INFO_SIZE, + TXT_WINDOW_INFO_LEVEL, + TXT_WINDOW_INFO_SERVER, + TXT_WINDOW_INFO_SERVER_STICKY, + TXT_WINDOW_INFO_THEME, + TXT_WINDOW_INFO_BOUND_ITEMS_HEADER, + TXT_WINDOW_INFO_BOUND_ITEM, + TXT_WINDOW_INFO_BOUND_ITEMS_FOOTER, + TXT_WINDOW_INFO_ITEMS_HEADER, + TXT_WINDOW_INFO_ITEM, + TXT_WINDOW_INFO_ITEMS_FOOTER, + + TXT_FILL_2, + + TXT_LOOKING_UP, + TXT_CONNECTING, + TXT_RECONNECTING, + TXT_CONNECTION_ESTABLISHED, + TXT_CANT_CONNECT, + TXT_CONNECTION_LOST, + TXT_LAG_DISCONNECTED, + TXT_DISCONNECTED, + TXT_SERVER_QUIT, + TXT_SERVER_CHANGED, + TXT_UNKNOWN_SERVER_TAG, + TXT_NO_CONNECTED_SERVERS, + TXT_SERVER_LIST, + TXT_SERVER_LOOKUP_LIST, + TXT_SERVER_RECONNECT_LIST, + TXT_RECONNECT_REMOVED, + TXT_RECONNECT_NOT_FOUND, + TXT_SETUPSERVER_ADDED, + TXT_SETUPSERVER_REMOVED, + TXT_SETUPSERVER_NOT_FOUND, + TXT_YOUR_NICK, + + TXT_FILL_3, + + TXT_JOIN, + TXT_JOIN_EXTENDED, + TXT_JOIN_EXTENDED_ACCOUNT, + TXT_HOST_CHANGED, + TXT_LOGGED_OUT, + TXT_LOGGED_IN, + TXT_PART, + TXT_KICK, + TXT_QUIT, + TXT_QUIT_ONCE, + TXT_INVITE, + TXT_NOT_INVITED, + TXT_INVITE_OTHER, + TXT_NEW_TOPIC, + TXT_TOPIC_UNSET, + TXT_YOUR_NICK_CHANGED, + TXT_NICK_CHANGED, + TXT_NOTIFY_AWAY_CHANNEL, + TXT_NOTIFY_UNAWAY_CHANNEL, + TXT_TALKING_IN, + TXT_NOT_IN_CHANNELS, + TXT_CURRENT_CHANNEL, + TXT_NAMES, + TXT_NAMES_PREFIX, + TXT_NAMES_NICK_OP, + TXT_NAMES_NICK_HALFOP, + TXT_NAMES_NICK_VOICE, + TXT_NAMES_NICK, + TXT_ENDOFNAMES, + TXT_CHANLIST_HEADER, + TXT_CHANLIST_LINE, + TXT_CHANSETUP_NOT_FOUND, + TXT_CHANSETUP_ADDED, + TXT_CHANSETUP_REMOVED, + TXT_CHANSETUP_HEADER, + TXT_CHANSETUP_LINE, + TXT_CHANSETUP_FOOTER, + + TXT_FILL_4, + + TXT_OWN_MSG, + TXT_OWN_MSG_CHANNEL, + TXT_OWN_MSG_PRIVATE, + TXT_OWN_MSG_PRIVATE_QUERY, + TXT_PUBMSG_ME, + TXT_PUBMSG_ME_CHANNEL, + TXT_PUBMSG_HILIGHT, + TXT_PUBMSG_HILIGHT_CHANNEL, + TXT_PUBMSG, + TXT_PUBMSG_CHANNEL, + TXT_MSG_PRIVATE, + TXT_MSG_PRIVATE_QUERY, + TXT_NO_MSGS_GOT, + TXT_NO_MSGS_SENT, + + TXT_FILL_5, + + TXT_QUERY_START, + TXT_QUERY_STOP, + TXT_NO_QUERY, + TXT_QUERY_SERVER_CHANGED, + + TXT_FILL_6, + + TXT_HILIGHT_HEADER, + TXT_HILIGHT_LINE, + TXT_HILIGHT_FOOTER, + TXT_HILIGHT_NOT_FOUND, + TXT_HILIGHT_REMOVED, + + TXT_FILL_7, + + TXT_ALIAS_ADDED, + TXT_ALIAS_REMOVED, + TXT_ALIAS_NOT_FOUND, + TXT_ALIASLIST_HEADER, + TXT_ALIASLIST_LINE, + TXT_ALIASLIST_FOOTER, + + TXT_FILL_8, + + TXT_LOG_OPENED, + TXT_LOG_CLOSED, + TXT_LOG_CREATE_FAILED, + TXT_LOG_LOCKED, + TXT_LOG_NOT_OPEN, + TXT_LOG_STARTED, + TXT_LOG_STOPPED, + TXT_LOG_LIST_HEADER, + TXT_LOG_LIST, + TXT_LOG_LIST_FOOTER, + TXT_WINDOWLOG_FILE, + TXT_WINDOWLOG_FILE_LOGGING, + TXT_LOG_NO_AWAY_MSGS, + TXT_LOG_AWAY_MSGS, + + TXT_FILL_9, + + TXT_MODULE_HEADER, + TXT_MODULE_LINE, + TXT_MODULE_FOOTER, + TXT_MODULE_ALREADY_LOADED, + TXT_MODULE_NOT_LOADED, + TXT_MODULE_LOAD_ERROR, + TXT_MODULE_VERSION_MISMATCH, + TXT_MODULE_INVALID, + TXT_MODULE_LOADED, + TXT_MODULE_UNLOADED, + + TXT_FILL_10, + + TXT_COMMAND_UNKNOWN, + TXT_COMMAND_AMBIGUOUS, + TXT_OPTION_UNKNOWN, + TXT_OPTION_AMBIGUOUS, + TXT_OPTION_MISSING_ARG, + TXT_NOT_ENOUGH_PARAMS, + TXT_NOT_CONNECTED, + TXT_NOT_JOINED, + TXT_CHAN_NOT_FOUND, + TXT_CHAN_NOT_SYNCED, + TXT_ILLEGAL_PROTO, + TXT_NOT_GOOD_IDEA, + TXT_INVALID_NUMBER, + TXT_INVALID_TIME, + TXT_INVALID_LEVEL, + TXT_INVALID_SIZE, + TXT_INVALID_CHARSET, + TXT_INVALID_CHOICE, + TXT_EVAL_MAX_RECURSE, + TXT_PROGRAM_NOT_FOUND, + TXT_NO_SERVER_DEFINED, + + TXT_FILL_11, + + TXT_THEME_SAVED, + TXT_THEME_SAVE_FAILED, + TXT_THEME_NOT_FOUND, + TXT_THEME_CHANGED, + TXT_WINDOW_THEME, + TXT_WINDOW_THEME_DEFAULT, + TXT_WINDOW_THEME_CHANGED, + TXT_WINDOW_THEME_REMOVED, + TXT_FORMAT_TITLE, + TXT_FORMAT_SUBTITLE, + TXT_FORMAT_ITEM, + + TXT_FILL_12, + + TXT_IGNORED, + TXT_IGNORED_OPTIONS, + TXT_UNIGNORED, + TXT_IGNORE_NOT_FOUND, + TXT_IGNORE_NO_IGNORES, + TXT_IGNORE_HEADER, + TXT_IGNORE_LINE, + TXT_IGNORE_FOOTER, + + TXT_FILL_13, + + TXT_NOT_CHANNEL_OR_QUERY, + TXT_CONVERSION_ADDED, + TXT_CONVERSION_REMOVED, + TXT_CONVERSION_NOT_FOUND, + TXT_CONVERSION_NO_TRANSLITS, + TXT_RECODE_HEADER, + TXT_RECODE_LINE, + + TXT_FILL_14, + + TXT_UNKNOWN_CHAT_PROTOCOL, + TXT_UNKNOWN_CHATNET, + TXT_NOT_TOGGLE, + TXT_PERL_ERROR, + TXT_BIND_HEADER, + TXT_BIND_LIST, + TXT_BIND_COMMAND_LIST, + TXT_BIND_FOOTER, + TXT_BIND_UNKNOWN_ID, + TXT_CONFIG_SAVED, + TXT_CONFIG_RELOADED, + TXT_CONFIG_MODIFIED, + TXT_GLIB_ERROR, + TXT_OVERWRITE_CONFIG, + TXT_SET_TITLE, + TXT_SET_ITEM, + TXT_SET_UNKNOWN, + TXT_SET_NOT_BOOLEAN, + TXT_NO_COMPLETIONS, + TXT_COMPLETION_REMOVED, + TXT_COMPLETION_HEADER, + TXT_COMPLETION_LINE, + TXT_COMPLETION_FOOTER, + TXT_CAPSICUM_ENABLED, + TXT_CAPSICUM_DISABLED, + TXT_CAPSICUM_FAILED, + + TLS_FILL_15, + + TXT_TLS_EPHEMERAL_KEY, + TXT_TLS_EPHEMERAL_KEY_UNAVAILBLE, + TXT_TLS_PUBKEY, + TXT_TLS_CERT_HEADER, + TXT_TLS_CERT_SUBJECT, + TXT_TLS_CERT_ISSUER, + TXT_TLS_PUBKEY_FINGERPRINT, + TXT_TLS_CERT_FINGERPRINT, + TXT_TLS_PROTOCOL_VERSION +}; + +extern FORMAT_REC fecommon_core_formats[]; diff --git a/src/fe-common/core/module.h b/src/fe-common/core/module.h new file mode 100644 index 0000000..e629dc5 --- /dev/null +++ b/src/fe-common/core/module.h @@ -0,0 +1,35 @@ +#include <irssi/src/common.h> + +#define MODULE_NAME "fe-common/core" + +#include <irssi/src/core/utf8.h> +typedef struct { + time_t time; + char *nick; + + /* channel specific msg to/from me - this is actually a reference + count. it begins from `completion_keep_publics' and is decreased + every time some nick is added to lastmsgs list. + + this is because of how the nick completion works. the same nick + is never in the lastmsgs list twice, but every time it's used + it's just moved to the beginning of the list. if this would be + just a boolean value the own-status would never be removed + from the nick if it didn't keep quiet for long enough. + + so, the own-status is rememberd only for the last + `completion_keep_publics' lines */ + int own; +} LAST_MSG_REC; + +typedef struct { + /* /MSG completion: */ + GSList *lastmsgs; /* list of nicks who sent you msg or + to who you send msg */ +} MODULE_SERVER_REC; + +typedef struct { + /* nick completion: */ + GSList *lastmsgs; /* list of nicks who sent latest msgs and + list of nicks who you sent msgs to */ +} MODULE_CHANNEL_REC; diff --git a/src/fe-common/core/printtext.c b/src/fe-common/core/printtext.c new file mode 100644 index 0000000..1865268 --- /dev/null +++ b/src/fe-common/core/printtext.c @@ -0,0 +1,566 @@ +/* + printtext.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/core/modules.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/core/levels.h> +#include <irssi/src/core/servers.h> + +#include <irssi/src/fe-common/core/themes.h> +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/core/printtext.h> + +static int beep_msg_level, beep_msg_level_ignore, beep_when_away, beep_when_window_active; + +static int signal_gui_print_text_finished; +static int signal_print_starting; +static int signal_print_text; +static int signal_print_format; +static int signal_print_noformat; +static int signal_window_hilight_check; + +static int sending_print_starting; + +static void print_line(TEXT_DEST_REC *dest, const char *text, int formatted); + +void printformat_module_dest_args(const char *module, TEXT_DEST_REC *dest, + int formatnum, va_list va) +{ + char *arglist[MAX_FORMAT_PARAMS]; + char buffer[DEFAULT_FORMAT_ARGLIST_SIZE]; + FORMAT_REC *formats; + + formats = g_hash_table_lookup(default_formats, module); + format_read_arglist(va, &formats[formatnum], + arglist, sizeof(arglist)/sizeof(char *), + buffer, sizeof(buffer)); + + printformat_module_dest_charargs(module, dest, formatnum, arglist); +} + +void printformat_module_dest_charargs(const char *module, TEXT_DEST_REC *dest, + int formatnum, char **arglist) +{ + THEME_REC *theme; + + theme = window_get_theme(dest->window); + + if (!sending_print_starting) { + sending_print_starting = TRUE; + signal_emit_id(signal_print_starting, 1, dest); + sending_print_starting = FALSE; + } + + signal_emit_id(signal_print_format, 5, theme, module, + dest, GINT_TO_POINTER(formatnum), arglist); +} + +void printformat_module_dest(const char *module, TEXT_DEST_REC *dest, + int formatnum, ...) +{ + va_list va; + + va_start(va, formatnum); + printformat_module_dest_args(module, dest, formatnum, va); + va_end(va); +} + +void printformat_module_args(const char *module, void *server, + const char *target, int level, + int formatnum, va_list va) +{ + TEXT_DEST_REC dest; + + format_create_dest(&dest, server, target, level, NULL); + printformat_module_dest_args(module, &dest, formatnum, va); +} + +void printformat_module(const char *module, void *server, const char *target, + int level, int formatnum, ...) +{ + va_list va; + + va_start(va, formatnum); + printformat_module_args(module, server, target, level, formatnum, va); + va_end(va); +} + +void printformat_module_window_args(const char *module, WINDOW_REC *window, + int level, int formatnum, va_list va) +{ + TEXT_DEST_REC dest; + + format_create_dest(&dest, NULL, NULL, level, window); + printformat_module_dest_args(module, &dest, formatnum, va); +} + +void printformat_module_window(const char *module, WINDOW_REC *window, + int level, int formatnum, ...) +{ + va_list va; + + va_start(va, formatnum); + printformat_module_window_args(module, window, level, formatnum, va); + va_end(va); +} + +void printformat_module_gui_args(const char *module, int formatnum, va_list va) +{ + TEXT_DEST_REC dest; + char *arglist[MAX_FORMAT_PARAMS]; + char buffer[DEFAULT_FORMAT_ARGLIST_SIZE]; + FORMAT_REC *formats; + char *str; + + g_return_if_fail(module != NULL); + + memset(&dest, 0, sizeof(dest)); + + formats = g_hash_table_lookup(default_formats, module); + format_read_arglist(va, &formats[formatnum], + arglist, sizeof(arglist)/sizeof(char *), + buffer, sizeof(buffer)); + + str = format_get_text_theme_charargs(window_get_theme(dest.window), + module, &dest, + formatnum, arglist); + if (*str != '\0') format_send_to_gui(&dest, str); + g_free(str); +} + +void printformat_module_gui(const char *module, int formatnum, ...) +{ + va_list va; + + va_start(va, formatnum); + printformat_module_gui_args(module, formatnum, va); + va_end(va); +} + +/* append string to `out', expand newlines. */ +static void printtext_append_str(TEXT_DEST_REC *dest, GString *out, + const char *str) +{ + while (*str != '\0') { + if (*str != '\n') + g_string_append_c(out, *str); + else { + print_line(dest, out->str, 0); + g_string_truncate(out, 0); + } + str++; + } +} + +static char *printtext_get_args(TEXT_DEST_REC *dest, const char *str, + va_list va) +{ + GString *out; + char *ret; + int adv; + + out = g_string_new(NULL); + for (; *str != '\0'; str++) { + if (*str != '%') { + g_string_append_c(out, *str); + continue; + } + + if (*++str == '\0') + break; + + /* standard parameters */ + switch (*str) { + case 's': { + char *s = (char *) va_arg(va, char *); + if (s && *s) printtext_append_str(dest, out, s); + break; + } + case 'd': { + int d = (int) va_arg(va, int); + g_string_append_printf(out, "%d", d); + break; + } + case 'f': { + double f = (double) va_arg(va, double); + g_string_append_printf(out, "%0.2f", f); + break; + } + case 'u': { + unsigned int d = + (unsigned int) va_arg(va, unsigned int); + g_string_append_printf(out, "%u", d); + break; + } + case 'l': { + long d = (long) va_arg(va, long); + + if (*++str != 'd' && *str != 'u') { + g_string_append_printf(out, "%ld", d); + str--; + } else { + if (*str == 'd') + g_string_append_printf(out, "%ld", d); + else + g_string_append_printf(out, "%lu", d); + } + break; + } + default: + adv = format_expand_styles(out, &str, &dest->flags); + if (!adv) { + g_string_append_c(out, '%'); + g_string_append_c(out, *str); + } else { + str += adv - 1; + } + break; + } + } + + ret = out->str; + g_string_free(out, FALSE); + return ret; +} + +static char *printtext_expand_formats(const char *str, int *flags) +{ + GString *out; + char *ret; + int adv; + + out = g_string_new(NULL); + for (; *str != '\0'; str++) { + if (*str != '%') { + g_string_append_c(out, *str); + continue; + } + + if (*++str == '\0') + break; + + adv = format_expand_styles(out, &str, flags); + if (!adv) { + g_string_append_c(out, '%'); + g_string_append_c(out, *str); + } else { + str += adv - 1; + } + } + + ret = out->str; + g_string_free(out, FALSE); + return ret; +} + +static void printtext_dest_args(TEXT_DEST_REC *dest, const char *text, va_list va) +{ + char *str; + + if (!sending_print_starting) { + sending_print_starting = TRUE; + signal_emit_id(signal_print_starting, 1, dest); + sending_print_starting = FALSE; + } + + str = printtext_get_args(dest, text, va); + print_line(dest, str, 0); + g_free(str); +} + +void printtext_dest(TEXT_DEST_REC *dest, const char *text, ...) +{ + va_list va; + + va_start(va, text); + printtext_dest_args(dest, text, va); + va_end(va); +} + +/* Write text to target - convert color codes */ +void printtext(void *server, const char *target, int level, const char *text, ...) +{ + TEXT_DEST_REC dest; + va_list va; + + g_return_if_fail(text != NULL); + + format_create_dest(&dest, server, target, level, NULL); + + va_start(va, text); + printtext_dest_args(&dest, text, va); + va_end(va); +} + +/* Like printtext(), but don't handle %s etc. */ +void printtext_string(void *server, const char *target, int level, const char *text) +{ + TEXT_DEST_REC dest; + char *str; + + g_return_if_fail(text != NULL); + + format_create_dest(&dest, server, target, level, NULL); + + if (!sending_print_starting) { + sending_print_starting = TRUE; + signal_emit_id(signal_print_starting, 1, &dest); + sending_print_starting = FALSE; + } + + str = printtext_expand_formats(text, &dest.flags); + print_line(&dest, str, 0); + g_free(str); +} + +/* Like printtext_window(), but don't handle %s etc. */ +void printtext_string_window(WINDOW_REC *window, int level, const char *text) +{ + TEXT_DEST_REC dest; + char *str; + + g_return_if_fail(text != NULL); + + format_create_dest(&dest, NULL, NULL, level, + window != NULL ? window : active_win); + + if (!sending_print_starting) { + sending_print_starting = TRUE; + signal_emit_id(signal_print_starting, 1, &dest); + sending_print_starting = FALSE; + } + + str = printtext_expand_formats(text, &dest.flags); + print_line(&dest, str, 0); + g_free(str); +} + +void printtext_window(WINDOW_REC *window, int level, const char *text, ...) +{ + TEXT_DEST_REC dest; + va_list va; + + g_return_if_fail(text != NULL); + + format_create_dest(&dest, NULL, NULL, level, + window != NULL ? window : active_win); + + va_start(va, text); + printtext_dest_args(&dest, text, va); + va_end(va); +} + +void printtext_gui(const char *text) +{ + TEXT_DEST_REC dest; + char *str; + + g_return_if_fail(text != NULL); + + memset(&dest, 0, sizeof(dest)); + + str = printtext_expand_formats(text, &dest.flags); + format_send_to_gui(&dest, str); + g_free(str); +} + +/* Like printtext_gui(), but don't expand % codes. */ +void printtext_gui_internal(const char *str) +{ + TEXT_DEST_REC dest; + + g_return_if_fail(str != NULL); + + memset(&dest, 0, sizeof(dest)); + + format_send_to_gui(&dest, str); +} + +static void msg_beep_check(TEXT_DEST_REC *dest) +{ + if (dest->level != 0 && (dest->level & MSGLEVEL_NO_ACT) == 0 && + (beep_msg_level & dest->level) && + (beep_when_away || (dest->server != NULL && + !dest->server->usermode_away)) && + (beep_when_window_active || dest->window != active_win)) { + if (beep_msg_level_ignore & MSGLEVEL_HIDDEN) { + int cb_ignore = 0; + if (signal_emit_id(signal_window_hilight_check, 4, dest, NULL, NULL, + &cb_ignore), + cb_ignore) { + return; + } + } else if (beep_msg_level_ignore & dest->level) { + return; + } + + signal_emit("beep", 0); + } +} + +static void sig_print_text(TEXT_DEST_REC *dest, const char *text) +{ + THEME_REC *theme; + char *str, *tmp; + + g_return_if_fail(dest != NULL); + g_return_if_fail(text != NULL); + + if (dest->window == NULL) { + str = strip_codes(text); +#ifndef SUPPRESS_PRINTF_FALLBACK + printf("## NO WINDOWS: %s\n", str); +#endif + g_free(str); + return; + } + + msg_beep_check(dest); + + if ((dest->level & MSGLEVEL_NEVER) == 0) + dest->window->last_line = time(NULL); + + /* add timestamp/server tag here - if it's done in print_line() + it would be written to log files too */ + theme = window_get_theme(dest->window); + tmp = format_get_line_start(theme, dest, time(NULL)); + str = !theme->info_eol ? format_add_linestart(text, tmp) : + format_add_lineend(text, tmp); + + g_free_not_null(tmp); + + format_send_to_gui(dest, str); + g_free(str); + + signal_emit_id(signal_gui_print_text_finished, 2, dest->window, dest); +} + +static void sig_print_format(THEME_REC *theme, const char *module, TEXT_DEST_REC *dest, + void *formatnump, char **arglist) +{ + int formatnum; + char *str; + + formatnum = GPOINTER_TO_INT(formatnump); + str = format_get_text_theme_charargs(theme, module, dest, formatnum, arglist); + if (str != NULL && *str != '\0') + print_line(dest, str, 1); + + g_free(str); +} + +static void sig_print_noformat(TEXT_DEST_REC *dest, const char *text) +{ + THEME_REC *theme; + char *str, *tmp, *stripped; + + theme = window_get_theme(dest->window); + tmp = format_get_level_tag(theme, dest); + str = !theme->info_eol ? format_add_linestart(text, tmp) : format_add_lineend(text, tmp); + g_free_not_null(tmp); + + /* send both the formatted + stripped (for logging etc.) */ + stripped = strip_codes(str); + signal_emit_id(signal_print_text, 3, dest, str, stripped); + + g_free_and_null(dest->hilight_color); + + g_free(str); + g_free(stripped); +} + +static void print_line(TEXT_DEST_REC *dest, const char *text, int formatted) +{ + g_return_if_fail(dest != NULL); + g_return_if_fail(text != NULL); + + if (!formatted) + signal_emit_id(signal_print_noformat, 2, dest, text); + else + sig_print_noformat(dest, text); +} + +void printtext_multiline(void *server, const char *target, int level, + const char *format, const char *text) +{ + char **lines, **tmp; + + g_return_if_fail(format != NULL); + g_return_if_fail(text != NULL); + + lines = g_strsplit(text, "\n", -1); + for (tmp = lines; *tmp != NULL; tmp++) + printtext(NULL, NULL, level, format, *tmp); + g_strfreev(lines); +} + +static void sig_gui_dialog(const char *type, const char *text) +{ + char *format; + + if (g_ascii_strcasecmp(type, "warning") == 0) + format = "%_Warning:%_ %s"; + else if (g_ascii_strcasecmp(type, "error") == 0) + format = "%_Error:%_ %s"; + else + format = "%s"; + + printtext_multiline(NULL, NULL, MSGLEVEL_NEVER, format, text); +} + +static void read_settings(void) +{ + beep_msg_level = settings_get_level("beep_msg_level"); + beep_msg_level_ignore = settings_get_level_negative("beep_msg_level"); + beep_when_away = settings_get_bool("beep_when_away"); + beep_when_window_active = settings_get_bool("beep_when_window_active"); +} + +void printtext_init(void) +{ + sending_print_starting = FALSE; + signal_gui_print_text_finished = signal_get_uniq_id("gui print text finished"); + signal_print_starting = signal_get_uniq_id("print starting"); + signal_print_text = signal_get_uniq_id("print text"); + signal_print_format = signal_get_uniq_id("print format"); + signal_print_noformat = signal_get_uniq_id("print noformat"); + signal_window_hilight_check = signal_get_uniq_id("window hilight check"); + + read_settings(); + signal_add("print text", (SIGNAL_FUNC) sig_print_text); + signal_add("print format", (SIGNAL_FUNC) sig_print_format); + signal_add("print noformat", (SIGNAL_FUNC) sig_print_noformat); + signal_add("gui dialog", (SIGNAL_FUNC) sig_gui_dialog); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); +} + +void printtext_deinit(void) +{ + signal_remove("print text", (SIGNAL_FUNC) sig_print_text); + signal_remove("print format", (SIGNAL_FUNC) sig_print_format); + signal_remove("print noformat", (SIGNAL_FUNC) sig_print_noformat); + signal_remove("gui dialog", (SIGNAL_FUNC) sig_gui_dialog); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); +} diff --git a/src/fe-common/core/printtext.h b/src/fe-common/core/printtext.h new file mode 100644 index 0000000..c223a3e --- /dev/null +++ b/src/fe-common/core/printtext.h @@ -0,0 +1,45 @@ +#ifndef IRSSI_FE_COMMON_CORE_PRINTTEXT_H +#define IRSSI_FE_COMMON_CORE_PRINTTEXT_H + +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/core/formats.h> + +void printformat_module(const char *module, void *server, const char *target, int level, int formatnum, ...); +void printformat_module_window(const char *module, WINDOW_REC *window, int level, int formatnum, ...); +void printformat_module_dest(const char *module, TEXT_DEST_REC *dest, int formatnum, ...); + +void printformat_module_args(const char *module, void *server, const char *target, int level, int formatnum, va_list va); +void printformat_module_window_args(const char *module, WINDOW_REC *window, int level, int formatnum, va_list va); +void printformat_module_dest_args(const char *module, TEXT_DEST_REC *dest, int formatnum, va_list va); +void printformat_module_dest_charargs(const char *module, TEXT_DEST_REC *dest, int formatnum, char **arglist); + +void printtext(void *server, const char *target, int level, const char *text, ...); +void printtext_string(void *server, const char *target, int level, const char *text); +void printtext_string_window(WINDOW_REC *window, int level, const char *text); +void printtext_window(WINDOW_REC *window, int level, const char *text, ...); +void printtext_multiline(void *server, const char *target, int level, const char *format, const char *text); +void printtext_dest(TEXT_DEST_REC *dest, const char *text, ...); + +/* only GUI should call these - used for printing text to somewhere else + than windows */ +void printtext_gui(const char *text); +void printtext_gui_internal(const char *str); +void printformat_module_gui(const char *module, int formatnum, ...); +void printformat_module_gui_args(const char *module, int formatnum, va_list va); + +void printtext_init(void); +void printtext_deinit(void); + +/* printformat(...) = printformat_format(MODULE_NAME, ...) + + Irssi requires a C99 pre-processor with __VA_ARGS__ support */ +# define printformat(server, target, level, formatnum, ...) \ + printformat_module(MODULE_NAME, server, target, level, formatnum, ##__VA_ARGS__) +# define printformat_window(window, level, formatnum, ...) \ + printformat_module_window(MODULE_NAME, window, level, formatnum, ##__VA_ARGS__) +# define printformat_dest(dest, formatnum, ...) \ + printformat_module_dest(MODULE_NAME, dest, formatnum, ##__VA_ARGS__) +# define printformat_gui(formatnum, ...) \ + printformat_module_gui(MODULE_NAME, formatnum, ##__VA_ARGS__) + +#endif diff --git a/src/fe-common/core/themes.c b/src/fe-common/core/themes.c new file mode 100644 index 0000000..04f0047 --- /dev/null +++ b/src/fe-common/core/themes.c @@ -0,0 +1,1488 @@ +/* + themes.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/special-vars.h> +#include <irssi/src/lib-config/iconfig.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/fe-common/core/themes.h> +#include <irssi/src/fe-common/core/printtext.h> + +#include "default-theme.h" + +GSList *themes; +THEME_REC *current_theme; +GHashTable *default_formats; + +static int init_finished; +static char *init_errors; +static THEME_REC *internal_theme; + +static int theme_read(THEME_REC *theme, const char *path); + +THEME_REC *theme_create(const char *path, const char *name) +{ + THEME_REC *rec; + + g_return_val_if_fail(path != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + + rec = g_new0(THEME_REC, 1); + rec->refcount = 1; + rec->path = g_strdup(path); + rec->name = g_strdup(name); + rec->abstracts = g_hash_table_new((GHashFunc) g_str_hash, + (GCompareFunc) g_str_equal); + rec->modules = g_hash_table_new((GHashFunc) i_istr_hash, (GCompareFunc) i_istr_equal); + themes = g_slist_append(themes, rec); + signal_emit("theme created", 1, rec); + + return rec; +} + +static void theme_abstract_destroy(char *key, char *value) +{ + g_free(key); + g_free(value); +} + +static void theme_module_destroy(const char *key, MODULE_THEME_REC *rec) +{ + int n; + + for (n = 0; n < rec->count; n++) { + g_free_not_null(rec->formats[n]); + g_free_not_null(rec->expanded_formats[n]); + } + g_free(rec->formats); + g_free(rec->expanded_formats); + + g_free(rec->name); + g_free(rec); +} + +static void theme_real_destroy(THEME_REC *rec) +{ + g_hash_table_foreach(rec->abstracts, (GHFunc) theme_abstract_destroy, NULL); + g_hash_table_destroy(rec->abstracts); + g_hash_table_foreach(rec->modules, (GHFunc) theme_module_destroy, NULL); + g_hash_table_destroy(rec->modules); + + g_slist_foreach(rec->replace_values, (GFunc) g_free, NULL); + g_slist_free(rec->replace_values); + + g_free(rec->path); + g_free(rec->name); + g_free(rec); +} + +static void theme_unref(THEME_REC *rec) +{ + if (--rec->refcount == 0) + theme_real_destroy(rec); +} + +void theme_destroy(THEME_REC *rec) +{ + themes = g_slist_remove(themes, rec); + signal_emit("theme destroyed", 1, rec); + + theme_unref(rec); +} + +static char *theme_replace_expand(THEME_REC *theme, int index, + theme_rm_col default_fg, theme_rm_col default_bg, + theme_rm_col *last_fg, theme_rm_col *last_bg, + char chr, int flags) +{ + GSList *rec; + char *ret, *abstract, data[2]; + + rec = g_slist_nth(theme->replace_values, index); + g_return_val_if_fail(rec != NULL, NULL); + + data[0] = chr; data[1] = '\0'; + + abstract = rec->data; + abstract = theme_format_expand_data(theme, (const char **) &abstract, + default_fg, default_bg, + last_fg, last_bg, (flags | EXPAND_FLAG_IGNORE_REPLACES)); + ret = parse_special_string(abstract, NULL, NULL, data, NULL, + PARSE_FLAG_ONLY_ARGS); + g_free(abstract); + return ret; +} + +static const char *fgcolorformats = "nkrgybmpcwKRGYBMPCW"; +static const char *bgcolorformats = "n01234567"; + +#define IS_FGCOLOR_FORMAT(c) \ + ((c) != '\0' && strchr(fgcolorformats, c) != NULL) +#define IS_BGCOLOR_FORMAT(c) \ + ((c) != '\0' && strchr(bgcolorformats, c) != NULL) + +/* append "variable" part in $variable, ie. not the contents of the variable */ +static void theme_format_append_variable(GString *str, const char **format) +{ + const char *orig; + char *value, *args[1] = { NULL }; + int free_ret; + + orig = *format; + (*format)++; + + value = parse_special((char **) format, NULL, NULL, + args, &free_ret, NULL, PARSE_FLAG_ONLY_ARGS); + if (free_ret) g_free(value); + + if (**format != '\0') + (*format)++; + + /* append the variable name */ + value = g_strndup(orig, (int) (*format-orig)); + g_string_append(str, value); + g_free(value); +} + +static inline int chr_is_valid_rgb(const char format[]) +{ + int tmp; + for (tmp = 1; tmp < 7; ++tmp) { + if (!isxdigit(format[tmp])) + return tmp; + } + return 0; +} + +static inline int chr_is_valid_ext(const char format[]) +{ + if (format[1] < '0' || format[1] > '7') + return 1; + + if (format[1] == '7') { + if (!isalpha(format[2]) || format[2] == 'y' || format[2] == 'Y' + || format[2] =='z' || format[2] == 'Z') + return 2; + } else if (format[1] == '0') { + if (!isxdigit(format[2])) + return 2; + } else if (!isalnum(format[2])) + return 2; + + return 0; +} + +/* append next "item", either a character, $variable or %format */ +static void theme_format_append_next(THEME_REC *theme, GString *str, + const char **format, + theme_rm_col default_fg, theme_rm_col default_bg, + theme_rm_col *last_fg, theme_rm_col *last_bg, + int flags) +{ + int index; + unsigned char chr; + char *t; + + chr = **format; + if ((chr == '$' || chr == '%') && + (*format)[1] == '\0') { + /* last char, always append */ + g_string_append_c(str, chr); + (*format)++; + return; + } + + if (chr == '$') { + /* $variable .. we'll always need to skip this, since it + may contain characters that are in replace chars. */ + theme_format_append_variable(str, format); + return; + } + + if (**format == '%') { + /* format */ + (*format)++; + if (**format != '{' && **format != '}') { + chr = **format; + if (**format == 'n') { + /* %n = change to default color */ + g_string_append(str, "%n"); + + if (default_bg.m[0] != 'n') { + g_string_append_c(str, '%'); + g_string_append(str, default_bg.m); + } + if (default_fg.m[0] != 'n') { + g_string_append_c(str, '%'); + g_string_append(str, default_fg.m); + } + + *last_fg = default_fg; + *last_bg = default_bg; + } else if (chr == 'z' || chr == 'Z') { + if (chr_is_valid_rgb(*format) == 0) { + t = chr == 'z' ? (*last_bg).m : (*last_fg).m; + strncpy(t, *format, 7); + t[7] = '\0'; + g_string_append_c(str, '%'); + g_string_append(str, t); + (*format)+=6; + } else { + g_string_append(str, "%%"); + g_string_append_c(str, **format); + } + } else if (chr == 'x' || chr == 'X') { + if (chr_is_valid_ext(*format) == 0) { + t = chr == 'x' ? (*last_bg).m : (*last_fg).m; + strncpy(t, *format, 3); + t[3] = '\0'; + g_string_append_c(str, '%'); + g_string_append(str, t); + (*format)+=2; + } else { + g_string_append(str, "%%"); + g_string_append_c(str, **format); + } + } else { + if (IS_FGCOLOR_FORMAT(chr)) { + (*last_fg).m[0] = chr; + (*last_fg).m[1] = '\0'; + } + if (IS_BGCOLOR_FORMAT(chr)) { + (*last_bg).m[0] = chr; + (*last_bg).m[1] = '\0'; + } + g_string_append_c(str, '%'); + g_string_append_c(str, chr); + } + (*format)++; + return; + } + + /* %{ or %} gives us { or } char - keep the % char + though to make sure {} isn't treated as abstract */ + g_string_append_c(str, '%'); + chr = **format; + } + + index = (flags & EXPAND_FLAG_IGNORE_REPLACES) ? -1 : + theme->replace_keys[(int) (unsigned char) chr]; + if (index == -1) + g_string_append_c(str, chr); + else { + char *value; + + value = theme_replace_expand(theme, index, + default_fg, default_bg, + last_fg, last_bg, chr, flags); + g_string_append(str, value); + g_free(value); + } + + (*format)++; +} + +/* returns TRUE if data is empty, or the data is a $variable which is empty */ +static int data_is_empty(const char **data) +{ + /* since we don't know the real argument list, assume there's always + an argument in them */ + static char *arglist[] = { + "x", "x", "x", "x", "x", "x","x", "x", "x", "x", + NULL + }; + SERVER_REC *server; + const char *p; + char *ret; + int free_ret, empty; + + p = *data; + while (*p == ' ') p++; + + if (*p == '}') { + /* empty */ + *data = p+1; + return TRUE; + } + + if (*p != '$') { + /* not empty */ + return FALSE; + } + + /* variable - check if it's empty */ + p++; + + server = active_win == NULL ? NULL : + active_win->active_server != NULL ? + active_win->active_server : active_win->connect_server; + + ret = parse_special((char **) &p, server, + active_win == NULL ? NULL : active_win->active, + arglist, &free_ret, NULL, 0); + p++; + + while (*p == ' ') p++; + empty = *p == '}' && (ret == NULL || *ret == '\0'); + if (free_ret) g_free(ret); + + if (empty) { + /* empty */ + *data = p+1; + return TRUE; + } + + return FALSE; +} + +/* return "data" from {abstract data} string */ +char *theme_format_expand_get(THEME_REC *theme, const char **format) +{ + GString *str; + char *ret; + theme_rm_col dummy, reset; + int braces = 1; /* we start with one brace opened */ + dummy.m[0] = '\0'; + strcpy(reset.m, "n"); + + str = g_string_new(NULL); + while (**format != '\0' && braces != 0) { + if (**format == '{') + braces++; + else if (**format == '}') + braces--; + else if ((braces > 1) && (**format == ' ')) { + g_string_append(str, "\\x20"); + (*format)++; + continue; + } else { + theme_format_append_next(theme, str, format, + reset, reset, + &dummy, &dummy, + EXPAND_FLAG_IGNORE_REPLACES); + continue; + } + + if (braces == 0) { + (*format)++; + break; + } + + g_string_append_c(str, **format); + (*format)++; + } + + ret = str->str; + g_string_free(str, FALSE); + return ret; +} + +static char *theme_format_expand_data_rec(THEME_REC *theme, const char **format, + theme_rm_col default_fg, theme_rm_col default_bg, + theme_rm_col *save_last_fg, theme_rm_col *save_last_bg, + int flags, GTree *block_list); + +/* expand a single {abstract ...data... } */ +static char *theme_format_expand_abstract(THEME_REC *theme, const char **formatp, + theme_rm_col *last_fg, theme_rm_col *last_bg, int flags, + GTree *block_list) +{ + GString *str; + const char *p, *format; + char *abstract, *data, *ret, *blocking; + theme_rm_col default_fg, default_bg; + int len; + + format = *formatp; + default_fg = *last_fg; + default_bg = *last_bg; + + /* get abstract name first */ + p = format; + while (*p != '\0' && *p != ' ' && + *p != '{' && *p != '}') p++; + if (*p == '\0' || p == format) + return NULL; /* error */ + + len = (int) (p-format); + abstract = g_strndup(format, len); + + /* skip the following space, if there's any more spaces they're + treated as arguments */ + if (*p == ' ') { + len++; + if ((flags & EXPAND_FLAG_IGNORE_EMPTY) && data_is_empty(&p)) { + *formatp = p; + g_free(abstract); + return NULL; + } + } + *formatp = format+len; + + if (block_list == NULL) { + block_list = g_tree_new_full((GCompareDataFunc) g_strcmp0, NULL, g_free, NULL); + } else { + g_tree_ref(block_list); + } + + /* get the abstract data */ + data = g_hash_table_lookup(theme->abstracts, abstract); + if (data == NULL || g_tree_lookup(block_list, abstract) != NULL) { + /* unknown abstract, just display the data */ + data = "$0-"; + g_free(abstract); + blocking = NULL; + } else { + blocking = abstract; + g_tree_insert(block_list, blocking, blocking); + } + abstract = g_strdup(data); + + /* we'll need to get the data part. it may contain + more abstracts, they are _NOT_ expanded. */ + data = theme_format_expand_get(theme, formatp); + len = strlen(data); + + if (len > 1 && i_isdigit(data[len-1]) && data[len-2] == '$') { + /* ends with $<digit> .. this breaks things if next + character is digit or '-' */ + char digit, *tmp; + + tmp = data; + digit = tmp[len-1]; + tmp[len-1] = '\0'; + + data = g_strdup_printf("%s{%c}", tmp, digit); + g_free(tmp); + } + + ret = parse_special_string(abstract, NULL, NULL, data, NULL, + PARSE_FLAG_ONLY_ARGS); + g_free(abstract); + g_free(data); + str = g_string_new(NULL); + p = ret; + while (*p != '\0') { + if (*p == '\\' && p[1] != '\0') { + int chr; + p++; + chr = expand_escape(&p); + g_string_append_c(str, chr != -1 ? chr : *p); + } else + g_string_append_c(str, *p); + p++; + } + g_free(ret); + abstract = str->str; + g_string_free(str, FALSE); + + /* abstract may itself contain abstracts or replaces */ + p = abstract; + ret = theme_format_expand_data_rec(theme, &p, default_fg, default_bg, last_fg, last_bg, + flags | EXPAND_FLAG_LASTCOLOR_ARG, block_list); + g_free(abstract); + if (blocking != NULL) { + g_tree_remove(block_list, blocking); + } + g_tree_unref(block_list); + return ret; +} + +static char *theme_format_expand_data_rec(THEME_REC *theme, const char **format, + theme_rm_col default_fg, theme_rm_col default_bg, + theme_rm_col *save_last_fg, theme_rm_col *save_last_bg, + int flags, GTree *block_list) +{ + GString *str; + char *ret, *abstract; + theme_rm_col last_fg, last_bg; + int recurse_flags; + + last_fg = default_fg; + last_bg = default_bg; + recurse_flags = flags & EXPAND_FLAG_RECURSIVE_MASK; + + str = g_string_new(NULL); + while (**format != '\0') { + if ((flags & EXPAND_FLAG_ROOT) == 0 && **format == '}') { + /* ignore } if we're expanding original string */ + (*format)++; + break; + } + + if (**format != '{') { + if ((flags & EXPAND_FLAG_LASTCOLOR_ARG) && + **format == '$' && (*format)[1] == '0') { + /* save the color before $0 .. + this is for the %n replacing */ + if (save_last_fg != NULL) { + *save_last_fg = last_fg; + save_last_fg = NULL; + } + if (save_last_bg != NULL) { + *save_last_bg = last_bg; + save_last_bg = NULL; + } + } + + theme_format_append_next(theme, str, format, + default_fg, default_bg, + &last_fg, &last_bg, + recurse_flags); + continue; + } + + (*format)++; + if (**format == '\0' || **format == '}') + break; /* error */ + + /* get a single {...} */ + abstract = theme_format_expand_abstract(theme, format, &last_fg, &last_bg, + recurse_flags, block_list); + if (abstract != NULL) { + g_string_append(str, abstract); + g_free(abstract); + } + } + + /* save the last color */ + if (save_last_fg != NULL) + *save_last_fg = last_fg; + if (save_last_bg != NULL) + *save_last_bg = last_bg; + + ret = str->str; + g_string_free(str, FALSE); + return ret; +} + +/* expand the data part in {abstract data} */ +char *theme_format_expand_data(THEME_REC *theme, const char **format, theme_rm_col default_fg, + theme_rm_col default_bg, theme_rm_col *save_last_fg, + theme_rm_col *save_last_bg, int flags) +{ + return theme_format_expand_data_rec(theme, format, default_fg, default_bg, save_last_bg, + save_last_bg, flags, NULL); +} + +#define IS_OLD_FORMAT(code, last_fg, last_bg) \ + (((code) == 'n' && (last_fg) == 'n' && (last_bg) == 'n') || \ + ((code) != 'n' && ((code) == (last_fg) || (code) == (last_bg)))) + +static char *theme_format_compress_colors(THEME_REC *theme, const char *format) +{ + GString *str; + char *ret; + char last_fg, last_bg; + + str = g_string_new(NULL); + + last_fg = last_bg = '\0'; + while (*format != '\0') { + if (*format == '$') { + /* $variable, skrip it entirely */ + theme_format_append_variable(str, &format); + last_fg = last_bg = '\0'; + } else if (*format != '%') { + /* a normal character */ + g_string_append_c(str, *format); + format++; + } else if (format[1] != '\0') { + /* %format */ + format++; + if (IS_OLD_FORMAT(*format, last_fg, last_bg)) { + /* active color set again */ + } else if (IS_FGCOLOR_FORMAT(*format) && + format[1] == '%' && + IS_FGCOLOR_FORMAT(format[2]) && + (*format != 'n' || format[2] == 'n')) { + /* two fg colors in a row. bg colors are + so rare that we don't bother checking + them */ + } else { + /* some format, add it */ + g_string_append_c(str, '%'); + g_string_append_c(str, *format); + + if (IS_FGCOLOR_FORMAT(*format)) + last_fg = *format; + else if (*format == 'Z' || *format == 'X') + last_fg = '\0'; + if (IS_BGCOLOR_FORMAT(*format)) + last_bg = *format; + else if (*format == 'z' || *format == 'x') + last_bg = '\0'; + } + format++; + } else { + /* % at end of string */ + format++; + g_string_append_c(str, '%'); + g_string_append_c(str, '%'); + } + } + + ret = str->str; + g_string_free(str, FALSE); + return ret; +} + +char *theme_format_expand(THEME_REC *theme, const char *format) +{ + char *data, *ret; + theme_rm_col reset; + strcpy(reset.m, "n"); + + g_return_val_if_fail(theme != NULL, NULL); + g_return_val_if_fail(format != NULL, NULL); + + data = theme_format_expand_data(theme, &format, reset, reset, NULL, NULL, + EXPAND_FLAG_ROOT); + ret = theme_format_compress_colors(theme, data); + g_free(data); + return ret; +} + +static MODULE_THEME_REC *theme_module_create(THEME_REC *theme, const char *module) +{ + MODULE_THEME_REC *rec; + FORMAT_REC *formats; + + rec = g_hash_table_lookup(theme->modules, module); + if (rec != NULL) return rec; + + formats = g_hash_table_lookup(default_formats, module); + g_return_val_if_fail(formats != NULL, NULL); + + rec = g_new0(MODULE_THEME_REC, 1); + rec->name = g_strdup(module); + + for (rec->count = 0; formats[rec->count].def != NULL; rec->count++) ; + rec->formats = g_new0(char *, rec->count); + rec->expanded_formats = g_new0(char *, rec->count); + + g_hash_table_insert(theme->modules, rec->name, rec); + return rec; +} + +static void theme_read_replaces(CONFIG_REC *config, THEME_REC *theme) +{ + GSList *tmp; + CONFIG_NODE *node; + const char *p; + int index; + + /* reset replace keys */ + for (index = 0; index < 256; index++) + theme->replace_keys[index] = -1; + index = 0; + + node = config_node_traverse(config, "replaces", FALSE); + if (node == NULL || node->type != NODE_TYPE_BLOCK) return; + + for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + node = tmp->data; + + if (node->key != NULL && node->value != NULL) { + for (p = node->key; *p != '\0'; p++) + theme->replace_keys[(int) (unsigned char) *p] = index; + + theme->replace_values = + g_slist_append(theme->replace_values, + g_strdup(node->value)); + index++; + } + } +} + +static void theme_read_abstracts(CONFIG_REC *config, THEME_REC *theme) +{ + GSList *tmp; + CONFIG_NODE *node; + gpointer oldkey, oldvalue; + + node = config_node_traverse(config, "abstracts", FALSE); + if (node == NULL || node->type != NODE_TYPE_BLOCK) return; + + for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + node = tmp->data; + + if (node->key == NULL || node->value == NULL) + continue; + + if (g_hash_table_lookup_extended(theme->abstracts, node->key, + &oldkey, &oldvalue)) { + /* new values override old ones */ + g_hash_table_remove(theme->abstracts, oldkey); + g_free(oldkey); + g_free(oldvalue); + } + + g_hash_table_insert(theme->abstracts, g_strdup(node->key), + g_strdup(node->value)); + } +} + +static void theme_set_format(THEME_REC *theme, MODULE_THEME_REC *rec, + const char *module, + const char *key, const char *value) +{ + int num; + + num = format_find_tag(module, key); + if (num != -1) { + rec->formats[num] = g_strdup(value); + rec->expanded_formats[num] = theme_format_expand(theme, value); + } +} + +static void theme_read_formats(THEME_REC *theme, const char *module, + CONFIG_REC *config, MODULE_THEME_REC *rec) +{ + CONFIG_NODE *node; + GSList *tmp; + + node = config_node_traverse(config, "formats", FALSE); + if (node == NULL) return; + node = config_node_section(config, node, module, -1); + if (node == NULL) return; + + for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + node = tmp->data; + + if (node->key != NULL && node->value != NULL) { + theme_set_format(theme, rec, module, + node->key, node->value); + } + } +} + +static void theme_init_module(THEME_REC *theme, const char *module, + CONFIG_REC *config) +{ + MODULE_THEME_REC *rec; + FORMAT_REC *formats; + int n; + + formats = g_hash_table_lookup(default_formats, module); + g_return_if_fail(formats != NULL); + + rec = theme_module_create(theme, module); + + if (config != NULL) + theme_read_formats(theme, module, config, rec); + + /* expand the remaining formats */ + for (n = 0; n < rec->count; n++) { + if (rec->expanded_formats[n] == NULL) { + rec->expanded_formats[n] = + theme_format_expand(theme, formats[n].def); + } + } +} + +static void sig_print_errors(void) +{ + init_finished = TRUE; + + if (init_errors != NULL) { + signal_emit("gui dialog", 2, "error", init_errors); + g_free(init_errors); + } +} + +static void theme_read_module(THEME_REC *theme, const char *module) +{ + CONFIG_REC *config; + + config = config_open(theme->path, -1); + if (config != NULL) + config_parse(config); + + theme_init_module(theme, module, config); + + if (config != NULL) config_close(config); +} + +static void themes_read_module(const char *module) +{ + g_slist_foreach(themes, (GFunc) theme_read_module, (void *) module); +} + +static void theme_remove_module(THEME_REC *theme, const char *module) +{ + MODULE_THEME_REC *rec; + + rec = g_hash_table_lookup(theme->modules, module); + if (rec == NULL) return; + + g_hash_table_remove(theme->modules, module); + theme_module_destroy(module, rec); +} + +static void themes_remove_module(const char *module) +{ + g_slist_foreach(themes, (GFunc) theme_remove_module, (void *) module); +} + +void theme_register_module(const char *module, FORMAT_REC *formats) +{ + if (g_hash_table_lookup(default_formats, module) != NULL) + return; + + g_hash_table_insert(default_formats, g_strdup(module), formats); + themes_read_module(module); +} + +void theme_unregister_module(const char *module) +{ + gpointer key, value; + + if (default_formats == NULL) + return; /* already uninitialized */ + + if (!g_hash_table_lookup_extended(default_formats, module, &key, &value)) + return; + + g_hash_table_remove(default_formats, key); + g_free(key); + + themes_remove_module(module); +} + +void theme_set_default_abstract(const char *key, const char *value) +{ + gpointer oldkey, oldvalue; + + if (g_hash_table_lookup_extended(internal_theme->abstracts, key, + &oldkey, &oldvalue)) { + /* new values override old ones */ + g_hash_table_remove(internal_theme->abstracts, oldkey); + g_free(oldkey); + g_free(oldvalue); + } + + g_hash_table_insert(internal_theme->abstracts, + g_strdup(key), g_strdup(value)); +} + +static THEME_REC *theme_find(const char *name) +{ + GSList *tmp; + + for (tmp = themes; tmp != NULL; tmp = tmp->next) { + THEME_REC *rec = tmp->data; + + if (g_ascii_strcasecmp(rec->name, name) == 0) + return rec; + } + + return NULL; +} + +static void window_themes_update(void) +{ + GSList *tmp; + + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + if (rec->theme_name != NULL) + rec->theme = theme_load(rec->theme_name); + } +} + +THEME_REC *theme_load(const char *setname) +{ + THEME_REC *theme, *oldtheme; + struct stat statbuf; + char *fname, *name, *p; + + name = g_strdup(setname); + p = strrchr(name, '.'); + if (p != NULL && g_strcmp0(p, ".theme") == 0) { + /* remove the trailing .theme */ + *p = '\0'; + } + + theme = theme_find(name); + + /* check home dir */ + fname = g_strdup_printf("%s/%s.theme", get_irssi_dir(), name); + if (stat(fname, &statbuf) != 0) { + /* check global config dir */ + g_free(fname); + fname = g_strdup_printf(THEMESDIR"/%s.theme", name); + if (stat(fname, &statbuf) != 0) { + /* theme not found */ + g_free(fname); + g_free(name); + return theme; /* use the one in memory if possible */ + } + } + + if (theme != NULL && theme->last_modify == statbuf.st_mtime) { + /* theme not modified, use the one already in memory */ + g_free(fname); + g_free(name); + return theme; + } + + oldtheme = theme; + theme = theme_create(fname, name); + theme->last_modify = statbuf.st_mtime; + if (!theme_read(theme, theme->path)) { + /* error reading .theme file */ + theme_destroy(theme); + theme = NULL; + } + + if (oldtheme != NULL && theme != NULL) { + theme_destroy(oldtheme); + if (current_theme == oldtheme) + current_theme = theme; + window_themes_update(); + } + + g_free(fname); + g_free(name); + return theme; +} + +static void copy_abstract_hash(char *key, char *value, GHashTable *dest) +{ + g_hash_table_insert(dest, g_strdup(key), g_strdup(value)); +} + +static void theme_copy_abstracts(THEME_REC *dest, THEME_REC *src) +{ + g_hash_table_foreach(src->abstracts, (GHFunc) copy_abstract_hash, + dest->abstracts); +} + +typedef struct { + THEME_REC *theme; + CONFIG_REC *config; +} THEME_READ_REC; + +static void theme_read_modules(const char *module, void *value, + THEME_READ_REC *rec) +{ + theme_init_module(rec->theme, module, rec->config); +} + +static void read_error(const char *str) +{ + char *old; + + if (init_finished) + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str); + else if (init_errors == NULL) + init_errors = g_strdup(str); + else { + old = init_errors; + init_errors = g_strconcat(init_errors, "\n", str, NULL); + g_free(old); + } +} + +static int theme_read(THEME_REC *theme, const char *path) +{ + CONFIG_REC *config; + THEME_READ_REC rec; + char *str; + + config = config_open(path, -1) ; + if (config == NULL) { + /* didn't exist or no access? */ + str = g_strdup_printf("Error reading theme file %s: %s", + path, g_strerror(errno)); + read_error(str); + g_free(str); + return FALSE; + } + + if (path == NULL) + config_parse_data(config, default_theme, "internal"); + else + config_parse(config); + + if (config_last_error(config) != NULL) { + str = g_strdup_printf("Ignored errors in theme %s:\n%s", + theme->name, config_last_error(config)); + read_error(str); + g_free(str); + } + + theme->default_color = + config_get_int(config, NULL, "default_color", -1); + theme->info_eol = config_get_bool(config, NULL, "info_eol", FALSE); + + theme_read_replaces(config, theme); + + if (path != NULL) + theme_copy_abstracts(theme, internal_theme); + theme_read_abstracts(config, theme); + + rec.theme = theme; + rec.config = config; + g_hash_table_foreach(default_formats, + (GHFunc) theme_read_modules, &rec); + config_close(config); + + return TRUE; +} + +typedef struct { + char *name; + char *short_name; +} THEME_SEARCH_REC; + +static int theme_search_equal(THEME_SEARCH_REC *r1, THEME_SEARCH_REC *r2) +{ + return g_ascii_strcasecmp(r1->short_name, r2->short_name); +} + +static void theme_get_modules(char *module, FORMAT_REC *formats, GSList **list) +{ + THEME_SEARCH_REC *rec; + + rec = g_new(THEME_SEARCH_REC, 1); + rec->name = module; + rec->short_name = strrchr(module, '/'); + if (rec->short_name != NULL) + rec->short_name++; else rec->short_name = module; + *list = g_slist_insert_sorted(*list, rec, (GCompareFunc) theme_search_equal); +} + +static GSList *get_sorted_modules(void) +{ + GSList *list; + + list = NULL; + g_hash_table_foreach(default_formats, (GHFunc) theme_get_modules, &list); + return list; +} + +static THEME_SEARCH_REC *theme_search(GSList *list, const char *module) +{ + THEME_SEARCH_REC *rec; + + while (list != NULL) { + rec = list->data; + + if (g_ascii_strcasecmp(rec->short_name, module) == 0) + return rec; + list = list->next; + } + + return NULL; +} + +static void theme_show(THEME_SEARCH_REC *rec, const char *key, const char *value, int reset) +{ + MODULE_THEME_REC *theme; + FORMAT_REC *formats; + const char *text, *last_title; + int n, first; + + formats = g_hash_table_lookup(default_formats, rec->name); + theme = g_hash_table_lookup(current_theme->modules, rec->name); + + last_title = NULL; first = TRUE; + for (n = 1; formats[n].def != NULL; n++) { + text = theme != NULL && theme->formats[n] != NULL ? + theme->formats[n] : formats[n].def; + + if (formats[n].tag == NULL) + last_title = text; + else if ((value != NULL && key != NULL && g_ascii_strcasecmp(formats[n].tag, key) == 0) || + (value == NULL && (key == NULL || stristr(formats[n].tag, key) != NULL))) { + if (first) { + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_FORMAT_TITLE, rec->short_name, formats[0].def); + first = FALSE; + } + if (last_title != NULL) + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_FORMAT_SUBTITLE, last_title); + if (reset || value != NULL) { + theme = theme_module_create(current_theme, rec->name); + g_free_not_null(theme->formats[n]); + g_free_not_null(theme->expanded_formats[n]); + + text = reset ? formats[n].def : value; + theme->formats[n] = reset ? NULL : g_strdup(value); + theme->expanded_formats[n] = theme_format_expand(current_theme, text); + } + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_FORMAT_ITEM, formats[n].tag, text); + last_title = NULL; + } + } +} + +/* SYNTAX: FORMAT [-delete | -reset] [<module>] [<key> [<value>]] */ +static void cmd_format(const char *data) +{ + GHashTable *optlist; + GSList *tmp, *modules; + char *module, *key, *value; + void *free_arg; + int reset; + + if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST | PARAM_FLAG_OPTIONS, + "format", &optlist, &module, &key, &value)) + return; + + modules = get_sorted_modules(); + if (*module == '\0') + module = NULL; + else if (theme_search(modules, module) == NULL) { + /* first argument isn't module.. */ + cmd_params_free(free_arg); + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | PARAM_FLAG_OPTIONS, + "format", &optlist, &key, &value)) + return; + module = NULL; + } + + reset = FALSE; + if (*key == '\0') key = NULL; + if (g_hash_table_lookup(optlist, "reset")) + reset = TRUE; + else if (g_hash_table_lookup(optlist, "delete")) + value = ""; + else if (*value == '\0') + value = NULL; + + for (tmp = modules; tmp != NULL; tmp = tmp->next) { + THEME_SEARCH_REC *rec = tmp->data; + + if (module == NULL || g_ascii_strcasecmp(rec->short_name, module) == 0) + theme_show(rec, key, value, reset); + } + g_slist_foreach(modules, (GFunc) g_free, NULL); + g_slist_free(modules); + + cmd_params_free(free_arg); +} + +typedef struct { + CONFIG_REC *config; + int save_all; +} THEME_SAVE_REC; + +static void module_save(const char *module, MODULE_THEME_REC *rec, + THEME_SAVE_REC *data) +{ + CONFIG_NODE *fnode, *node; + FORMAT_REC *formats; + int n; + + formats = g_hash_table_lookup(default_formats, rec->name); + if (formats == NULL) return; + + fnode = config_node_traverse(data->config, "formats", TRUE); + + node = config_node_section(data->config, fnode, rec->name, NODE_TYPE_BLOCK); + for (n = 1; formats[n].def != NULL; n++) { + if (rec->formats[n] != NULL) { + config_node_set_str(data->config, node, formats[n].tag, + rec->formats[n]); + } else if (data->save_all && formats[n].tag != NULL) { + config_node_set_str(data->config, node, formats[n].tag, + formats[n].def); + } + } + + if (node->value == NULL) { + /* not modified, don't keep the empty section */ + config_node_remove(data->config, fnode, node); + if (fnode->value == NULL) { + config_node_remove(data->config, + data->config->mainnode, fnode); + } + } +} + +static void theme_save(THEME_REC *theme, int save_all) +{ + CONFIG_REC *config; + THEME_SAVE_REC data; + char *path; + char *basename; + int ok; + + config = config_open(theme->path, -1); + if (config != NULL) + config_parse(config); + else { + if (g_ascii_strcasecmp(theme->name, "default") == 0) { + config = config_open(NULL, -1); + config_parse_data(config, default_theme, "internal"); + config_change_file_name(config, theme->path, 0660); + } else { + config = config_open(theme->path, 0660); + if (config == NULL) + return; + config_parse(config); + } + } + + data.config = config; + data.save_all = save_all; + g_hash_table_foreach(theme->modules, (GHFunc) module_save, &data); + + basename = g_path_get_basename(theme->path); + /* always save the theme to ~/.irssi/ */ + path = g_strdup_printf("%s/%s", get_irssi_dir(), basename); + ok = config_write(config, path, 0660) == 0; + g_free(basename); + + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + ok ? TXT_THEME_SAVED : TXT_THEME_SAVE_FAILED, + path, config_last_error(config)); + + g_free(path); + config_close(config); +} + +/* save changed formats, -format saves all */ +static void cmd_save(const char *data) +{ + GSList *tmp; + GHashTable *optlist; + void *free_arg; + char *fname; + int saveall; + + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS, + "save", &optlist, &fname)) + return; + + saveall = g_hash_table_lookup(optlist, "formats") != NULL; + for (tmp = themes; tmp != NULL; tmp = tmp->next) { + THEME_REC *theme = tmp->data; + + theme_save(theme, saveall); + } + + cmd_params_free(free_arg); +} + +static void complete_format_list(THEME_SEARCH_REC *rec, const char *key, GList **list) +{ + FORMAT_REC *formats; + int n, len; + + formats = g_hash_table_lookup(default_formats, rec->name); + + len = strlen(key); + for (n = 1; formats[n].def != NULL; n++) { + const char *item = formats[n].tag; + + if (item != NULL && g_ascii_strncasecmp(item, key, len) == 0) + *list = g_list_append(*list, g_strdup(item)); + } +} + +static GList *completion_get_formats(const char *module, const char *key) +{ + GSList *modules, *tmp; + GList *list; + + g_return_val_if_fail(key != NULL, NULL); + + list = NULL; + + modules = get_sorted_modules(); + if (*module == '\0' || theme_search(modules, module) != NULL) { + for (tmp = modules; tmp != NULL; tmp = tmp->next) { + THEME_SEARCH_REC *rec = tmp->data; + + if (*module == '\0' || g_ascii_strcasecmp(rec->short_name, module) == 0) + complete_format_list(rec, key, &list); + } + } + g_slist_foreach(modules, (GFunc) g_free, NULL); + g_slist_free(modules); + + return list; +} + +static void sig_complete_format(GList **list, WINDOW_REC *window, + const char *word, const char *line, int *want_space) +{ + const char *ptr; + int words; + + g_return_if_fail(list != NULL); + g_return_if_fail(word != NULL); + g_return_if_fail(line != NULL); + + ptr = line; + + words = 0; + if (*ptr != '\0') { + do { + ptr++; + words++; + ptr = strchr(ptr, ' '); + } while (ptr != NULL); + } + + if (words > 2) + return; + + *list = completion_get_formats(line, word); + if (*list != NULL) signal_stop(); +} + +static void change_theme(const char *name, int verbose) +{ + THEME_REC *rec; + + rec = theme_load(name); + if (rec != NULL) { + current_theme = rec; + signal_emit("theme changed", 1, rec); + + if (verbose) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_THEME_CHANGED, + rec->name, rec->path); + } + } else if (verbose) { + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + TXT_THEME_NOT_FOUND, name); + } +} + +static void read_settings(void) +{ + const char *theme; + int len; + + theme = settings_get_str("theme"); + len = strlen(current_theme->name); + if (g_strcmp0(current_theme->name, theme) != 0 && + (strncmp(current_theme->name, theme, len) != 0 || + g_strcmp0(theme+len, ".theme") != 0)) + change_theme(theme, TRUE); +} + +void themes_reload(void) +{ + GSList *refs; + char *fname; + + /* increase every theme's refcount, and destroy them. this way if + we want to use the theme before it's reloaded we don't crash. */ + refs = NULL; + while (themes != NULL) { + THEME_REC *theme = themes->data; + + refs = g_slist_prepend(refs, theme); + + theme->refcount++; + theme_destroy(theme); + } + + /* first there's default theme.. */ + current_theme = theme_load("default"); + if (current_theme == NULL) { + fname = g_strdup_printf("%s/default.theme", get_irssi_dir()); + current_theme = theme_create(fname, "default"); + current_theme->default_color = -1; + theme_read(current_theme, NULL); + g_free(fname); + } + + window_themes_update(); + change_theme(settings_get_str("theme"), FALSE); + + while (refs != NULL) { + void *tmp = refs->data; + refs = g_slist_remove(refs, refs->data); + theme_unref(tmp); + } +} + +static THEME_REC *read_internal_theme(void) +{ + CONFIG_REC *config; + THEME_REC *theme; + + theme = theme_create("internal", "_internal"); + theme->refcount++; + theme_destroy(theme); + + config = config_open(NULL, -1); + config_parse_data(config, default_theme, "internal"); + theme_read_abstracts(config, theme); + config_close(config); + + return theme; +} + +void themes_init(void) +{ + settings_add_str("lookandfeel", "theme", "default"); + + default_formats = g_hash_table_new((GHashFunc) g_str_hash, + (GCompareFunc) g_str_equal); + internal_theme = read_internal_theme(); + + init_finished = FALSE; + init_errors = NULL; + + themes_reload(); + + command_bind("format", NULL, (SIGNAL_FUNC) cmd_format); + command_bind("save", NULL, (SIGNAL_FUNC) cmd_save); + signal_add("complete command format", (SIGNAL_FUNC) sig_complete_format); + signal_add("irssi init finished", (SIGNAL_FUNC) sig_print_errors); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); + signal_add("setup reread", (SIGNAL_FUNC) themes_reload); + + command_set_options("format", "delete reset"); + command_set_options("save", "formats"); +} + +void themes_deinit(void) +{ + while (themes != NULL) + theme_destroy(themes->data); + theme_destroy(internal_theme); + + g_hash_table_destroy(default_formats); + default_formats = NULL; + + command_unbind("format", (SIGNAL_FUNC) cmd_format); + command_unbind("save", (SIGNAL_FUNC) cmd_save); + signal_remove("complete command format", (SIGNAL_FUNC) sig_complete_format); + signal_remove("irssi init finished", (SIGNAL_FUNC) sig_print_errors); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); + signal_remove("setup reread", (SIGNAL_FUNC) themes_reload); +} diff --git a/src/fe-common/core/themes.h b/src/fe-common/core/themes.h new file mode 100644 index 0000000..53426f3 --- /dev/null +++ b/src/fe-common/core/themes.h @@ -0,0 +1,75 @@ +#ifndef IRSSI_FE_COMMON_CORE_THEMES_H +#define IRSSI_FE_COMMON_CORE_THEMES_H + +typedef struct { + char *name; + + int count; + char **formats; /* in same order as in module's default formats */ + char **expanded_formats; /* this contains the formats after + expanding {templates} */ +} MODULE_THEME_REC; + +typedef struct { + int refcount; + + char *path; + char *name; + time_t last_modify; + + int default_color; /* default color to use with text with default + background. default is -1 which means the + default color set by terminal */ + unsigned int info_eol:1; /* show the timestamp/servertag at the + end of the line, not at beginning */ + + GHashTable *modules; + + int replace_keys[256]; /* index to replace_values for each char */ + GSList *replace_values; + GHashTable *abstracts; + + void *gui_data; +} THEME_REC; + +typedef struct _FORMAT_REC FORMAT_REC; + +extern GSList *themes; +extern THEME_REC *current_theme; +extern GHashTable *default_formats; + +THEME_REC *theme_create(const char *path, const char *name); +void theme_destroy(THEME_REC *rec); + +THEME_REC *theme_load(const char *name); + +#define theme_register(formats) theme_register_module(MODULE_NAME, formats) +#define theme_unregister() theme_unregister_module(MODULE_NAME) +void theme_register_module(const char *module, FORMAT_REC *formats); +void theme_unregister_module(const char *module); + +void theme_set_default_abstract(const char *key, const char *value); + +#define EXPAND_FLAG_IGNORE_REPLACES 0x01 /* don't use the character replaces when expanding */ +#define EXPAND_FLAG_IGNORE_EMPTY 0x02 /* if abstract's argument is empty, or the argument is a $variable that is empty, don't try to expand it (ie. {xx }, but not {xx}) */ +#define EXPAND_FLAG_RECURSIVE_MASK 0x0f +/* private */ +#define EXPAND_FLAG_ROOT 0x10 +#define EXPAND_FLAG_LASTCOLOR_ARG 0x20 + +typedef struct { + char m[8]; +} theme_rm_col; + +char *theme_format_expand(THEME_REC *theme, const char *format); +char *theme_format_expand_data(THEME_REC *theme, const char **format, + theme_rm_col default_fg, theme_rm_col default_bg, + theme_rm_col *save_last_fg, theme_rm_col *save_last_bg, + int flags); + +void themes_reload(void); + +void themes_init(void); +void themes_deinit(void); + +#endif diff --git a/src/fe-common/core/window-activity.c b/src/fe-common/core/window-activity.c new file mode 100644 index 0000000..7a2903f --- /dev/null +++ b/src/fe-common/core/window-activity.c @@ -0,0 +1,166 @@ +/* + window-activity.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/signals.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/servers.h> +#include <irssi/src/core/channels.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/core/window-items.h> +#include <irssi/src/core/nicklist.h> +#include <irssi/src/fe-common/core/hilight-text.h> +#include <irssi/src/fe-common/core/formats.h> +#include <irssi/src/fe-common/core/fe-common-core.h> + +static char **hide_targets; +static int hide_level, msg_level, hilight_level, signal_window_hilight_check; + +void window_activity(WINDOW_REC *window, int data_level, + const char *hilight_color) +{ + int old_data_level; + + old_data_level = window->data_level; + if (data_level == 0 || window->data_level < data_level) { + window->data_level = data_level; + g_free_not_null(window->hilight_color); + window->hilight_color = g_strdup(hilight_color); + signal_emit("window hilight", 1, window); + } + + signal_emit("window activity", 2, window, + GINT_TO_POINTER(old_data_level)); +} + +void window_item_activity(WI_ITEM_REC *item, int data_level, + const char *hilight_color) +{ + int old_data_level; + + old_data_level = item->data_level; + if (data_level == 0 || item->data_level < data_level) { + item->data_level = data_level; + g_free_not_null(item->hilight_color); + item->hilight_color = g_strdup(hilight_color); + signal_emit("window item hilight", 1, item); + } + + signal_emit("window item activity", 2, item, + GINT_TO_POINTER(old_data_level)); +} + +static void sig_hilight_text(TEXT_DEST_REC *dest, const char *msg) +{ + WI_ITEM_REC *item; + int data_level; + int cb_ignore = 0; + + if (dest->window == active_win || (dest->level & hide_level)) + return; + + if (dest->level & hilight_level) { + data_level = DATA_LEVEL_HILIGHT+dest->hilight_priority; + } else { + data_level = (dest->level & msg_level) ? + DATA_LEVEL_MSG : DATA_LEVEL_TEXT; + } + + if (hide_targets != NULL && (dest->level & MSGLEVEL_HILIGHT) == 0) { + /* check for both target and tag/target */ + if (strarray_find_dest(hide_targets, dest)) + return; + } + + /* we should ask the text view if this line is hidden */ + signal_emit_id(signal_window_hilight_check, 4, dest, msg, &data_level, &cb_ignore); + if (cb_ignore) { + return; + } + + if (dest->target != NULL) { + item = window_item_find(dest->server, dest->target); + if (item != NULL) { + window_item_activity(item, data_level, + dest->hilight_color); + } + } + window_activity(dest->window, data_level, dest->hilight_color); +} + +static void sig_dehilight_window(WINDOW_REC *window) +{ + GSList *tmp; + + g_return_if_fail(window != NULL); + + if (window->data_level != 0) { + window_activity(window, 0, NULL); + for (tmp = window->items; tmp != NULL; tmp = tmp->next) + window_item_activity(tmp->data, 0, NULL); + } +} + +static void read_settings(void) +{ + const char *targets; + + if (hide_targets != NULL) + g_strfreev(hide_targets); + + targets = settings_get_str("activity_hide_targets"); + hide_targets = *targets == '\0' ? NULL : + g_strsplit(targets, " ", -1); + + hide_level = MSGLEVEL_NEVER | MSGLEVEL_NO_ACT | + settings_get_level("activity_hide_level"); + msg_level = settings_get_level("activity_msg_level"); + hilight_level = MSGLEVEL_HILIGHT | + settings_get_level("activity_hilight_level"); +} + +void window_activity_init(void) +{ + settings_add_str("lookandfeel", "activity_hide_targets", ""); + settings_add_level("lookandfeel", "activity_hide_level", ""); + settings_add_level("lookandfeel", "activity_msg_level", "PUBLIC"); + settings_add_level("lookandfeel", "activity_hilight_level", "MSGS DCCMSGS"); + signal_window_hilight_check = signal_get_uniq_id("window hilight check"); + + read_settings(); + signal_add("print text", (SIGNAL_FUNC) sig_hilight_text); + signal_add("window changed", (SIGNAL_FUNC) sig_dehilight_window); + signal_add("window dehilight", (SIGNAL_FUNC) sig_dehilight_window); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); +} + +void window_activity_deinit(void) +{ + if (hide_targets != NULL) + g_strfreev(hide_targets); + + signal_remove("print text", (SIGNAL_FUNC) sig_hilight_text); + signal_remove("window changed", (SIGNAL_FUNC) sig_dehilight_window); + signal_remove("window dehilight", (SIGNAL_FUNC) sig_dehilight_window); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); +} diff --git a/src/fe-common/core/window-activity.h b/src/fe-common/core/window-activity.h new file mode 100644 index 0000000..42a4cda --- /dev/null +++ b/src/fe-common/core/window-activity.h @@ -0,0 +1,13 @@ +#ifndef IRSSI_FE_COMMON_CORE_WINDOW_ACTIVITY_H +#define IRSSI_FE_COMMON_CORE_WINDOW_ACTIVITY_H + +void window_activity(WINDOW_REC *window, int data_level, + const char *hilight_color); + +void window_item_activity(WI_ITEM_REC *item, int data_level, + const char *hilight_color); + +void window_activity_init(void); +void window_activity_deinit(void); + +#endif diff --git a/src/fe-common/core/window-commands.c b/src/fe-common/core/window-commands.c new file mode 100644 index 0000000..6669ba4 --- /dev/null +++ b/src/fe-common/core/window-commands.c @@ -0,0 +1,962 @@ +/* + window-commands.c : irssi + + Copyright (C) 2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/servers.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/core/levels.h> + +#include <irssi/src/fe-common/core/themes.h> +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/core/window-items.h> +#include <irssi/src/fe-common/core/windows-layout.h> +#include <irssi/src/fe-common/core/printtext.h> +#include <irssi/src/fe-common/core/command-history.h> + +static void window_print_binds(WINDOW_REC *win) +{ + GSList *tmp; + + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_BOUND_ITEMS_HEADER); + for (tmp = win->bound_items; tmp != NULL; tmp = tmp->next) { + WINDOW_BIND_REC *bind = tmp->data; + + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_BOUND_ITEM, + bind->name, bind->servertag, + bind->sticky ? "sticky" : ""); + } + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_BOUND_ITEMS_FOOTER); +} + +static void window_print_items(WINDOW_REC *win) +{ + GSList *tmp; + const char *type; + + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_ITEMS_HEADER); + for (tmp = win->items; tmp != NULL; tmp = tmp->next) { + WI_ITEM_REC *item = tmp->data; + + type = module_find_id_str("WINDOW ITEM TYPE", item->type); + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_ITEM, + type == NULL ? "??" : type, + item->visible_name, + item->server == NULL ? "" : + item->server->tag); + } + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_ITEMS_FOOTER); +} + +static void cmd_window_info(WINDOW_REC *win) +{ + char *levelstr; + + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_HEADER); + + /* Window reference number + sticky status */ + if (!win->sticky_refnum) { + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_REFNUM, win->refnum); + } else { + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_REFNUM_STICKY, win->refnum); + } + + /* Window name */ + if (win->name != NULL) { + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_NAME, win->name); + } + + /* Window width / height */ + printformat_window(win, MSGLEVEL_CLIENTCRAP, TXT_WINDOW_INFO_SIZE, + win->width, win->height); + + /* Window immortality */ + if (win->immortal) { + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_IMMORTAL); + } + + /* Window history name */ + if (win->history_name != NULL) { + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_HISTORY, win->history_name); + } + + /* Window level */ + levelstr = win->level == 0 ? + g_strdup("NONE") : bits2level(win->level); + printformat_window(win, MSGLEVEL_CLIENTCRAP, TXT_WINDOW_INFO_LEVEL, + levelstr); + g_free(levelstr); + + /* Active window server + sticky status */ + if (win->servertag == NULL) { + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_SERVER, + win->active_server != NULL ? + win->active_server->tag : "NONE"); + } else { + if (win->active_server != NULL && + g_strcmp0(win->active_server->tag, win->servertag) != 0) + g_warning("Active server isn't the sticky server!"); + + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_SERVER_STICKY, + win->servertag); + } + + /* Window theme + error status */ + if (win->theme_name != NULL) { + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_THEME, win->theme_name, + win->theme != NULL ? "" : "(not loaded)"); + } + + /* Bound items in window */ + if (win->bound_items != NULL) + window_print_binds(win); + + /* Item */ + if (win->items != NULL) + window_print_items(win); + + signal_emit("window print info", 1, win); + + printformat_window(win, MSGLEVEL_CLIENTCRAP, + TXT_WINDOW_INFO_FOOTER); +} + +static void cmd_window(const char *data, void *server, WI_ITEM_REC *item) +{ + while (*data == ' ') data++; + + if (*data == '\0') + cmd_window_info(active_win); + else if (is_numeric(data, 0)) + signal_emit("command window refnum", 3, data, server, item); + else + command_runsub("window", data, server, item); +} + +/* SYNTAX: WINDOW NEW [HIDDEN|SPLIT|-right SPLIT] */ +static void cmd_window_new(const char *data, void *server, WI_ITEM_REC *item) +{ + WINDOW_REC *window; + int type; + + g_return_if_fail(data != NULL); + + type = (g_ascii_strncasecmp(data, "hid", 3) == 0 || g_ascii_strcasecmp(data, "tab") == 0) ? MAIN_WINDOW_TYPE_HIDDEN : + g_ascii_strcasecmp(data, "split") == 0 ? MAIN_WINDOW_TYPE_SPLIT : + g_ascii_strncasecmp(data, "-r", 2) == 0 ? MAIN_WINDOW_TYPE_RSPLIT : MAIN_WINDOW_TYPE_DEFAULT; + signal_emit("gui window create override", 1, GINT_TO_POINTER(type)); + + window = window_create(NULL, FALSE); + window_change_server(window, server); +} + +/* SYNTAX: WINDOW CLOSE [<first> [<last>]] */ +static void cmd_window_close(const char *data) +{ + GSList *tmp, *destroys; + char *first, *last; + int first_num, last_num; + void *free_arg; + + if (!cmd_get_params(data, &free_arg, 2, &first, &last)) + return; + + if ((*first != '\0' && !is_numeric(first, '\0')) || + ((*last != '\0') && !is_numeric(last, '\0'))) { + cmd_params_free(free_arg); + return; + } + + first_num = *first == '\0' ? active_win->refnum : atoi(first); + last_num = *last == '\0' ? first_num : atoi(last); + + /* get list of windows to destroy */ + destroys = NULL; + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + if (rec->refnum >= first_num && rec->refnum <= last_num) + destroys = g_slist_append(destroys, rec); + } + + /* really destroy the windows */ + while (destroys != NULL) { + WINDOW_REC *rec = destroys->data; + + if (windows->next != NULL) { + if (!rec->immortal) + window_destroy(rec); + else { + printformat_window(rec, MSGLEVEL_CLIENTERROR, + TXT_WINDOW_IMMORTAL_ERROR); + } + } + + destroys = g_slist_remove(destroys, rec); + } + + cmd_params_free(free_arg); +} + +/* SYNTAX: WINDOW REFNUM <number> */ +static void cmd_window_refnum(const char *data) +{ + WINDOW_REC *window; + + if (!is_numeric(data, 0)) + return; + + window = window_find_refnum(atoi(data)); + if (window != NULL) + window_set_active(window); +} + +/** + * return the window with the highest activity + * + * If ignore_refnum is true, the most recently active window with the highest + * activity will be returned. If ignore_refnum is false, the refnum will be used + * to break ties between windows with equally high activity. + */ +static WINDOW_REC *window_highest_activity(WINDOW_REC *window, + int ignore_refnum) +{ + WINDOW_REC *rec, *max_win; + GSList *tmp; + int max_act, max_ref, through; + + g_return_val_if_fail(window != NULL, NULL); + + max_win = NULL; max_act = 0; max_ref = 0; through = FALSE; + + tmp = g_slist_find(windows, window); + for (;; tmp = tmp->next) { + if (tmp == NULL) { + tmp = windows; + through = TRUE; + } + + if (through && tmp->data == window) + break; + + rec = tmp->data; + + /* ignore refnum */ + if (ignore_refnum && + rec->data_level > 0 && max_act < rec->data_level) { + max_act = rec->data_level; + max_win = rec; + } + + /* windows with lower refnums break ties */ + else if (!ignore_refnum && + rec->data_level > 0 && + (rec->data_level > max_act || + (rec->data_level == max_act && rec->refnum < max_ref))) { + max_act = rec->data_level; + max_win = rec; + max_ref = rec->refnum; + } + } + + return max_win; +} + +static inline int is_nearer(int r1, int r2) +{ + int a = r2 < active_win->refnum; + int b = r1 < r2; + + if (r1 > active_win->refnum) + return a || b; + else + return a && b; +} + +static WINDOW_REC *window_find_item_cycle(SERVER_REC *server, const char *name) +{ + WINDOW_REC *rec, *win; + GSList *tmp; + + win = NULL; + + tmp = g_slist_find(windows, active_win); + tmp = tmp->next; + for (;; tmp = tmp->next) { + if (tmp == NULL) + tmp = windows; + + if (tmp->data == active_win) + break; + + rec = tmp->data; + + if (window_item_find_window(rec, server, name) != NULL && + (win == NULL || is_nearer(rec->refnum, win->refnum))) { + win = rec; + if (server != NULL) break; + } + } + + return win; +} + +/* SYNTAX: WINDOW GOTO active|<number>|<name> */ +static void cmd_window_goto(const char *data) +{ + WINDOW_REC *window; + char *target; + void *free_arg; + + g_return_if_fail(data != NULL); + + if (is_numeric(data, 0)) { + cmd_window_refnum(data); + return; + } + + if (!cmd_get_params(data, &free_arg, 1, &target)) + return; + + if (g_ascii_strcasecmp(target, "active") == 0) + window = window_highest_activity(active_win, + settings_get_bool("active_window_ignore_refnum")); + else { + window = window_find_name(target); + if (window == NULL && active_win->active_server != NULL) + window = window_find_item_cycle(active_win->active_server, target); + if (window == NULL) + window = window_find_item_cycle(NULL, target); + } + + if (window != NULL) + window_set_active(window); + + cmd_params_free(free_arg); +} + +/* SYNTAX: WINDOW NEXT */ +static void cmd_window_next(void) +{ + int num; + + num = window_refnum_next(active_win->refnum, TRUE); + if (num < 1) num = windows_refnum_last(); + + window_set_active(window_find_refnum(num)); +} + +/* SYNTAX: WINDOW LAST */ +static void cmd_window_last(void) +{ + if (windows->next != NULL) + window_set_active(windows->next->data); +} + +/* SYNTAX: WINDOW PREVIOUS */ +static void cmd_window_previous(void) +{ + int num; + + num = window_refnum_prev(active_win->refnum, TRUE); + if (num < 1) num = window_refnum_next(0, TRUE); + + window_set_active(window_find_refnum(num)); +} + +/* SYNTAX: WINDOW LEVEL [<levels>] */ +static void cmd_window_level(const char *data) +{ + char *level; + + g_return_if_fail(data != NULL); + + window_set_level(active_win, combine_level(active_win->level, data)); + + level = active_win->level == 0 ? g_strdup("NONE") : + bits2level(active_win->level); + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_WINDOW_LEVEL, level); + g_free(level); +} + +/* SYNTAX: WINDOW IMMORTAL on|off|toggle */ +static void cmd_window_immortal(const char *data) +{ + int set; + + if (*data == '\0') + set = active_win->immortal; + else if (g_ascii_strcasecmp(data, "ON") == 0) + set = TRUE; + else if (g_ascii_strcasecmp(data, "OFF") == 0) + set = FALSE; + else if (g_ascii_strcasecmp(data, "TOGGLE") == 0) + set = !active_win->immortal; + else { + printformat_window(active_win, MSGLEVEL_CLIENTERROR, + TXT_NOT_TOGGLE); + return; + } + + if (set) { + window_set_immortal(active_win, TRUE); + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_WINDOW_SET_IMMORTAL); + } else { + window_set_immortal(active_win, FALSE); + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_WINDOW_UNSET_IMMORTAL); + } +} + +/* SYNTAX: WINDOW SERVER [-sticky | -unsticky] <tag> */ +static void cmd_window_server(const char *data) +{ + GHashTable *optlist; + SERVER_REC *server; + char *tag; + void *free_arg; + + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS, + "window server", &optlist, &tag)) + return; + + if (*tag == '\0' && active_win->active_server != NULL && + (g_hash_table_lookup(optlist, "sticky") != NULL || + g_hash_table_lookup(optlist, "unsticky") != NULL)) { + tag = active_win->active_server->tag; + } + + if (*tag == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + server = server_find_tag(tag); + if (server == NULL) + server = server_find_lookup_tag(tag); + + if (g_hash_table_lookup(optlist, "unsticky") != NULL && + active_win->servertag != NULL) { + g_free_and_null(active_win->servertag); + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_UNSET_SERVER_STICKY); + } + + if (active_win->servertag != NULL && + g_hash_table_lookup(optlist, "sticky") == NULL) { + printformat_window(active_win, MSGLEVEL_CLIENTERROR, + TXT_ERROR_SERVER_STICKY); + } else if (server == NULL) { + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_UNKNOWN_SERVER_TAG, tag); + } else if (active_win->active == NULL) { + window_change_server(active_win, server); + if (g_hash_table_lookup(optlist, "sticky") != NULL) { + g_free_not_null(active_win->servertag); + active_win->servertag = g_strdup(server->tag); + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_SET_SERVER_STICKY, server->tag); + } + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_SERVER_CHANGED, + server->tag, server->connrec->address, + server->connrec->chatnet == NULL ? "" : + server->connrec->chatnet); + } + + cmd_params_free(free_arg); +} + +static void cmd_window_item(const char *data, void *server, WI_ITEM_REC *item) +{ + while (*data == ' ') data++; + + if (is_numeric(data, '\0')) + signal_emit("command window item goto", 3, data, server, item); + else + command_runsub("window item", data, server, item); +} + +/* SYNTAX: WINDOW ITEM PREV */ +static void cmd_window_item_prev(void) +{ + window_item_prev(active_win); +} + +/* SYNTAX: WINDOW ITEM NEXT */ +static void cmd_window_item_next(void) +{ + window_item_next(active_win); +} + +/* SYNTAX: WINDOW ITEM GOTO <number>|<name> */ +static void cmd_window_item_goto(const char *data, SERVER_REC *server) +{ + WI_ITEM_REC *item; + GSList *tmp; + void *free_arg; + char *target; + + if (!cmd_get_params(data, &free_arg, 1, &target)) + return; + + if (is_numeric(target, '\0')) { + /* change to specified number */ + tmp = g_slist_nth(active_win->items, atoi(target)-1); + item = tmp == NULL ? NULL : tmp->data; + } else { + item = window_item_find_window(active_win, server, target); + } + + if (item != NULL) + window_item_set_active(active_win, item); + + cmd_params_free(free_arg); +} + +/* SYNTAX: WINDOW ITEM MOVE <number>|<name> */ +static void cmd_window_item_move(const char *data, SERVER_REC *server, + WI_ITEM_REC *item) +{ + WINDOW_REC *window; + void *free_arg; + char *target; + + if (!cmd_get_params(data, &free_arg, 1, &target)) + return; + + if (is_numeric(target, '\0')) { + /* move current window item to specified window */ + window = window_find_refnum(atoi(target)); + } else { + /* move specified window item to current window */ + item = window_item_find(server, target); + window = active_win; + } + if (window != NULL && item != NULL) + window_item_set_active(window, item); + + cmd_params_free(free_arg); +} + +/* SYNTAX: WINDOW NUMBER [-sticky] <number> */ +static void cmd_window_number(const char *data) +{ + GHashTable *optlist; + char *refnum; + void *free_arg; + int num; + + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS, + "window number", &optlist, &refnum)) + return; + + if (*refnum == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + num = atoi(refnum); + if (num < 1) { + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_REFNUM_TOO_LOW); + } else { + window_set_refnum(active_win, num); + active_win->sticky_refnum = + g_hash_table_lookup(optlist, "sticky") != NULL; + } + + cmd_params_free(free_arg); +} + +/* SYNTAX: WINDOW NAME <name> */ +static void cmd_window_name(const char *data) +{ + WINDOW_REC *win; + + win = window_find_name(data); + if (win == NULL || win == active_win) + window_set_name(active_win, data); + else if (active_win->name == NULL || + g_strcmp0(active_win->name, data) != 0) { + printformat_window(active_win, MSGLEVEL_CLIENTERROR, + TXT_WINDOW_NAME_NOT_UNIQUE, data); + } +} + +/* SYNTAX: WINDOW HISTORY [-clear] <name> */ +void cmd_window_history(const char *data) +{ + GHashTable *optlist; + char *name; + void *free_arg; + + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS | PARAM_FLAG_STRIP_TRAILING_WS, + "window history", &optlist, &name)) + return; + + if (g_hash_table_lookup(optlist, "clear") != NULL) { + signal_continue(1, data); + window_clear_history(active_win, name); + } else { + window_set_history(active_win, name); + } + + cmd_params_free(free_arg); +} + +/* we're moving the first window to last - move the first contiguous block + of refnums to left. Like if there's windows 1..5 and 7..10, move 1 to + 11, 2..5 to 1..4 and leave 7..10 alone */ +static void window_refnums_move_left(WINDOW_REC *move_window) +{ + WINDOW_REC *window; + int refnum, new_refnum; + + new_refnum = windows_refnum_last(); + for (refnum = move_window->refnum+1; refnum <= new_refnum; refnum++) { + window = window_find_refnum(refnum); + if (window == NULL) { + new_refnum++; + break; + } + + window_set_refnum(window, refnum-1); + } + + window_set_refnum(move_window, new_refnum); +} + +/* we're moving the last window to first - make some space so we can use the + refnum 1 */ +static void window_refnums_move_right(WINDOW_REC *move_window) +{ + WINDOW_REC *window; + int refnum, new_refnum; + + new_refnum = 1; + if (window_find_refnum(new_refnum) == NULL) { + window_set_refnum(move_window, new_refnum); + return; + } + + /* find the first unused refnum, like if there's windows + 1..5 and 7..10, we only need to move 1..5 to 2..6 */ + refnum = new_refnum; + while (move_window->refnum == refnum || + window_find_refnum(refnum) != NULL) refnum++; + refnum--; + + while (refnum >= new_refnum) { + window = window_find_refnum(refnum); + window_set_refnum(window, refnum+1); + + refnum--; + } + + window_set_refnum(move_window, new_refnum); +} + +/* SYNTAX: WINDOW MOVE PREV */ +static void cmd_window_move_prev(void) +{ + int refnum; + + refnum = window_refnum_prev(active_win->refnum, FALSE); + if (refnum != -1) { + window_set_refnum(active_win, refnum); + return; + } + + window_refnums_move_left(active_win); +} + +/* SYNTAX: WINDOW MOVE NEXT */ +static void cmd_window_move_next(void) +{ + int refnum; + + refnum = window_refnum_next(active_win->refnum, FALSE); + if (refnum != -1) { + window_set_refnum(active_win, refnum); + return; + } + + window_refnums_move_right(active_win); +} + +static void active_window_move_to(int new_refnum) +{ + int refnum; + + if (new_refnum > active_win->refnum) { + for (;;) { + refnum = window_refnum_next(active_win->refnum, FALSE); + if (refnum == -1 || refnum > new_refnum) + break; + + window_set_refnum(active_win, refnum); + } + } else { + for (;;) { + refnum = window_refnum_prev(active_win->refnum, FALSE); + if (refnum == -1 || refnum < new_refnum) + break; + + window_set_refnum(active_win, refnum); + } + } +} + +/* SYNTAX: WINDOW MOVE FIRST */ +static void cmd_window_move_first(void) +{ + active_window_move_to(1); +} + +/* SYNTAX: WINDOW MOVE LAST */ +static void cmd_window_move_last(void) +{ + active_window_move_to(windows_refnum_last()); +} + +/* SYNTAX: WINDOW MOVE <number>|<direction> */ +static void cmd_window_move(const char *data, SERVER_REC *server, WI_ITEM_REC *item) +{ + if (!is_numeric(data, 0)) { + command_runsub("window move", data, server, item); + return; + } + + active_window_move_to(atoi(data)); +} + +/* SYNTAX: WINDOW LIST */ +static void cmd_window_list(void) +{ + GSList *tmp, *sorted; + char *levelstr; + + sorted = windows_get_sorted(); + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_WINDOWLIST_HEADER); + for (tmp = sorted; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + levelstr = bits2level(rec->level); + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_WINDOWLIST_LINE, + rec->refnum, rec->name == NULL ? "" : rec->name, + rec->active == NULL ? "" : rec->active->visible_name, + rec->active_server == NULL ? "" : ((SERVER_REC *) rec->active_server)->tag, + levelstr); + g_free(levelstr); + } + g_slist_free(sorted); + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_WINDOWLIST_FOOTER); +} + +/* SYNTAX: WINDOW THEME [-delete] [<name>] */ +static void cmd_window_theme(const char *data) +{ + THEME_REC *theme; + GHashTable *optlist; + char *name; + void *free_arg; + + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS, + "window theme", &optlist, &name)) + return; + + if (g_hash_table_lookup(optlist, "delete") != NULL) { + g_free_and_null(active_win->theme_name); + + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_WINDOW_THEME_REMOVED); + } else if (*name == '\0') { + if (active_win->theme == NULL) { + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_WINDOW_THEME_DEFAULT); + } else { + theme = active_win->theme; + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_WINDOW_THEME, + theme->name, theme->path); + } + } else { + g_free_not_null(active_win->theme_name); + active_win->theme_name = g_strdup(data); + + active_win->theme = theme = theme_load(data); + if (theme != NULL) { + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_WINDOW_THEME_CHANGED, + theme->name, theme->path); + } else { + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_THEME_NOT_FOUND, data); + } + } + + cmd_params_free(free_arg); +} + +static void cmd_layout(const char *data, SERVER_REC *server, WI_ITEM_REC *item) +{ + command_runsub("layout", data, server, item); +} + +/* SYNTAX: FOREACH WINDOW <command> */ +static void cmd_foreach_window(const char *data) +{ + WINDOW_REC *old; + GSList *list; + const char *cmdchars; + char *str; + + cmdchars = settings_get_str("cmdchars"); + str = strchr(cmdchars, *data) != NULL ? g_strdup(data) : + g_strdup_printf("%c%s", *cmdchars, data); + + old = active_win; + + list = g_slist_copy(windows); + while (list != NULL) { + WINDOW_REC *rec = list->data; + + active_win = rec; + signal_emit("send command", 3, str, rec->active_server, + rec->active); + list = g_slist_remove(list, list->data); + } + + if (g_slist_find(windows, old) != NULL) + active_win = old; + + g_free(str); +} + +static void cmd_window_default_command(const char *data, SERVER_REC *server, WI_ITEM_REC *item) +{ + if (!is_numeric(data, 0) || + !settings_get_bool("window_number_commands")) { + return; + } + signal_emit("command window refnum", 3, data, server, item); + signal_stop(); +} + +void window_commands_init(void) +{ + settings_add_bool("lookandfeel", "active_window_ignore_refnum", TRUE); + settings_add_bool("misc", "window_number_commands", TRUE); + + signal_add("default command", (SIGNAL_FUNC) cmd_window_default_command); + + command_bind("window", NULL, (SIGNAL_FUNC) cmd_window); + command_bind("window new", NULL, (SIGNAL_FUNC) cmd_window_new); + command_bind("window close", NULL, (SIGNAL_FUNC) cmd_window_close); + command_bind("window kill", NULL, (SIGNAL_FUNC) cmd_window_close); + command_bind("window server", NULL, (SIGNAL_FUNC) cmd_window_server); + command_bind("window refnum", NULL, (SIGNAL_FUNC) cmd_window_refnum); + command_bind("window goto", NULL, (SIGNAL_FUNC) cmd_window_goto); + command_bind("window previous", NULL, (SIGNAL_FUNC) cmd_window_previous); + command_bind("window next", NULL, (SIGNAL_FUNC) cmd_window_next); + command_bind("window last", NULL, (SIGNAL_FUNC) cmd_window_last); + command_bind("window level", NULL, (SIGNAL_FUNC) cmd_window_level); + command_bind("window immortal", NULL, (SIGNAL_FUNC) cmd_window_immortal); + command_bind("window item", NULL, (SIGNAL_FUNC) cmd_window_item); + command_bind("window item prev", NULL, (SIGNAL_FUNC) cmd_window_item_prev); + command_bind("window item next", NULL, (SIGNAL_FUNC) cmd_window_item_next); + command_bind("window item goto", NULL, (SIGNAL_FUNC) cmd_window_item_goto); + command_bind("window item move", NULL, (SIGNAL_FUNC) cmd_window_item_move); + command_bind("window number", NULL, (SIGNAL_FUNC) cmd_window_number); + command_bind("window name", NULL, (SIGNAL_FUNC) cmd_window_name); + command_bind("window history", NULL, (SIGNAL_FUNC) cmd_window_history); + command_bind("window move", NULL, (SIGNAL_FUNC) cmd_window_move); + command_bind("window move prev", NULL, (SIGNAL_FUNC) cmd_window_move_prev); + command_bind("window move next", NULL, (SIGNAL_FUNC) cmd_window_move_next); + command_bind("window move first", NULL, (SIGNAL_FUNC) cmd_window_move_first); + command_bind("window move last", NULL, (SIGNAL_FUNC) cmd_window_move_last); + command_bind("window list", NULL, (SIGNAL_FUNC) cmd_window_list); + command_bind("window theme", NULL, (SIGNAL_FUNC) cmd_window_theme); + command_bind("layout", NULL, (SIGNAL_FUNC) cmd_layout); + /* SYNTAX: LAYOUT SAVE */ + command_bind("layout save", NULL, (SIGNAL_FUNC) windows_layout_save); + /* SYNTAX: LAYOUT RESET */ + command_bind("layout reset", NULL, (SIGNAL_FUNC) windows_layout_reset); + command_bind("foreach window", NULL, (SIGNAL_FUNC) cmd_foreach_window); + + command_set_options("window number", "sticky"); + command_set_options("window server", "sticky unsticky"); + command_set_options("window theme", "delete"); + command_set_options("window history", "clear"); +} + +void window_commands_deinit(void) +{ + command_unbind("window", (SIGNAL_FUNC) cmd_window); + command_unbind("window new", (SIGNAL_FUNC) cmd_window_new); + command_unbind("window close", (SIGNAL_FUNC) cmd_window_close); + command_unbind("window kill", (SIGNAL_FUNC) cmd_window_close); + command_unbind("window server", (SIGNAL_FUNC) cmd_window_server); + command_unbind("window refnum", (SIGNAL_FUNC) cmd_window_refnum); + command_unbind("window goto", (SIGNAL_FUNC) cmd_window_goto); + command_unbind("window previous", (SIGNAL_FUNC) cmd_window_previous); + command_unbind("window next", (SIGNAL_FUNC) cmd_window_next); + command_unbind("window last", (SIGNAL_FUNC) cmd_window_last); + command_unbind("window level", (SIGNAL_FUNC) cmd_window_level); + command_unbind("window immortal", (SIGNAL_FUNC) cmd_window_immortal); + command_unbind("window item", (SIGNAL_FUNC) cmd_window_item); + command_unbind("window item prev", (SIGNAL_FUNC) cmd_window_item_prev); + command_unbind("window item next", (SIGNAL_FUNC) cmd_window_item_next); + command_unbind("window item goto", (SIGNAL_FUNC) cmd_window_item_goto); + command_unbind("window item move", (SIGNAL_FUNC) cmd_window_item_move); + command_unbind("window number", (SIGNAL_FUNC) cmd_window_number); + command_unbind("window name", (SIGNAL_FUNC) cmd_window_name); + command_unbind("window history", (SIGNAL_FUNC) cmd_window_history); + command_unbind("window move", (SIGNAL_FUNC) cmd_window_move); + command_unbind("window move prev", (SIGNAL_FUNC) cmd_window_move_prev); + command_unbind("window move next", (SIGNAL_FUNC) cmd_window_move_next); + command_unbind("window move first", (SIGNAL_FUNC) cmd_window_move_first); + command_unbind("window move last", (SIGNAL_FUNC) cmd_window_move_last); + command_unbind("window list", (SIGNAL_FUNC) cmd_window_list); + command_unbind("window theme", (SIGNAL_FUNC) cmd_window_theme); + command_unbind("layout", (SIGNAL_FUNC) cmd_layout); + command_unbind("layout save", (SIGNAL_FUNC) windows_layout_save); + command_unbind("layout reset", (SIGNAL_FUNC) windows_layout_reset); + command_unbind("foreach window", (SIGNAL_FUNC) cmd_foreach_window); + + signal_remove("default command", (SIGNAL_FUNC) cmd_window_default_command); +} diff --git a/src/fe-common/core/window-items.c b/src/fe-common/core/window-items.c new file mode 100644 index 0000000..c60c796 --- /dev/null +++ b/src/fe-common/core/window-items.c @@ -0,0 +1,355 @@ +/* + window-items.c : irssi + + Copyright (C) 2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/core/modules.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/servers.h> +#include <irssi/src/core/channels.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/core/levels.h> + +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/core/window-items.h> +#include <irssi/src/fe-common/core/printtext.h> + +static void window_item_add_signal(WINDOW_REC *window, WI_ITEM_REC *item, int automatic, int send_signal) +{ + g_return_if_fail(window != NULL); + g_return_if_fail(item != NULL); + g_return_if_fail(item->window == NULL); + + item->window = window; + + if (window->items == NULL) { + window->active = item; + window->active_server = item->server; + } + + if (!automatic || settings_get_bool("window_auto_change")) { + if (automatic) + signal_emit("window changed automatic", 1, window); + window_set_active(window); + } + + window->items = g_slist_append(window->items, item); + if (send_signal) + signal_emit("window item new", 2, window, item); + + if (g_slist_length(window->items) == 1 || + (!automatic && settings_get_bool("autofocus_new_items"))) { + window->active = NULL; + window_item_set_active(window, item); + } +} + +void window_item_add(WINDOW_REC *window, WI_ITEM_REC *item, int automatic) +{ + window_item_add_signal(window, item, automatic, TRUE); +} + +static void window_item_remove_signal(WI_ITEM_REC *item, int emit_signal) +{ + WINDOW_REC *window; + + g_return_if_fail(item != NULL); + + window = window_item_window(item); + + if (window == NULL) + return; + + item->window = NULL; + window->items = g_slist_remove(window->items, item); + + if (window->active == item) { + window_item_set_active(window, window->items == NULL ? NULL : + window->items->data); + } + + if (emit_signal) + signal_emit("window item remove", 2, window, item); +} + +void window_item_remove(WI_ITEM_REC *item) +{ + window_item_remove_signal(item, TRUE); +} + +void window_item_destroy(WI_ITEM_REC *item) +{ + window_item_remove(item); + item->destroy(item); +} + +void window_item_change_server(WI_ITEM_REC *item, void *server) +{ + WINDOW_REC *window; + + g_return_if_fail(item != NULL); + + window = window_item_window(item); + item->server = server; + + signal_emit("window item server changed", 2, window, item); + if (window->active == item) window_change_server(window, item->server); +} + +void window_item_set_active(WINDOW_REC *window, WI_ITEM_REC *item) +{ + WINDOW_REC *old_window; + + g_return_if_fail(window != NULL); + + if (item != NULL) { + old_window = window_item_window(item); + if (old_window != window) { + /* move item to different window */ + window_item_remove_signal(item, FALSE); + window_item_add_signal(window, item, FALSE, FALSE); + signal_emit("window item moved", 3, window, item, old_window); + } + } + + if (window->active != item) { + window->active = item; + if (item != NULL && window->active_server != item->server) + window_change_server(window, item->server); + signal_emit("window item changed", 2, window, item); + } +} + +/* Return TRUE if `item' is the active window item in the window. + `item' can be NULL. */ +int window_item_is_active(WI_ITEM_REC *item) +{ + WINDOW_REC *window; + + if (item == NULL) + return FALSE; + + window = window_item_window(item); + if (window == NULL) + return FALSE; + + return window->active == item; +} + +void window_item_prev(WINDOW_REC *window) +{ + WI_ITEM_REC *last; + GSList *tmp; + + g_return_if_fail(window != NULL); + + last = NULL; + for (tmp = window->items; tmp != NULL; tmp = tmp->next) { + WI_ITEM_REC *rec = tmp->data; + + if (rec != window->active) + last = rec; + else { + /* current channel. did we find anything? + if not, go to the last channel */ + if (last != NULL) break; + } + } + + if (last != NULL) + window_item_set_active(window, last); +} + +void window_item_next(WINDOW_REC *window) +{ + WI_ITEM_REC *next; + GSList *tmp; + int gone; + + g_return_if_fail(window != NULL); + + next = NULL; gone = FALSE; + for (tmp = window->items; tmp != NULL; tmp = tmp->next) { + WI_ITEM_REC *rec = tmp->data; + + if (rec == window->active) + gone = TRUE; + else { + if (gone) { + /* found the next channel */ + next = rec; + break; + } + + if (next == NULL) + next = rec; /* fallback to first channel */ + } + } + + if (next != NULL) + window_item_set_active(window, next); +} + +WI_ITEM_REC *window_item_find_window(WINDOW_REC *window, + void *server, const char *name) +{ + GSList *tmp; + + for (tmp = window->items; tmp != NULL; tmp = tmp->next) { + WI_ITEM_REC *rec = tmp->data; + + if ((server == NULL || rec->server == server) && + (g_ascii_strcasecmp(name, rec->visible_name) == 0 + || (rec->name && g_ascii_strcasecmp(name, rec->name) == 0))) + return rec; + } + return NULL; +} + +/* Find wanted window item by name. `server' can be NULL. */ +WI_ITEM_REC *window_item_find(void *server, const char *name) +{ + WI_ITEM_REC *item; + GSList *tmp; + + g_return_val_if_fail(name != NULL, NULL); + + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + item = window_item_find_window(rec, server, name); + if (item != NULL) return item; + } + + return NULL; +} + +static int window_bind_has_sticky(WINDOW_REC *window) +{ + GSList *tmp; + + for (tmp = window->bound_items; tmp != NULL; tmp = tmp->next) { + WINDOW_BIND_REC *rec = tmp->data; + + if (rec->sticky) + return TRUE; + } + + return FALSE; +} + +void window_item_create(WI_ITEM_REC *item, int automatic) +{ + WINDOW_REC *window; + WINDOW_BIND_REC *bind; + GSList *tmp, *sorted; + int clear_waiting, reuse_unused_windows; + + g_return_if_fail(item != NULL); + + reuse_unused_windows = settings_get_bool("reuse_unused_windows"); + + clear_waiting = TRUE; + window = NULL; + sorted = windows_get_sorted(); + for (tmp = sorted; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + /* is item bound to this window? */ + if (item->server != NULL) { + bind = window_bind_find(rec, item->server->tag, + item->visible_name); + if (bind != NULL) { + if (!bind->sticky) + window_bind_destroy(rec, bind); + window = rec; + clear_waiting = FALSE; + break; + } + } + + /* use this window IF: + - reuse_unused_windows is ON + - window has no existing items + - window has no name + - window has no sticky binds (/LAYOUT SAVEd) + - we already haven't found "good enough" window, + except if + - this is the active window + - old window had some temporary bounds and this + one doesn't + */ + if (reuse_unused_windows && rec->items == NULL && + rec->name == NULL && !window_bind_has_sticky(rec) && + (window == NULL || rec == active_win || + window->bound_items != NULL)) + window = rec; + } + g_slist_free(sorted); + + if (window == NULL && !settings_get_bool("autocreate_windows")) { + /* never create new windows automatically */ + window = active_win; + } + + if (window == NULL) { + /* create new window to use */ + if (settings_get_bool("autocreate_split_windows")) { + signal_emit("gui window create override", 1, + GINT_TO_POINTER(MAIN_WINDOW_TYPE_SPLIT)); + } + window = window_create(item, automatic); + } else { + /* use existing window */ + window_item_add(window, item, automatic); + } + + if (clear_waiting) + window_bind_remove_unsticky(window); +} + +static void signal_window_item_changed(WINDOW_REC *window, WI_ITEM_REC *item) +{ + g_return_if_fail(window != NULL); + + if (g_slist_length(window->items) > 1) { + /* default to printing "talking with ...", + you can override it it you wish */ + printformat(item->server, item->visible_name, + MSGLEVEL_CLIENTNOTICE, + TXT_TALKING_WITH, item->visible_name); + } +} + +void window_items_init(void) +{ + settings_add_bool("lookandfeel", "reuse_unused_windows", FALSE); + settings_add_bool("lookandfeel", "autocreate_windows", TRUE); + settings_add_bool("lookandfeel", "autocreate_split_windows", FALSE); + settings_add_bool("lookandfeel", "autofocus_new_items", TRUE); + + signal_add_last("window item changed", (SIGNAL_FUNC) signal_window_item_changed); +} + +void window_items_deinit(void) +{ + signal_remove("window item changed", (SIGNAL_FUNC) signal_window_item_changed); +} diff --git a/src/fe-common/core/window-items.h b/src/fe-common/core/window-items.h new file mode 100644 index 0000000..5bfa92f --- /dev/null +++ b/src/fe-common/core/window-items.h @@ -0,0 +1,34 @@ +#ifndef IRSSI_FE_COMMON_CORE_WINDOW_ITEMS_H +#define IRSSI_FE_COMMON_CORE_WINDOW_ITEMS_H + +#include <irssi/src/fe-common/core/fe-windows.h> + +/* Add/remove/destroy window item from `window' */ +void window_item_add(WINDOW_REC *window, WI_ITEM_REC *item, int automatic); +void window_item_remove(WI_ITEM_REC *item); +void window_item_destroy(WI_ITEM_REC *item); + +/* Find a window for `item' and call window_item_add(). */ +void window_item_create(WI_ITEM_REC *item, int automatic); + +#define window_item_window(item) \ + ((WINDOW_REC *) ((WI_ITEM_REC *) (item))->window) +void window_item_change_server(WI_ITEM_REC *item, void *server); + +void window_item_set_active(WINDOW_REC *window, WI_ITEM_REC *item); +/* Return TRUE if `item' is the active window item in the window. + `item' can be NULL. */ +int window_item_is_active(WI_ITEM_REC *item); + +void window_item_prev(WINDOW_REC *window); +void window_item_next(WINDOW_REC *window); + +/* Find wanted window item by name. `server' can be NULL. */ +WI_ITEM_REC *window_item_find(void *server, const char *name); +WI_ITEM_REC *window_item_find_window(WINDOW_REC *window, + void *server, const char *name); + +void window_items_init(void); +void window_items_deinit(void); + +#endif diff --git a/src/fe-common/core/windows-layout.c b/src/fe-common/core/windows-layout.c new file mode 100644 index 0000000..fa209f4 --- /dev/null +++ b/src/fe-common/core/windows-layout.c @@ -0,0 +1,278 @@ +/* + windows-layout.c : irssi + + Copyright (C) 2000-2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/signals.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/lib-config/iconfig.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/core/chat-protocols.h> +#include <irssi/src/core/servers.h> +#include <irssi/src/core/channels.h> +#include <irssi/src/core/queries.h> + +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/fe-common/core/printtext.h> +#include <irssi/src/fe-common/core/themes.h> +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/core/window-items.h> + +static WINDOW_REC *restore_win; + +static void signal_query_created_curwin(QUERY_REC *query) +{ + g_return_if_fail(IS_QUERY(query)); + + window_item_add(restore_win, (WI_ITEM_REC *) query, TRUE); +} + +static void sig_layout_restore_item(WINDOW_REC *window, const char *type, + CONFIG_NODE *node) +{ + char *name, *tag, *chat_type; + + chat_type = config_node_get_str(node, "chat_type", NULL); + name = config_node_get_str(node, "name", NULL); + tag = config_node_get_str(node, "tag", NULL); + + if (name == NULL || tag == NULL) + return; + + if (g_ascii_strcasecmp(type, "CHANNEL") == 0) { + /* bind channel to window */ + WINDOW_BIND_REC *rec = window_bind_add(window, tag, name); + rec->sticky = TRUE; + } else if (g_ascii_strcasecmp(type, "QUERY") == 0 && chat_type != NULL) { + CHAT_PROTOCOL_REC *protocol; + /* create query immediately */ + signal_add("query created", + (SIGNAL_FUNC) signal_query_created_curwin); + + restore_win = window; + + protocol = chat_protocol_find(chat_type); + if (protocol == NULL || protocol->not_initialized) { + WINDOW_BIND_REC *rec = window_bind_add(window, tag, name); + rec->type = module_get_uniq_id_str("WINDOW ITEM TYPE", "QUERY"); + } else if (protocol->query_create != NULL) { + protocol->query_create(tag, name, TRUE); + } else { + QUERY_REC *query; + + query = g_new0(QUERY_REC, 1); + query->chat_type = chat_protocol_lookup(chat_type); + query->name = g_strdup(name); + query->server_tag = g_strdup(tag); + query_init(query, TRUE); + } + + signal_remove("query created", + (SIGNAL_FUNC) signal_query_created_curwin); + } +} + +static void window_add_items(WINDOW_REC *window, CONFIG_NODE *node) +{ + GSList *tmp; + char *type; + + if (node == NULL) + return; + + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) { + CONFIG_NODE *node = tmp->data; + + type = config_node_get_str(node, "type", NULL); + if (type != NULL) { + signal_emit("layout restore item", 3, + window, type, node); + } + } +} + +void windows_layout_restore(void) +{ + signal_emit("layout restore", 0); +} + +static void sig_layout_restore(void) +{ + WINDOW_REC *window; + CONFIG_NODE *node; + GSList *tmp; + + node = iconfig_node_traverse("windows", FALSE); + if (node == NULL) return; + + tmp = config_node_first(node->value); + for (; tmp != NULL; tmp = config_node_next(tmp)) { + CONFIG_NODE *node = tmp->data; + + if (node->key == NULL) continue; + window = window_find_refnum(atoi(node->key)); + if (window == NULL) + window = window_create(NULL, TRUE); + + window_set_refnum(window, atoi(node->key)); + window->sticky_refnum = config_node_get_bool(node, "sticky_refnum", FALSE); + window->immortal = config_node_get_bool(node, "immortal", FALSE); + window_set_name(window, config_node_get_str(node, "name", NULL)); + window_set_history(window, config_node_get_str(node, "history_name", NULL)); + window_set_level(window, level2bits(config_node_get_str(node, "level", ""), NULL)); + + window->servertag = g_strdup(config_node_get_str(node, "servertag", NULL)); + window->theme_name = g_strdup(config_node_get_str(node, "theme", NULL)); + if (window->theme_name != NULL) + window->theme = theme_load(window->theme_name); + + window_add_items(window, iconfig_node_section(node, "items", -1)); + signal_emit("layout restore window", 2, window, node); + } +} + +static void sig_layout_save_item(WINDOW_REC *window, WI_ITEM_REC *item, + CONFIG_NODE *node) +{ + CONFIG_NODE *subnode; + CHAT_PROTOCOL_REC *proto; + const char *type; + WINDOW_BIND_REC *rec; + + type = module_find_id_str("WINDOW ITEM TYPE", item->type); + if (type == NULL) + return; + + subnode = iconfig_node_section(node, NULL, NODE_TYPE_BLOCK); + + iconfig_node_set_str(subnode, "type", type); + proto = item->chat_type == 0 ? NULL : + chat_protocol_find_id(item->chat_type); + if (proto != NULL) + iconfig_node_set_str(subnode, "chat_type", proto->name); + iconfig_node_set_str(subnode, "name", item->visible_name); + + if (item->server != NULL) { + iconfig_node_set_str(subnode, "tag", item->server->tag); + if (IS_CHANNEL(item)) { + rec = window_bind_add(window, item->server->tag, item->visible_name); + if (rec != NULL) + rec->sticky = TRUE; + } + } else if (IS_QUERY(item)) { + iconfig_node_set_str(subnode, "tag", QUERY(item)->server_tag); + } +} + +static void window_save_items(WINDOW_REC *window, CONFIG_NODE *node) +{ + GSList *tmp; + + node = iconfig_node_section(node, "items", NODE_TYPE_LIST); + for (tmp = window->items; tmp != NULL; tmp = tmp->next) + signal_emit("layout save item", 3, window, tmp->data, node); +} + +static void window_save(WINDOW_REC *window, CONFIG_NODE *node) +{ + char refnum[MAX_INT_STRLEN]; + + ltoa(refnum, window->refnum); + node = iconfig_node_section(node, refnum, NODE_TYPE_BLOCK); + + if (window->sticky_refnum) + iconfig_node_set_bool(node, "sticky_refnum", TRUE); + + if (window->immortal) + iconfig_node_set_bool(node, "immortal", TRUE); + + if (window->name != NULL) + iconfig_node_set_str(node, "name", window->name); + + if (window->history_name != NULL) + iconfig_node_set_str(node, "history_name", window->history_name); + + if (window->servertag != NULL) + iconfig_node_set_str(node, "servertag", window->servertag); + if (window->level != 0) { + char *level = bits2level(window->level); + iconfig_node_set_str(node, "level", level); + g_free(level); + } + if (window->theme_name != NULL) + iconfig_node_set_str(node, "theme", window->theme_name); + + while (window->bound_items != NULL) + window_bind_destroy(window, window->bound_items->data); + if (window->items != NULL) + window_save_items(window, node); + + signal_emit("layout save window", 2, window, node); +} + +void windows_layout_save(void) +{ + CONFIG_NODE *node; + GSList *sorted; + + iconfig_set_str(NULL, "windows", NULL); + node = iconfig_node_traverse("windows", TRUE); + + sorted = windows_get_sorted(); + g_slist_foreach(sorted, (GFunc) window_save, node); + g_slist_free(sorted); + signal_emit("layout save", 0); + + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_WINDOWS_LAYOUT_SAVED); +} + +void windows_layout_reset(void) +{ + GSList *tmp; + + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *window = tmp->data; + while (window->bound_items != NULL) + window_bind_destroy(window, window->bound_items->data); + } + + iconfig_set_str(NULL, "windows", NULL); + signal_emit("layout reset", 0); + + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_WINDOWS_LAYOUT_RESET); +} + +void windows_layout_init(void) +{ + signal_add("layout restore item", (SIGNAL_FUNC) sig_layout_restore_item); + signal_add("layout restore", (SIGNAL_FUNC) sig_layout_restore); + signal_add("layout save item", (SIGNAL_FUNC) sig_layout_save_item); +} + +void windows_layout_deinit(void) +{ + signal_remove("layout restore item", (SIGNAL_FUNC) sig_layout_restore_item); + signal_remove("layout restore", (SIGNAL_FUNC) sig_layout_restore); + signal_remove("layout save item", (SIGNAL_FUNC) sig_layout_save_item); +} diff --git a/src/fe-common/core/windows-layout.h b/src/fe-common/core/windows-layout.h new file mode 100644 index 0000000..706fb78 --- /dev/null +++ b/src/fe-common/core/windows-layout.h @@ -0,0 +1,11 @@ +#ifndef IRSSI_FE_COMMON_CORE_WINDOWS_LAYOUT_H +#define IRSSI_FE_COMMON_CORE_WINDOWS_LAYOUT_H + +void windows_layout_restore(void); +void windows_layout_save(void); +void windows_layout_reset(void); + +void windows_layout_init(void); +void windows_layout_deinit(void); + +#endif diff --git a/src/fe-common/irc/Makefile.am b/src/fe-common/irc/Makefile.am new file mode 100644 index 0000000..8a04bee --- /dev/null +++ b/src/fe-common/irc/Makefile.am @@ -0,0 +1,42 @@ +SUBDIRS = dcc notifylist + +noinst_LIBRARIES = libfe_common_irc.a + +AM_CPPFLAGS = \ + -I$(top_builddir) \ + $(GLIB_CFLAGS) \ + -DHELPDIR=\""$(datadir)/irssi/help"\" \ + -DSYSCONFDIR=\""$(sysconfdir)"\" + +real_sources = \ + fe-irc-channels.c \ + fe-irc-commands.c \ + fe-irc-messages.c \ + fe-irc-queries.c \ + fe-irc-server.c \ + fe-ircnet.c \ + fe-ctcp.c \ + fe-events.c \ + fe-events-numeric.c \ + fe-modes.c \ + fe-netjoin.c \ + fe-netsplit.c \ + fe-common-irc.c \ + fe-whois.c \ + fe-sasl.c \ + fe-cap.c \ + irc-completion.c \ + module-formats.c + +libfe_common_irc_a_SOURCES = \ + $(real_sources) \ + irc-modules.c + +pkginc_fe_common_ircdir=$(pkgincludedir)/src/fe-common/irc +pkginc_fe_common_irc_HEADERS = \ + fe-irc-server.h \ + fe-irc-channels.h \ + module.h \ + module-formats.h + +EXTRA_DIST = meson.build diff --git a/src/fe-common/irc/Makefile.in b/src/fe-common/irc/Makefile.in new file mode 100644 index 0000000..202076b --- /dev/null +++ b/src/fe-common/irc/Makefile.in @@ -0,0 +1,929 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/fe-common/irc +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/glib-2.0.m4 \ + $(top_srcdir)/m4/glibtests.m4 $(top_srcdir)/m4/libgcrypt.m4 \ + $(top_srcdir)/m4/libotr.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_fe_common_irc_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/irssi-config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +am__v_AR_1 = +libfe_common_irc_a_AR = $(AR) $(ARFLAGS) +libfe_common_irc_a_LIBADD = +am__objects_1 = fe-irc-channels.$(OBJEXT) fe-irc-commands.$(OBJEXT) \ + fe-irc-messages.$(OBJEXT) fe-irc-queries.$(OBJEXT) \ + fe-irc-server.$(OBJEXT) fe-ircnet.$(OBJEXT) fe-ctcp.$(OBJEXT) \ + fe-events.$(OBJEXT) fe-events-numeric.$(OBJEXT) \ + fe-modes.$(OBJEXT) fe-netjoin.$(OBJEXT) fe-netsplit.$(OBJEXT) \ + fe-common-irc.$(OBJEXT) fe-whois.$(OBJEXT) fe-sasl.$(OBJEXT) \ + fe-cap.$(OBJEXT) irc-completion.$(OBJEXT) \ + module-formats.$(OBJEXT) +am_libfe_common_irc_a_OBJECTS = $(am__objects_1) irc-modules.$(OBJEXT) +libfe_common_irc_a_OBJECTS = $(am_libfe_common_irc_a_OBJECTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/fe-cap.Po \ + ./$(DEPDIR)/fe-common-irc.Po ./$(DEPDIR)/fe-ctcp.Po \ + ./$(DEPDIR)/fe-events-numeric.Po ./$(DEPDIR)/fe-events.Po \ + ./$(DEPDIR)/fe-irc-channels.Po ./$(DEPDIR)/fe-irc-commands.Po \ + ./$(DEPDIR)/fe-irc-messages.Po ./$(DEPDIR)/fe-irc-queries.Po \ + ./$(DEPDIR)/fe-irc-server.Po ./$(DEPDIR)/fe-ircnet.Po \ + ./$(DEPDIR)/fe-modes.Po ./$(DEPDIR)/fe-netjoin.Po \ + ./$(DEPDIR)/fe-netsplit.Po ./$(DEPDIR)/fe-sasl.Po \ + ./$(DEPDIR)/fe-whois.Po ./$(DEPDIR)/irc-completion.Po \ + ./$(DEPDIR)/irc-modules.Po ./$(DEPDIR)/module-formats.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libfe_common_irc_a_SOURCES) +DIST_SOURCES = $(libfe_common_irc_a_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkginc_fe_common_ircdir)" +HEADERS = $(pkginc_fe_common_irc_HEADERS) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(top_srcdir)/build-aux/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CHAT_MODULES = @CHAT_MODULES@ +COMMON_LIBS = @COMMON_LIBS@ +COMMON_NOUI_LIBS = @COMMON_NOUI_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FUZZER_LIBS = @FUZZER_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GOBJECT_QUERY = @GOBJECT_QUERY@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBOTR_CFLAGS = @LIBOTR_CFLAGS@ +LIBOTR_LIBS = @LIBOTR_LIBS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +OTR_CFLAGS = @OTR_CFLAGS@ +OTR_LDFLAGS = @OTR_LDFLAGS@ +OTR_LINK_FLAGS = @OTR_LINK_FLAGS@ +OTR_LINK_LIBS = @OTR_LINK_LIBS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL_CFLAGS = @PERL_CFLAGS@ +PERL_EXTRA_OPTS = @PERL_EXTRA_OPTS@ +PERL_FE_LINK_LIBS = @PERL_FE_LINK_LIBS@ +PERL_LDFLAGS = @PERL_LDFLAGS@ +PERL_LINK_FLAGS = @PERL_LINK_FLAGS@ +PERL_LINK_LIBS = @PERL_LINK_LIBS@ +PERL_MM_OPT = @PERL_MM_OPT@ +PERL_MM_PARAMS = @PERL_MM_PARAMS@ +PERL_STATIC_LIBS = @PERL_STATIC_LIBS@ +PERL_USE_LIB = @PERL_USE_LIB@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROG_LIBS = @PROG_LIBS@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TEXTUI_LIBS = @TEXTUI_LIBS@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +installed_test_metadir = @installed_test_metadir@ +installed_testdir = @installed_testdir@ +irc_MODULES = @irc_MODULES@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +otr_module_lib = @otr_module_lib@ +otr_static_lib = @otr_static_lib@ +pdfdir = @pdfdir@ +perl_module_fe_lib = @perl_module_fe_lib@ +perl_module_lib = @perl_module_lib@ +perl_static_fe_lib = @perl_static_fe_lib@ +perl_static_lib = @perl_static_lib@ +perlpath = @perlpath@ +pkgconfigdir = @pkgconfigdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sedpath = @sedpath@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = dcc notifylist +noinst_LIBRARIES = libfe_common_irc.a +AM_CPPFLAGS = \ + -I$(top_builddir) \ + $(GLIB_CFLAGS) \ + -DHELPDIR=\""$(datadir)/irssi/help"\" \ + -DSYSCONFDIR=\""$(sysconfdir)"\" + +real_sources = \ + fe-irc-channels.c \ + fe-irc-commands.c \ + fe-irc-messages.c \ + fe-irc-queries.c \ + fe-irc-server.c \ + fe-ircnet.c \ + fe-ctcp.c \ + fe-events.c \ + fe-events-numeric.c \ + fe-modes.c \ + fe-netjoin.c \ + fe-netsplit.c \ + fe-common-irc.c \ + fe-whois.c \ + fe-sasl.c \ + fe-cap.c \ + irc-completion.c \ + module-formats.c + +libfe_common_irc_a_SOURCES = \ + $(real_sources) \ + irc-modules.c + +pkginc_fe_common_ircdir = $(pkgincludedir)/src/fe-common/irc +pkginc_fe_common_irc_HEADERS = \ + fe-irc-server.h \ + fe-irc-channels.h \ + module.h \ + module-formats.h + +EXTRA_DIST = meson.build +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/fe-common/irc/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/fe-common/irc/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +libfe_common_irc.a: $(libfe_common_irc_a_OBJECTS) $(libfe_common_irc_a_DEPENDENCIES) $(EXTRA_libfe_common_irc_a_DEPENDENCIES) + $(AM_V_at)-rm -f libfe_common_irc.a + $(AM_V_AR)$(libfe_common_irc_a_AR) libfe_common_irc.a $(libfe_common_irc_a_OBJECTS) $(libfe_common_irc_a_LIBADD) + $(AM_V_at)$(RANLIB) libfe_common_irc.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-cap.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-common-irc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-ctcp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-events-numeric.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-events.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-irc-channels.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-irc-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-irc-messages.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-irc-queries.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-irc-server.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-ircnet.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-modes.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-netjoin.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-netsplit.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-sasl.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-whois.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/irc-completion.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/irc-modules.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/module-formats.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkginc_fe_common_ircHEADERS: $(pkginc_fe_common_irc_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginc_fe_common_irc_HEADERS)'; test -n "$(pkginc_fe_common_ircdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_fe_common_ircdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkginc_fe_common_ircdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_fe_common_ircdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_fe_common_ircdir)" || exit $$?; \ + done + +uninstall-pkginc_fe_common_ircHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginc_fe_common_irc_HEADERS)'; test -n "$(pkginc_fe_common_ircdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkginc_fe_common_ircdir)'; $(am__uninstall_files_from_dir) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) $(HEADERS) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(pkginc_fe_common_ircdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/fe-cap.Po + -rm -f ./$(DEPDIR)/fe-common-irc.Po + -rm -f ./$(DEPDIR)/fe-ctcp.Po + -rm -f ./$(DEPDIR)/fe-events-numeric.Po + -rm -f ./$(DEPDIR)/fe-events.Po + -rm -f ./$(DEPDIR)/fe-irc-channels.Po + -rm -f ./$(DEPDIR)/fe-irc-commands.Po + -rm -f ./$(DEPDIR)/fe-irc-messages.Po + -rm -f ./$(DEPDIR)/fe-irc-queries.Po + -rm -f ./$(DEPDIR)/fe-irc-server.Po + -rm -f ./$(DEPDIR)/fe-ircnet.Po + -rm -f ./$(DEPDIR)/fe-modes.Po + -rm -f ./$(DEPDIR)/fe-netjoin.Po + -rm -f ./$(DEPDIR)/fe-netsplit.Po + -rm -f ./$(DEPDIR)/fe-sasl.Po + -rm -f ./$(DEPDIR)/fe-whois.Po + -rm -f ./$(DEPDIR)/irc-completion.Po + -rm -f ./$(DEPDIR)/irc-modules.Po + -rm -f ./$(DEPDIR)/module-formats.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-pkginc_fe_common_ircHEADERS + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/fe-cap.Po + -rm -f ./$(DEPDIR)/fe-common-irc.Po + -rm -f ./$(DEPDIR)/fe-ctcp.Po + -rm -f ./$(DEPDIR)/fe-events-numeric.Po + -rm -f ./$(DEPDIR)/fe-events.Po + -rm -f ./$(DEPDIR)/fe-irc-channels.Po + -rm -f ./$(DEPDIR)/fe-irc-commands.Po + -rm -f ./$(DEPDIR)/fe-irc-messages.Po + -rm -f ./$(DEPDIR)/fe-irc-queries.Po + -rm -f ./$(DEPDIR)/fe-irc-server.Po + -rm -f ./$(DEPDIR)/fe-ircnet.Po + -rm -f ./$(DEPDIR)/fe-modes.Po + -rm -f ./$(DEPDIR)/fe-netjoin.Po + -rm -f ./$(DEPDIR)/fe-netsplit.Po + -rm -f ./$(DEPDIR)/fe-sasl.Po + -rm -f ./$(DEPDIR)/fe-whois.Po + -rm -f ./$(DEPDIR)/irc-completion.Po + -rm -f ./$(DEPDIR)/irc-modules.Po + -rm -f ./$(DEPDIR)/module-formats.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-pkginc_fe_common_ircHEADERS + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-am clean clean-generic clean-libtool \ + clean-noinstLIBRARIES cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-pkginc_fe_common_ircHEADERS \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am \ + uninstall-pkginc_fe_common_ircHEADERS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/fe-common/irc/dcc/Makefile.am b/src/fe-common/irc/dcc/Makefile.am new file mode 100644 index 0000000..feee349 --- /dev/null +++ b/src/fe-common/irc/dcc/Makefile.am @@ -0,0 +1,24 @@ +noinst_LIBRARIES = libfe_irc_dcc.a + +AM_CPPFLAGS = \ + -I$(top_builddir) \ + $(GLIB_CFLAGS) \ + -DHELPDIR=\""$(datadir)/irssi/help"\" \ + -DSYSCONFDIR=\""$(sysconfdir)"\" + +libfe_irc_dcc_a_SOURCES = \ + fe-dcc.c \ + fe-dcc-chat.c \ + fe-dcc-chat-messages.c \ + fe-dcc-get.c \ + fe-dcc-send.c \ + module-formats.c \ + fe-dcc-server.c + +pkginc_fe_common_irc_dccdir=$(pkgincludedir)/src/fe-common/irc/dcc +pkginc_fe_common_irc_dcc_HEADERS = \ + module.h \ + module-formats.h \ + fe-dcc.h + +EXTRA_DIST = meson.build diff --git a/src/fe-common/irc/dcc/Makefile.in b/src/fe-common/irc/dcc/Makefile.in new file mode 100644 index 0000000..f73bf3b --- /dev/null +++ b/src/fe-common/irc/dcc/Makefile.in @@ -0,0 +1,750 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/fe-common/irc/dcc +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/glib-2.0.m4 \ + $(top_srcdir)/m4/glibtests.m4 $(top_srcdir)/m4/libgcrypt.m4 \ + $(top_srcdir)/m4/libotr.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am \ + $(pkginc_fe_common_irc_dcc_HEADERS) $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/irssi-config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +am__v_AR_1 = +libfe_irc_dcc_a_AR = $(AR) $(ARFLAGS) +libfe_irc_dcc_a_LIBADD = +am_libfe_irc_dcc_a_OBJECTS = fe-dcc.$(OBJEXT) fe-dcc-chat.$(OBJEXT) \ + fe-dcc-chat-messages.$(OBJEXT) fe-dcc-get.$(OBJEXT) \ + fe-dcc-send.$(OBJEXT) module-formats.$(OBJEXT) \ + fe-dcc-server.$(OBJEXT) +libfe_irc_dcc_a_OBJECTS = $(am_libfe_irc_dcc_a_OBJECTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/fe-dcc-chat-messages.Po \ + ./$(DEPDIR)/fe-dcc-chat.Po ./$(DEPDIR)/fe-dcc-get.Po \ + ./$(DEPDIR)/fe-dcc-send.Po ./$(DEPDIR)/fe-dcc-server.Po \ + ./$(DEPDIR)/fe-dcc.Po ./$(DEPDIR)/module-formats.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libfe_irc_dcc_a_SOURCES) +DIST_SOURCES = $(libfe_irc_dcc_a_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkginc_fe_common_irc_dccdir)" +HEADERS = $(pkginc_fe_common_irc_dcc_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(top_srcdir)/build-aux/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CHAT_MODULES = @CHAT_MODULES@ +COMMON_LIBS = @COMMON_LIBS@ +COMMON_NOUI_LIBS = @COMMON_NOUI_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FUZZER_LIBS = @FUZZER_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GOBJECT_QUERY = @GOBJECT_QUERY@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBOTR_CFLAGS = @LIBOTR_CFLAGS@ +LIBOTR_LIBS = @LIBOTR_LIBS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +OTR_CFLAGS = @OTR_CFLAGS@ +OTR_LDFLAGS = @OTR_LDFLAGS@ +OTR_LINK_FLAGS = @OTR_LINK_FLAGS@ +OTR_LINK_LIBS = @OTR_LINK_LIBS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL_CFLAGS = @PERL_CFLAGS@ +PERL_EXTRA_OPTS = @PERL_EXTRA_OPTS@ +PERL_FE_LINK_LIBS = @PERL_FE_LINK_LIBS@ +PERL_LDFLAGS = @PERL_LDFLAGS@ +PERL_LINK_FLAGS = @PERL_LINK_FLAGS@ +PERL_LINK_LIBS = @PERL_LINK_LIBS@ +PERL_MM_OPT = @PERL_MM_OPT@ +PERL_MM_PARAMS = @PERL_MM_PARAMS@ +PERL_STATIC_LIBS = @PERL_STATIC_LIBS@ +PERL_USE_LIB = @PERL_USE_LIB@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROG_LIBS = @PROG_LIBS@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TEXTUI_LIBS = @TEXTUI_LIBS@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +installed_test_metadir = @installed_test_metadir@ +installed_testdir = @installed_testdir@ +irc_MODULES = @irc_MODULES@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +otr_module_lib = @otr_module_lib@ +otr_static_lib = @otr_static_lib@ +pdfdir = @pdfdir@ +perl_module_fe_lib = @perl_module_fe_lib@ +perl_module_lib = @perl_module_lib@ +perl_static_fe_lib = @perl_static_fe_lib@ +perl_static_lib = @perl_static_lib@ +perlpath = @perlpath@ +pkgconfigdir = @pkgconfigdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sedpath = @sedpath@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +noinst_LIBRARIES = libfe_irc_dcc.a +AM_CPPFLAGS = \ + -I$(top_builddir) \ + $(GLIB_CFLAGS) \ + -DHELPDIR=\""$(datadir)/irssi/help"\" \ + -DSYSCONFDIR=\""$(sysconfdir)"\" + +libfe_irc_dcc_a_SOURCES = \ + fe-dcc.c \ + fe-dcc-chat.c \ + fe-dcc-chat-messages.c \ + fe-dcc-get.c \ + fe-dcc-send.c \ + module-formats.c \ + fe-dcc-server.c + +pkginc_fe_common_irc_dccdir = $(pkgincludedir)/src/fe-common/irc/dcc +pkginc_fe_common_irc_dcc_HEADERS = \ + module.h \ + module-formats.h \ + fe-dcc.h + +EXTRA_DIST = meson.build +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/fe-common/irc/dcc/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/fe-common/irc/dcc/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +libfe_irc_dcc.a: $(libfe_irc_dcc_a_OBJECTS) $(libfe_irc_dcc_a_DEPENDENCIES) $(EXTRA_libfe_irc_dcc_a_DEPENDENCIES) + $(AM_V_at)-rm -f libfe_irc_dcc.a + $(AM_V_AR)$(libfe_irc_dcc_a_AR) libfe_irc_dcc.a $(libfe_irc_dcc_a_OBJECTS) $(libfe_irc_dcc_a_LIBADD) + $(AM_V_at)$(RANLIB) libfe_irc_dcc.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-dcc-chat-messages.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-dcc-chat.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-dcc-get.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-dcc-send.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-dcc-server.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-dcc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/module-formats.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkginc_fe_common_irc_dccHEADERS: $(pkginc_fe_common_irc_dcc_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginc_fe_common_irc_dcc_HEADERS)'; test -n "$(pkginc_fe_common_irc_dccdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_fe_common_irc_dccdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkginc_fe_common_irc_dccdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_fe_common_irc_dccdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_fe_common_irc_dccdir)" || exit $$?; \ + done + +uninstall-pkginc_fe_common_irc_dccHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginc_fe_common_irc_dcc_HEADERS)'; test -n "$(pkginc_fe_common_irc_dccdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkginc_fe_common_irc_dccdir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(pkginc_fe_common_irc_dccdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/fe-dcc-chat-messages.Po + -rm -f ./$(DEPDIR)/fe-dcc-chat.Po + -rm -f ./$(DEPDIR)/fe-dcc-get.Po + -rm -f ./$(DEPDIR)/fe-dcc-send.Po + -rm -f ./$(DEPDIR)/fe-dcc-server.Po + -rm -f ./$(DEPDIR)/fe-dcc.Po + -rm -f ./$(DEPDIR)/module-formats.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pkginc_fe_common_irc_dccHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/fe-dcc-chat-messages.Po + -rm -f ./$(DEPDIR)/fe-dcc-chat.Po + -rm -f ./$(DEPDIR)/fe-dcc-get.Po + -rm -f ./$(DEPDIR)/fe-dcc-send.Po + -rm -f ./$(DEPDIR)/fe-dcc-server.Po + -rm -f ./$(DEPDIR)/fe-dcc.Po + -rm -f ./$(DEPDIR)/module-formats.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pkginc_fe_common_irc_dccHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-pkginc_fe_common_irc_dccHEADERS \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am \ + uninstall-pkginc_fe_common_irc_dccHEADERS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/fe-common/irc/dcc/fe-dcc-chat-messages.c b/src/fe-common/irc/dcc/fe-dcc-chat-messages.c new file mode 100644 index 0000000..8dd67f7 --- /dev/null +++ b/src/fe-common/irc/dcc/fe-dcc-chat-messages.c @@ -0,0 +1,164 @@ +/* + fe-dcc-chat-messages.c : irssi + + Copyright (C) 2002 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/signals.h> +#include <irssi/src/core/levels.h> + +#include <irssi/src/irc/core/irc-servers.h> +#include <irssi/src/irc/core/irc-queries.h> +#include <irssi/src/irc/dcc/dcc-chat.h> +#include <irssi/src/core/ignore.h> + +#include <irssi/src/fe-common/irc/dcc/module-formats.h> +#include <irssi/src/fe-common/core/printtext.h> + +static void sig_message_dcc_own(CHAT_DCC_REC *dcc, const char *msg) +{ + TEXT_DEST_REC dest; + QUERY_REC *query; + char *tag; + + tag = g_strconcat("=", dcc->id, NULL); + query = query_find(NULL, tag); + + format_create_dest_tag(&dest, dcc->server, dcc->servertag, tag, + MSGLEVEL_DCCMSGS | MSGLEVEL_NOHILIGHT | + MSGLEVEL_NO_ACT, NULL); + + printformat_dest(&dest, query != NULL ? IRCTXT_OWN_DCC_QUERY : + IRCTXT_OWN_DCC, dcc->mynick, dcc->id, msg); + g_free(tag); +} + +static void sig_message_dcc_own_action(CHAT_DCC_REC *dcc, const char *msg) +{ + TEXT_DEST_REC dest; + QUERY_REC *query; + char *tag; + + tag = g_strconcat("=", dcc->id, NULL); + query = query_find(NULL, tag); + + format_create_dest_tag(&dest, dcc->server, dcc->servertag, tag, + MSGLEVEL_DCCMSGS | MSGLEVEL_ACTIONS | + MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT, NULL); + + printformat_dest(&dest, query != NULL ? IRCTXT_OWN_DCC_ACTION_QUERY : + IRCTXT_OWN_DCC_ACTION, dcc->mynick, dcc->id, msg); + g_free(tag); +} + +static void sig_message_dcc_own_ctcp(CHAT_DCC_REC *dcc, const char *cmd, + const char *data) +{ + TEXT_DEST_REC dest; + char *tag; + + tag = g_strconcat("=", dcc->id, NULL); + + format_create_dest_tag(&dest, dcc->server, dcc->servertag, tag, + MSGLEVEL_DCC | MSGLEVEL_CTCPS | + MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT, NULL); + + printformat_dest(&dest, IRCTXT_OWN_DCC_CTCP, dcc->id, cmd, data); + g_free(tag); +} + +static void sig_message_dcc(CHAT_DCC_REC *dcc, const char *msg) +{ + TEXT_DEST_REC dest; + QUERY_REC *query; + char *tag; + int level = MSGLEVEL_DCCMSGS; + + tag = g_strconcat("=", dcc->id, NULL); + query = query_find(NULL, tag); + + ignore_check_plus(SERVER(dcc->server), tag, dcc->addrstr, NULL, msg, + &level, FALSE); + + format_create_dest_tag(&dest, dcc->server, dcc->servertag, tag, + level, NULL); + + printformat_dest(&dest, query != NULL ? IRCTXT_DCC_MSG_QUERY : + IRCTXT_DCC_MSG, dcc->id, msg); + g_free(tag); +} + +static void sig_message_dcc_action(CHAT_DCC_REC *dcc, const char *msg) +{ + TEXT_DEST_REC dest; + QUERY_REC *query; + char *tag; + int level = MSGLEVEL_DCCMSGS | MSGLEVEL_ACTIONS; + + tag = g_strconcat("=", dcc->id, NULL); + query = query_find(NULL, tag); + + ignore_check_plus(SERVER(dcc->server), tag, dcc->addrstr, NULL, msg, + &level, FALSE); + + format_create_dest_tag(&dest, dcc->server, dcc->servertag, tag, + level, NULL); + + printformat_dest(&dest, query != NULL ? IRCTXT_ACTION_DCC_QUERY : + IRCTXT_ACTION_DCC, dcc->id, msg); + g_free(tag); +} + +static void sig_message_dcc_ctcp(CHAT_DCC_REC *dcc, const char *cmd, + const char *data) +{ + TEXT_DEST_REC dest; + char *tag; + int level = MSGLEVEL_DCCMSGS | MSGLEVEL_CTCPS; + + tag = g_strconcat("=", dcc->id, NULL); + + ignore_check_plus(SERVER(dcc->server), tag, dcc->addrstr, NULL, cmd, + &level, FALSE); + + format_create_dest_tag(&dest, dcc->server, dcc->servertag, tag, + level, NULL); + + printformat_dest(&dest, IRCTXT_DCC_CTCP, dcc->id, cmd, data); + g_free(tag); +} + +void fe_dcc_chat_messages_init(void) +{ + signal_add("message dcc own", (SIGNAL_FUNC) sig_message_dcc_own); + signal_add("message dcc own_action", (SIGNAL_FUNC) sig_message_dcc_own_action); + signal_add("message dcc own_ctcp", (SIGNAL_FUNC) sig_message_dcc_own_ctcp); + signal_add("message dcc", (SIGNAL_FUNC) sig_message_dcc); + signal_add("message dcc action", (SIGNAL_FUNC) sig_message_dcc_action); + signal_add("message dcc ctcp", (SIGNAL_FUNC) sig_message_dcc_ctcp); +} + +void fe_dcc_chat_messages_deinit(void) +{ + signal_remove("message dcc own", (SIGNAL_FUNC) sig_message_dcc_own); + signal_remove("message dcc own_action", (SIGNAL_FUNC) sig_message_dcc_own_action); + signal_remove("message dcc own_ctcp", (SIGNAL_FUNC) sig_message_dcc_own_ctcp); + signal_remove("message dcc", (SIGNAL_FUNC) sig_message_dcc); + signal_remove("message dcc action", (SIGNAL_FUNC) sig_message_dcc_action); + signal_remove("message dcc ctcp", (SIGNAL_FUNC) sig_message_dcc_ctcp); +} diff --git a/src/fe-common/irc/dcc/fe-dcc-chat.c b/src/fe-common/irc/dcc/fe-dcc-chat.c new file mode 100644 index 0000000..4099e2f --- /dev/null +++ b/src/fe-common/irc/dcc/fe-dcc-chat.c @@ -0,0 +1,385 @@ +/* + fe-dcc-chat.c : irssi + + Copyright (C) 1999-2002 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/settings.h> +#include <irssi/src/core/misc.h> + +#include <irssi/src/irc/core/irc.h> +#include <irssi/src/irc/core/irc-servers.h> +#include <irssi/src/irc/core/irc-queries.h> +#include <irssi/src/irc/dcc/dcc-chat.h> + +#include <irssi/src/fe-common/irc/dcc/module-formats.h> +#include <irssi/src/fe-common/core/printtext.h> +#include <irssi/src/fe-common/core/fe-messages.h> + +#include <irssi/src/fe-common/core/chat-completion.h> + +void fe_dcc_chat_messages_init(void); +void fe_dcc_chat_messages_deinit(void); + +static void dcc_request(CHAT_DCC_REC *dcc) +{ + if (!IS_DCC_CHAT(dcc)) return; + + printformat(dcc->server, NULL, MSGLEVEL_DCC, + server_ischannel(SERVER(dcc->server), dcc->target) ? IRCTXT_DCC_CHAT_CHANNEL : + IRCTXT_DCC_CHAT, dcc->id, dcc->addrstr, + dcc->port, dcc->target); +} + +static void dcc_connected(CHAT_DCC_REC *dcc) +{ + char *sender; + + if (!IS_DCC_CHAT(dcc)) return; + + sender = g_strconcat("=", dcc->id, NULL); + printformat(dcc->server, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_CHAT_CONNECTED, + dcc->id, dcc->addrstr, dcc->port); + + if (query_find(NULL, sender) == NULL) { + int level = settings_get_level("autocreate_query_level"); + int autocreate_dccquery = (level & MSGLEVEL_DCCMSGS) != 0; + + if (!autocreate_dccquery) + completion_last_message_add(sender); + else + irc_query_create(dcc->servertag, sender, TRUE); + } + g_free(sender); +} + +static void dcc_closed(CHAT_DCC_REC *dcc) +{ + char *sender; + + if (!IS_DCC_CHAT(dcc)) return; + + sender = g_strconcat("=", dcc->id, NULL); + printformat(dcc->server, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_CHAT_DISCONNECTED, dcc->id); + g_free(sender); +} + +static void dcc_chat_msg(CHAT_DCC_REC *dcc, const char *msg) +{ + QUERY_REC *query; + char *sender, *freemsg; + + g_return_if_fail(IS_DCC_CHAT(dcc)); + g_return_if_fail(msg != NULL); + + sender = g_strconcat("=", dcc->id, NULL); + query = query_find(NULL, sender); + + if (settings_get_bool("emphasis")) + msg = freemsg = expand_emphasis((WI_ITEM_REC *) query, msg); + else + freemsg = NULL; + + if (query == NULL) + completion_last_message_add(sender); + signal_emit("message dcc", 2, dcc, msg); + + g_free_not_null(freemsg); + g_free(sender); +} + +static void dcc_chat_action(CHAT_DCC_REC *dcc, const char *msg) +{ + char *sender; + + g_return_if_fail(IS_DCC_CHAT(dcc)); + g_return_if_fail(msg != NULL); + + sender = g_strconcat("=", dcc->id, NULL); + if (query_find(NULL, sender) == NULL) + completion_last_message_add(sender); + + signal_emit("message dcc action", 2, dcc, msg); + g_free(sender); +} + +static void dcc_chat_ctcp(CHAT_DCC_REC *dcc, const char *cmd, const char *data) +{ + g_return_if_fail(IS_DCC_CHAT(dcc)); + + signal_emit("message dcc ctcp", 3, dcc, cmd, data); +} + +static void dcc_error_ctcp(const char *type, const char *data, + const char *nick, const char *addr, + const char *target) +{ + printformat(NULL, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_INVALID_CTCP, type, nick, addr, target); +} + +static void dcc_unknown_ctcp(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr, + const char *target, CHAT_DCC_REC *chat) +{ + char *type, *args; + void *free_arg; + + g_return_if_fail(data != NULL); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, + &type, &args)) + return; + + printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_UNKNOWN_CTCP, + type, nick, args); + cmd_params_free(free_arg); +} + +static void dcc_unknown_reply(IRC_SERVER_REC *server, const char *data, + const char *nick) +{ + char *type, *args; + void *free_arg; + + g_return_if_fail(data != NULL); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, + &type, &args)) + return; + + printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_UNKNOWN_REPLY, + type, nick, args); + cmd_params_free(free_arg); +} + +static void sig_dcc_destroyed(CHAT_DCC_REC *dcc) +{ + QUERY_REC *query; + char *nick; + + if (!IS_DCC_CHAT(dcc)) return; + + nick = g_strconcat("=", dcc->id, NULL); + query = query_find(NULL, nick); + if (query != NULL) { + /* DCC chat closed, close the query with it. */ + if (dcc->connection_lost) query->unwanted = TRUE; + query_destroy(query); + } else { + /* remove nick from msg completion + since it won't work anymore */ + completion_last_message_remove(nick); + } + + g_free(nick); +} + +static void sig_query_destroyed(QUERY_REC *query) +{ + CHAT_DCC_REC *dcc; + + if (*query->name != '=') + return; + + dcc = dcc_chat_find_id(query->name+1); + if (dcc != NULL && !dcc->destroyed) { + /* DCC query window closed, close the dcc chat too. */ + dcc_close(DCC(dcc)); + } +} + +static void dcc_error_close_not_found(const char *type, const char *nick, + const char *fname) +{ + g_return_if_fail(type != NULL); + g_return_if_fail(nick != NULL); + if (g_ascii_strcasecmp(type, "CHAT") != 0) return; + + printformat(NULL, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_CHAT_NOT_FOUND, nick); +} + +static void sig_dcc_list_print(CHAT_DCC_REC *dcc) +{ + if (!IS_DCC_CHAT(dcc)) return; + + printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_LIST_LINE_CHAT, + dcc->id, "CHAT"); +} + +static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item) +{ + CHAT_DCC_REC *dcc; + GHashTable *optlist; + char *text, *target; + void *free_arg; + + g_return_if_fail(data != NULL); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_UNKNOWN_OPTIONS | + PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST, "msg", + &optlist, &target, &text)) + return; + + /* handle only DCC messages */ + if (g_strcmp0(target, "*") == 0) + dcc = item_get_dcc(item); + else if (*target == '=') + dcc = dcc_chat_find_id(target+1); + else + dcc = NULL; + + if (dcc == NULL && *target == '=') { + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + IRCTXT_DCC_CHAT_NOT_FOUND, target+1); + } else if (dcc != NULL) { + if (query_find(NULL, target) == NULL) + completion_last_message_add(target); + + signal_emit("message dcc own", 2, dcc, text); + } + + cmd_params_free(free_arg); +} + +static void cmd_me(const char *data, SERVER_REC *server, WI_ITEM_REC *item) +{ + CHAT_DCC_REC *dcc; + + dcc = item_get_dcc(item); + if (dcc != NULL) + signal_emit("message dcc own_action", 2, dcc, data); +} + +static void cmd_action(const char *data, SERVER_REC *server, WI_ITEM_REC *item) +{ + CHAT_DCC_REC *dcc; + char *target, *text; + void *free_arg; + + g_return_if_fail(data != NULL); + + if (*data != '=') { + /* handle only DCC actions */ + return; + } + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, + &target, &text)) + return; + if (*target == '\0' || *text == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + dcc = dcc_chat_find_id(target+1); + if (dcc == NULL || dcc->sendbuf == NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + IRCTXT_DCC_CHAT_NOT_FOUND, target+1); + } else { + if (query_find(NULL, target) == NULL) + completion_last_message_add(target); + + signal_emit("message dcc own_action", 2, dcc, text); + } + cmd_params_free(free_arg); +} + +static void cmd_ctcp(const char *data, SERVER_REC *server) +{ + CHAT_DCC_REC *dcc; + char *target, *ctcpcmd, *ctcpdata; + void *free_arg; + + g_return_if_fail(data != NULL); + if (server == NULL || !server->connected) + cmd_return_error(CMDERR_NOT_CONNECTED); + + if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST, + &target, &ctcpcmd, &ctcpdata)) + return; + if (*target == '\0' || *ctcpcmd == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + if (*target != '=') { + /* handle only DCC CTCPs */ + cmd_params_free(free_arg); + return; + } + + dcc = dcc_chat_find_id(target+1); + if (dcc == NULL || dcc->sendbuf == NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + IRCTXT_DCC_CHAT_NOT_FOUND, target+1); + } else { + ascii_strup(ctcpcmd); + signal_emit("message dcc own_ctcp", 3, dcc, ctcpcmd, ctcpdata); + } + + cmd_params_free(free_arg); +} + +void fe_dcc_chat_init(void) +{ + fe_dcc_chat_messages_init(); + + signal_add("dcc request", (SIGNAL_FUNC) dcc_request); + signal_add("dcc connected", (SIGNAL_FUNC) dcc_connected); + signal_add("dcc closed", (SIGNAL_FUNC) dcc_closed); + signal_add("dcc chat message", (SIGNAL_FUNC) dcc_chat_msg); + signal_add("dcc ctcp action", (SIGNAL_FUNC) dcc_chat_action); + signal_add("default dcc ctcp", (SIGNAL_FUNC) dcc_chat_ctcp); + signal_add("dcc error ctcp", (SIGNAL_FUNC) dcc_error_ctcp); + signal_add("default ctcp msg dcc", (SIGNAL_FUNC) dcc_unknown_ctcp); + signal_add("default ctcp reply dcc", (SIGNAL_FUNC) dcc_unknown_reply); + signal_add("dcc destroyed", (SIGNAL_FUNC) sig_dcc_destroyed); + signal_add("query destroyed", (SIGNAL_FUNC) sig_query_destroyed); + signal_add("dcc list print", (SIGNAL_FUNC) sig_dcc_list_print); + command_bind("msg", NULL, (SIGNAL_FUNC) cmd_msg); + command_bind("me", NULL, (SIGNAL_FUNC) cmd_me); + command_bind("action", NULL, (SIGNAL_FUNC) cmd_action); + command_bind("ctcp", NULL, (SIGNAL_FUNC) cmd_ctcp); + signal_add("dcc error close not found", (SIGNAL_FUNC) dcc_error_close_not_found); +} + +void fe_dcc_chat_deinit(void) +{ + fe_dcc_chat_messages_deinit(); + + signal_remove("dcc request", (SIGNAL_FUNC) dcc_request); + signal_remove("dcc connected", (SIGNAL_FUNC) dcc_connected); + signal_remove("dcc closed", (SIGNAL_FUNC) dcc_closed); + signal_remove("dcc chat message", (SIGNAL_FUNC) dcc_chat_msg); + signal_remove("dcc ctcp action", (SIGNAL_FUNC) dcc_chat_action); + signal_remove("default dcc ctcp", (SIGNAL_FUNC) dcc_chat_ctcp); + signal_remove("dcc error ctcp", (SIGNAL_FUNC) dcc_error_ctcp); + signal_remove("default ctcp msg dcc", (SIGNAL_FUNC) dcc_unknown_ctcp); + signal_remove("default ctcp reply dcc", (SIGNAL_FUNC) dcc_unknown_reply); + signal_remove("dcc destroyed", (SIGNAL_FUNC) sig_dcc_destroyed); + signal_remove("query destroyed", (SIGNAL_FUNC) sig_query_destroyed); + signal_remove("dcc list print", (SIGNAL_FUNC) sig_dcc_list_print); + command_unbind("msg", (SIGNAL_FUNC) cmd_msg); + command_unbind("me", (SIGNAL_FUNC) cmd_me); + command_unbind("action", (SIGNAL_FUNC) cmd_action); + command_unbind("ctcp", (SIGNAL_FUNC) cmd_ctcp); + signal_remove("dcc error close not found", (SIGNAL_FUNC) dcc_error_close_not_found); +} diff --git a/src/fe-common/irc/dcc/fe-dcc-get.c b/src/fe-common/irc/dcc/fe-dcc-get.c new file mode 100644 index 0000000..e75640b --- /dev/null +++ b/src/fe-common/irc/dcc/fe-dcc-get.c @@ -0,0 +1,150 @@ +/* + fe-dcc-get.c : irssi + + Copyright (C) 1999-2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/signals.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/servers.h> + +#include <irssi/src/irc/core/irc.h> +#include <irssi/src/irc/dcc/dcc-file.h> +#include <irssi/src/irc/dcc/dcc-get.h> + +#include <irssi/src/fe-common/irc/dcc/module-formats.h> +#include <irssi/src/fe-common/core/printtext.h> + +#include <irssi/src/fe-common/irc/dcc/fe-dcc.h> + +static void dcc_request(GET_DCC_REC *dcc) +{ + char *sizestr; + + if (!IS_DCC_GET(dcc)) return; + + sizestr = dcc_get_size_str(dcc->size); + + printformat(dcc->server, NULL, MSGLEVEL_DCC, + server_ischannel(SERVER(dcc->server), dcc->target) ? IRCTXT_DCC_SEND_CHANNEL : + IRCTXT_DCC_SEND, dcc->nick, dcc->addrstr, + dcc->port, dcc->arg, sizestr, dcc->target); + + g_free(sizestr); +} + +static void dcc_connected(GET_DCC_REC *dcc) +{ + if (!IS_DCC_GET(dcc)) return; + + printformat(dcc->server, NULL, MSGLEVEL_DCC, IRCTXT_DCC_GET_CONNECTED, + dcc->arg, dcc->nick, dcc->addrstr, dcc->port); +} + +static void dcc_closed(GET_DCC_REC *dcc) +{ + char *sizestr, timestr[20]; + double kbs; + time_t secs; + + if (!IS_DCC_GET(dcc)) return; + + secs = dcc->starttime == 0 ? -1 : time(NULL)-dcc->starttime; + kbs = (double) (dcc->transfd-dcc->skipped) / + (secs == 0 ? 1 : secs) / 1024.0; + + sizestr = dcc_get_size_str(dcc->transfd); + g_snprintf(timestr, sizeof(timestr), "%02d:%02d:%02d", + (int)(secs/3600), (int)((secs/60)%60), (int)(secs%60)); + + if (secs == -1) { + /* aborted */ + printformat(dcc->server, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_GET_ABORTED, dcc->arg, dcc->nick); + } else { + printformat(dcc->server, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_GET_COMPLETE, dcc->arg, sizestr, + dcc->nick, timestr, kbs); + } + + g_free(sizestr); +} + +static void dcc_error_file_create(GET_DCC_REC *dcc, const char *fname, + const char *error) +{ + printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_CANT_CREATE, + fname, error); +} + + +static void dcc_error_get_not_found(const char *nick) +{ + g_return_if_fail(nick != NULL); + + printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_GET_NOT_FOUND, nick); +} + +static void dcc_error_close_not_found(const char *type, const char *nick, + const char *fname) +{ + g_return_if_fail(type != NULL); + g_return_if_fail(nick != NULL); + g_return_if_fail(fname != NULL); + if (g_ascii_strcasecmp(type, "GET") != 0) return; + + if (fname == NULL || *fname == '\0') fname = "(ANY)"; + printformat(NULL, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_GET_NOT_FOUND, nick, fname); +} + +static void dcc_error_write(GET_DCC_REC *dcc, const char *error) +{ + printformat(NULL, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_GET_WRITE_ERROR, dcc->file, error); +} + +static void sig_dcc_list_print(GET_DCC_REC *dcc) +{ + if (IS_DCC_GET(dcc)) + dcc_list_print_file((FILE_DCC_REC *) dcc); +} + +void fe_dcc_get_init(void) +{ + signal_add("dcc request", (SIGNAL_FUNC) dcc_request); + signal_add("dcc connected", (SIGNAL_FUNC) dcc_connected); + signal_add("dcc closed", (SIGNAL_FUNC) dcc_closed); + signal_add("dcc error file create", (SIGNAL_FUNC) dcc_error_file_create); + signal_add("dcc error get not found", (SIGNAL_FUNC) dcc_error_get_not_found); + signal_add("dcc error close not found", (SIGNAL_FUNC) dcc_error_close_not_found); + signal_add("dcc error write", (SIGNAL_FUNC) dcc_error_write); + signal_add("dcc list print", (SIGNAL_FUNC) sig_dcc_list_print); +} + +void fe_dcc_get_deinit(void) +{ + signal_remove("dcc request", (SIGNAL_FUNC) dcc_request); + signal_remove("dcc connected", (SIGNAL_FUNC) dcc_connected); + signal_remove("dcc closed", (SIGNAL_FUNC) dcc_closed); + signal_remove("dcc error file create", (SIGNAL_FUNC) dcc_error_file_create); + signal_remove("dcc error get not found", (SIGNAL_FUNC) dcc_error_get_not_found); + signal_remove("dcc error close not found", (SIGNAL_FUNC) dcc_error_close_not_found); + signal_remove("dcc error write", (SIGNAL_FUNC) dcc_error_write); + signal_remove("dcc list print", (SIGNAL_FUNC) sig_dcc_list_print); +} diff --git a/src/fe-common/irc/dcc/fe-dcc-send.c b/src/fe-common/irc/dcc/fe-dcc-send.c new file mode 100644 index 0000000..cf9b634 --- /dev/null +++ b/src/fe-common/irc/dcc/fe-dcc-send.c @@ -0,0 +1,186 @@ +/* + fe-dcc-send.c : irssi + + Copyright (C) 1999-2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/signals.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/irc/dcc/dcc-file.h> +#include <irssi/src/irc/dcc/dcc-send.h> +#include <irssi/src/irc/dcc/dcc-queue.h> + +#include <irssi/src/fe-common/irc/dcc/module-formats.h> +#include <irssi/src/fe-common/core/printtext.h> +#include <irssi/src/fe-common/core/completion.h> + +#include <irssi/src/fe-common/irc/dcc/fe-dcc.h> + +static void dcc_connected(SEND_DCC_REC *dcc) +{ + if (!IS_DCC_SEND(dcc)) return; + + printformat(dcc->server, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_SEND_CONNECTED, + dcc->arg, dcc->nick, dcc->addrstr, dcc->port); +} + +static void dcc_closed(SEND_DCC_REC *dcc) +{ + char *sizestr, timestr[20]; + double kbs; + time_t secs; + + if (!IS_DCC_SEND(dcc)) return; + + secs = dcc->starttime == 0 ? -1 : time(NULL)-dcc->starttime; + kbs = (double) (dcc->transfd-dcc->skipped) / + (secs == 0 ? 1 : secs) / 1024.0; + + if (secs == -1) { + /* aborted */ + printformat(dcc->server, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_SEND_ABORTED, + dcc->arg, dcc->nick); + } else { + sizestr = dcc_get_size_str(dcc->transfd); + g_snprintf(timestr, sizeof(timestr), "%02d:%02d:%02d", + (int)(secs/3600), (int)((secs/60)%60), + (int)(secs%60)); + + printformat(dcc->server, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_SEND_COMPLETE, + dcc->arg, sizestr, dcc->nick, timestr, kbs); + + g_free(sizestr); + } +} + +static void dcc_error_file_open(const char *nick, const char *fname, + void *error) +{ + g_return_if_fail(nick != NULL); + g_return_if_fail(fname != NULL); + + printformat(NULL, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_SEND_FILE_OPEN_ERROR, fname, + g_strerror(GPOINTER_TO_INT(error))); +} + +static void dcc_error_send_exists(const char *nick, const char *fname) +{ + g_return_if_fail(nick != NULL); + g_return_if_fail(fname != NULL); + + printformat(NULL, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_SEND_EXISTS, fname, nick); +} + +static void dcc_error_send_no_route(const char *nick, const char *fname) +{ + printformat(NULL, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_SEND_NO_ROUTE, nick, fname); +} + +static void dcc_error_close_not_found(const char *type, const char *nick, + const char *fname) +{ + g_return_if_fail(type != NULL); + g_return_if_fail(nick != NULL); + g_return_if_fail(fname != NULL); + if (g_ascii_strcasecmp(type, "SEND") != 0) return; + + if (fname == NULL || *fname == '\0') fname = "(ANY)"; + printformat(NULL, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_SEND_NOT_FOUND, nick, fname); +} + +static void sig_dcc_send_complete(GList **list, WINDOW_REC *window, + const char *word, const char *line, + int *want_space) +{ + char *path; + + g_return_if_fail(list != NULL); + g_return_if_fail(word != NULL); + g_return_if_fail(line != NULL); + + if (*line == '\0' || strchr(line, ' ') != NULL) + return; + + /* completing filename parameter for /DCC SEND */ + path = convert_home(settings_get_str("dcc_upload_path")); + if (*path == '\0') { + /* use the default path */ + g_free_and_null(path); + } + + *list = filename_complete(word, path); + + if (*list != NULL) { + *want_space = FALSE; + signal_stop(); + } +} + +static void sig_dcc_list_print(SEND_DCC_REC *dcc) +{ + GSList *queue; + + if (!IS_DCC_SEND(dcc)) + return; + + dcc_list_print_file((FILE_DCC_REC *) dcc); + + queue = dcc_queue_get_queue(dcc->queue); + for (; queue != NULL; queue = queue->next) { + DCC_QUEUE_REC *rec = queue->data; + + printformat(NULL, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_LIST_LINE_QUEUED_SEND, rec->nick, + rec->servertag == NULL ? "" : rec->servertag, + rec->file); + } +} + +void fe_dcc_send_init(void) +{ + signal_add("dcc connected", (SIGNAL_FUNC) dcc_connected); + signal_add("dcc closed", (SIGNAL_FUNC) dcc_closed); + signal_add("dcc error file open", (SIGNAL_FUNC) dcc_error_file_open); + signal_add("dcc error send exists", (SIGNAL_FUNC) dcc_error_send_exists); + signal_add("dcc error send no route", (SIGNAL_FUNC) dcc_error_send_no_route); + signal_add("dcc error close not found", (SIGNAL_FUNC) dcc_error_close_not_found); + signal_add("complete command dcc send", (SIGNAL_FUNC) sig_dcc_send_complete); + signal_add("dcc list print", (SIGNAL_FUNC) sig_dcc_list_print); +} + +void fe_dcc_send_deinit(void) +{ + signal_remove("dcc connected", (SIGNAL_FUNC) dcc_connected); + signal_remove("dcc closed", (SIGNAL_FUNC) dcc_closed); + signal_remove("dcc error file open", (SIGNAL_FUNC) dcc_error_file_open); + signal_remove("dcc error send exists", (SIGNAL_FUNC) dcc_error_send_exists); + signal_remove("dcc error send no route", (SIGNAL_FUNC) dcc_error_send_no_route); + signal_remove("dcc error close not found", (SIGNAL_FUNC) dcc_error_close_not_found); + signal_remove("complete command dcc send", (SIGNAL_FUNC) sig_dcc_send_complete); + signal_remove("dcc list print", (SIGNAL_FUNC) sig_dcc_list_print); +} diff --git a/src/fe-common/irc/dcc/fe-dcc-server.c b/src/fe-common/irc/dcc/fe-dcc-server.c new file mode 100644 index 0000000..eaab963 --- /dev/null +++ b/src/fe-common/irc/dcc/fe-dcc-server.c @@ -0,0 +1,83 @@ +/* + fe-dcc-server.c : irssi + + Copyright (C) 2003 Mark Trumbull + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/network.h> +#include <irssi/src/core/levels.h> + +#include <irssi/src/irc/dcc/dcc-server.h> + +#include <irssi/src/fe-common/irc/dcc/module-formats.h> +#include <irssi/src/fe-common/core/printtext.h> +#include <irssi/src/fe-common/core/themes.h> + +static void dcc_server_started(SERVER_DCC_REC *dcc) +{ + if (!IS_DCC_SERVER(dcc)) { + return; + } + + printformat(dcc->server, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_SERVER_STARTED, dcc->port); +} + +static void dcc_closed(SERVER_DCC_REC *dcc) +{ + /* We don't want to print a msg if its just starting a chat/get */ + /* and getting rid of the leftover SERVER_DCC_REC */ + if (!IS_DCC_SERVER(dcc) || dcc->connection_established) { + return; + } + + printformat(dcc->server, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_SERVER_CLOSED, dcc->port); +} + +static void sig_dcc_list_print(SERVER_DCC_REC *dcc) +{ + /* We don't want to print a msg if its just starting a chat/get */ + /* and getting rid of the leftover SERVER_DCC_REC */ + if (!IS_DCC_SERVER(dcc) || dcc->connection_established) { + return; + } + + /* SERVER: Port(59) - Send(on) - Chat(on) - Fserve(on) */ + printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_LIST_LINE_SERVER, + "SERVER", dcc->port, dcc->accept_send ? "on" : "off", + dcc->accept_chat ? "on" : "off", + dcc->accept_fserve ? "on" : "off"); +} + +void fe_dcc_server_init(void) +{ + signal_add("dcc server started", (SIGNAL_FUNC) dcc_server_started); + signal_add("dcc closed", (SIGNAL_FUNC) dcc_closed); + signal_add("dcc list print", (SIGNAL_FUNC) sig_dcc_list_print); +} + +void fe_dcc_server_deinit(void) +{ + signal_remove("dcc server started", (SIGNAL_FUNC) dcc_server_started); + signal_remove("dcc closed", (SIGNAL_FUNC) dcc_closed); + signal_remove("dcc list print", (SIGNAL_FUNC) sig_dcc_list_print); +} + diff --git a/src/fe-common/irc/dcc/fe-dcc.c b/src/fe-common/irc/dcc/fe-dcc.c new file mode 100644 index 0000000..f5fb3b2 --- /dev/null +++ b/src/fe-common/irc/dcc/fe-dcc.c @@ -0,0 +1,195 @@ +/* + fe-dcc.c : irssi + + Copyright (C) 1999-2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/network.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/irc/dcc/dcc-chat.h> +#include <irssi/src/irc/dcc/dcc-file.h> +#include <irssi/src/irc/dcc/dcc-get.h> +#include <irssi/src/irc/dcc/dcc-send.h> + +#include <irssi/src/fe-common/irc/dcc/module-formats.h> +#include <irssi/src/fe-common/core/printtext.h> +#include <irssi/src/fe-common/core/themes.h> + +void fe_dcc_chat_init(void); +void fe_dcc_chat_deinit(void); + +void fe_dcc_get_init(void); +void fe_dcc_get_deinit(void); + +void fe_dcc_send_init(void); +void fe_dcc_send_deinit(void); + +void fe_dcc_server_init(void); +void fe_dcc_server_deinit(void); + +char *dcc_get_size_str(uoff_t size) +{ + if (size < 1024) + return g_strdup_printf("%"PRIuUOFF_T"B", size); + if (size < 1024*1024) + return g_strdup_printf("%"PRIuUOFF_T"kB", (size+1023) / 1024); + return g_strdup_printf("%"PRIuUOFF_T"MB", size / (1024*1024)); +} + +static void dcc_request(DCC_REC *dcc) +{ + char *service; + + g_return_if_fail(dcc != NULL); + + if (dcc->port < 1024) { + /* warn about connecting to lowports */ + service = net_getservbyport(dcc->port); + + printformat(dcc->server, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_LOWPORT, dcc->port, + service != NULL ? service : "unknown"); + } +} + +static void dcc_rejected(DCC_REC *dcc) +{ + g_return_if_fail(dcc != NULL); + + printformat(dcc->server, NULL, MSGLEVEL_DCC, IRCTXT_DCC_CLOSE, + dcc_type2str(dcc->type), dcc->nick, dcc->arg); +} + +static void dcc_request_send(DCC_REC *dcc) +{ + g_return_if_fail(dcc != NULL); + + printformat(dcc->server, NULL, MSGLEVEL_DCC, IRCTXT_DCC_REQUEST_SEND, + dcc_type2str(dcc->type), dcc->nick, dcc->arg); +} + +static void dcc_error_connect(DCC_REC *dcc) +{ + g_return_if_fail(dcc != NULL); + + printformat(dcc->server, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_CONNECT_ERROR, dcc->addrstr, dcc->port); +} + +static void dcc_error_unknown_type(const char *type) +{ + g_return_if_fail(type != NULL); + + printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_UNKNOWN_TYPE, type); +} + +void dcc_list_print_file(FILE_DCC_REC *dcc) +{ + time_t going, eta; + char *transfd_str, *size_str, etastr[20]; + uoff_t bps; + + going = time(NULL) - dcc->starttime; + if (going <= 0) going = 1; + + transfd_str = dcc_get_size_str(dcc->transfd); + size_str = dcc_get_size_str(dcc->size); + + bps = (dcc->transfd-dcc->skipped) / going; + if (bps == 0) { + strcpy(etastr, "(stalled)"); + } else { + eta = (dcc->size - dcc->transfd) / bps; + g_snprintf(etastr, sizeof(etastr), "%02d:%02d:%02d", + (int)(eta/3600), (int)((eta/60)%60), (int)(eta%60)); + } + + printformat(NULL, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_LIST_LINE_FILE, + dcc->nick, dcc_type2str(dcc->type), + transfd_str, size_str, + dcc->size == 0 ? 0 : (int)((double)dcc->transfd/(double)dcc->size*100.0), + (double)bps/1024.0, dcc->arg, etastr); + + g_free(transfd_str); + g_free(size_str); +} + +static void cmd_dcc_list(const char *data) +{ + GSList *tmp; + + g_return_if_fail(data != NULL); + + printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_LIST_HEADER); + for (tmp = dcc_conns; tmp != NULL; tmp = tmp->next) + signal_emit("dcc list print", 1, tmp->data); + printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_LIST_FOOTER); +} + +static void cmd_dcc(const char *data) +{ + if (*data == '\0') { + cmd_dcc_list(data); + signal_stop(); + } +} + +void fe_irc_dcc_init(void) +{ + fe_dcc_chat_init(); + fe_dcc_get_init(); + fe_dcc_send_init(); + fe_dcc_server_init(); + + signal_add("dcc request", (SIGNAL_FUNC) dcc_request); + signal_add("dcc rejected", (SIGNAL_FUNC) dcc_rejected); + signal_add("dcc request send", (SIGNAL_FUNC) dcc_request_send); + signal_add("dcc error connect", (SIGNAL_FUNC) dcc_error_connect); + signal_add("dcc error unknown type", (SIGNAL_FUNC) dcc_error_unknown_type); + command_bind("dcc", NULL, (SIGNAL_FUNC) cmd_dcc); + command_bind("dcc list", NULL, (SIGNAL_FUNC) cmd_dcc_list); + + theme_register(fecommon_irc_dcc_formats); + settings_check(); + module_register("dcc", "fe-irc"); +} + +void fe_irc_dcc_deinit(void) +{ + fe_dcc_chat_deinit(); + fe_dcc_get_deinit(); + fe_dcc_send_deinit(); + fe_dcc_server_deinit(); + + theme_unregister(); + + signal_remove("dcc request", (SIGNAL_FUNC) dcc_request); + signal_remove("dcc rejected", (SIGNAL_FUNC) dcc_rejected); + signal_remove("dcc request send", (SIGNAL_FUNC) dcc_request_send); + signal_remove("dcc error connect", (SIGNAL_FUNC) dcc_error_connect); + signal_remove("dcc error unknown type", (SIGNAL_FUNC) dcc_error_unknown_type); + command_unbind("dcc", (SIGNAL_FUNC) cmd_dcc); + command_unbind("dcc list", (SIGNAL_FUNC) cmd_dcc_list); +} + +MODULE_ABICHECK(fe_irc_dcc) diff --git a/src/fe-common/irc/dcc/fe-dcc.h b/src/fe-common/irc/dcc/fe-dcc.h new file mode 100644 index 0000000..4d79296 --- /dev/null +++ b/src/fe-common/irc/dcc/fe-dcc.h @@ -0,0 +1,7 @@ +#ifndef IRSSI_FE_COMMON_IRC_DCC_FE_DCC_H +#define IRSSI_FE_COMMON_IRC_DCC_FE_DCC_H + +char *dcc_get_size_str(uoff_t size); +void dcc_list_print_file(FILE_DCC_REC *dcc); + +#endif diff --git a/src/fe-common/irc/dcc/meson.build b/src/fe-common/irc/dcc/meson.build new file mode 100644 index 0000000..296b1d6 --- /dev/null +++ b/src/fe-common/irc/dcc/meson.build @@ -0,0 +1,27 @@ +# this file is part of irssi + +libfe_irc_dcc_a = static_library('fe_irc_dcc', + files( + 'fe-dcc-chat-messages.c', + 'fe-dcc-chat.c', + 'fe-dcc-get.c', + 'fe-dcc-send.c', + 'fe-dcc-server.c', + 'fe-dcc.c', + 'module-formats.c', + ), + include_directories : rootinc, + implicit_include_directories : false, + c_args : [ + def_helpdir, + def_sysconfdir, + ], + dependencies : dep) + +install_headers( + files( + 'fe-dcc.h', + 'module-formats.h', + 'module.h', + ), + subdir : incdir / 'src' / 'fe-common' / 'irc' / 'dcc') diff --git a/src/fe-common/irc/dcc/module-formats.c b/src/fe-common/irc/dcc/module-formats.c new file mode 100644 index 0000000..f973cd9 --- /dev/null +++ b/src/fe-common/irc/dcc/module-formats.c @@ -0,0 +1,79 @@ +/* + module-formats.c : irssi + + Copyright (C) 2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/core/formats.h> + +FORMAT_REC fecommon_irc_dcc_formats[] = { + { MODULE_NAME, "IRC", 0 }, + + /* ---- */ + { NULL, "DCC", 0 }, + + { "own_dcc", "{dccownmsg dcc {dccownnick $1}}$2", 3, { 0, 0, 0 } }, + { "own_dcc_action", "{dccownaction_target $0 $1}$2", 3, { 0, 0, 0 } }, + { "own_dcc_action_query", "{dccownaction $0}$2", 3, { 0, 0, 0 } }, + { "own_dcc_ctcp", "{ownctcp ctcp $0}$1 $2", 3, { 0, 0, 0 } }, + { "dcc_msg", "{dccmsg dcc $0}$1", 2, { 0, 0 } }, + { "action_dcc", "{dccaction $0}$1", 2, { 0, 0 } }, + { "action_dcc_query", "{dccaction $0}$1", 2, { 0, 0 } }, + { "own_dcc_query", "{ownmsgnick {dccownquerynick $0}}$2", 3, { 0, 0, 0 } }, + { "dcc_msg_query", "{privmsgnick $0}$1", 2, { 0, 0 } }, + { "dcc_ctcp", "{dcc >>> DCC CTCP {hilight $1} received from {hilight $0}: $2}", 3, { 0, 0, 0 } }, + { "dcc_chat", "{dcc DCC CHAT from {nick $0} [$1 port $2]}", 3, { 0, 0, 1 } }, + { "dcc_chat_channel", "{dcc DCC CHAT from {nick $0} [$1 port $2] requested in channel {channel $3}}", 4, { 0, 0, 1, 0 } }, + { "dcc_chat_not_found", "{dcc No DCC CHAT connection open to {nick $0}}", 1, { 0 } }, + { "dcc_chat_connected", "{dcc DCC CHAT connection with {nick $0} [$1 port $2] established}", 3, { 0, 0, 1 } }, + { "dcc_chat_disconnected", "{dcc DCC lost chat to {nick $0}}", 1, { 0 } }, + { "dcc_send", "{dcc DCC SEND from {nick $0} [$1 port $2]: $3 [$4]}", 5, { 0, 0, 1, 0, 0 } }, + { "dcc_send_channel", "{dcc DCC SEND from {nick $0} [$1 port $2]: $3 [$4 bytes] requested in channel {channel $5}}", 6, { 0, 0, 1, 0, 0, 0 } }, + { "dcc_send_exists", "{dcc DCC already sending file {dccfile $0} for {nick $1}}", 2, { 0, 0 } }, + { "dcc_send_no_route", "{dcc DCC route lost to nick {nick $0} when trying to send file {dccfile $1}}", 2, { 0, 0 } }, + { "dcc_send_not_found", "{dcc DCC not sending file {dccfile $1} to {nick $0}}", 2, { 0, 0 } }, + { "dcc_send_file_open_error", "{dcc DCC can't open file {dccfile $0}: $1}", 2, { 0, 0 } }, + { "dcc_send_connected", "{dcc DCC sending file {dccfile $0} for {nick $1} [$2 port $3]}", 4, { 0, 0, 0, 1 } }, + { "dcc_send_complete", "{dcc DCC sent file {dccfile $0} [{hilight $1}] for {nick $2} in {hilight $3} [{hilight $4kB/s}]}", 5, { 0, 0, 0, 0, 3 } }, + { "dcc_send_aborted", "{dcc DCC aborted sending file {dccfile $0} for {nick $1}}", 2, { 0, 0 } }, + { "dcc_get_not_found", "{dcc DCC no file offered by {nick $0}}", 1, { 0 } }, + { "dcc_get_connected", "{dcc DCC receiving file {dccfile $0} from {nick $1} [$2 port $3]}", 4, { 0, 0, 0, 1 } }, + { "dcc_get_complete", "{dcc DCC received file {dccfile $0} [$1] from {nick $2} in {hilight $3} [$4kB/s]}", 5, { 0, 0, 0, 0, 3 } }, + { "dcc_get_aborted", "{dcc DCC aborted receiving file {dccfile $0} from {nick $1}}", 2, { 0, 0 } }, + { "dcc_get_write_error", "{dcc DCC error writing to file {dccfile $0}: {comment $1}", 2, { 0, 0 } }, + { "dcc_unknown_ctcp", "{dcc DCC unknown ctcp {hilight $0} from {nick $1} [$2]}", 3, { 0, 0, 0 } }, + { "dcc_unknown_reply", "{dcc DCC unknown reply {hilight $0} from {nick $1} [$2]}", 3, { 0, 0, 0 } }, + { "dcc_unknown_type", "{dcc DCC unknown type {hilight $0}}", 1, { 0 } }, + { "dcc_invalid_ctcp", "{dcc DCC received CTCP {hilight $0} with invalid parameters from {nick $1}}", 4, { 0, 0, 0, 0 } }, + { "dcc_connect_error", "{dcc DCC can't connect to {hilight $0} port {hilight $1}}", 2, { 0, 1 } }, + { "dcc_cant_create", "{dcc DCC can't create file {dccfile $0}: $1}", 2, { 0, 0 } }, + { "dcc_rejected", "{dcc DCC $0 was rejected by {nick $1} [{hilight $2}]}", 3, { 0, 0, 0 } }, + { "dcc_request_send", "{dcc DCC $0 request sent to {nick $1}: $2", 3, { 0, 0, 0 } }, + { "dcc_close", "{dcc DCC $0 close for {nick $1} [{hilight $2}]}", 3, { 0, 0, 0 } }, + { "dcc_lowport", "{dcc Warning: Port sent with DCC request is a lowport ({hilight $0, $1}) - this isn't normal. It is possible the address/port is faked (or maybe someone is just trying to bypass firewall)}", 2, { 1, 0 } }, + { "dcc_list_header", "{dcc DCC connections}", 0 }, + { "dcc_list_line_chat", "{dcc $0 $1}", 2, { 0, 0 } }, + { "dcc_list_line_file", "{dcc $0 $1: %|$2 of $3 ($4%%) - $5kB/s - ETA $7 - $6}", 8, { 0, 0, 0, 0, 1, 3, 0, 0 } }, + { "dcc_list_line_queued_send", "{dcc - $0 $2 (queued)}", 3, { 0, 0, 0 } }, + { "dcc_list_footer", "", 0 }, + { "dcc_list_line_server", "{dcc $0: Port($1) - Send($2) - Chat($3) - Fserve($4)}", 5, { 0, 1, 0, 0, 0 } }, + { "dcc_server_started", "{dcc DCC SERVER started on port {hilight $0}}", 1, { 1 } }, + { "dcc_server_closed", "{dcc DCC SERVER on port {hilight $0} closed}", 1, { 1 } }, + + { NULL, NULL, 0 } +}; diff --git a/src/fe-common/irc/dcc/module-formats.h b/src/fe-common/irc/dcc/module-formats.h new file mode 100644 index 0000000..204c7dd --- /dev/null +++ b/src/fe-common/irc/dcc/module-formats.h @@ -0,0 +1,57 @@ +#include <irssi/src/fe-common/core/formats.h> + +enum { + IRCTXT_MODULE_NAME, + + IRCTXT_FILL_1, + + IRCTXT_OWN_DCC, + IRCTXT_OWN_DCC_ACTION, + IRCTXT_OWN_DCC_ACTION_QUERY, + IRCTXT_OWN_DCC_CTCP, + IRCTXT_DCC_MSG, + IRCTXT_ACTION_DCC, + IRCTXT_ACTION_DCC_QUERY, + IRCTXT_OWN_DCC_QUERY, + IRCTXT_DCC_MSG_QUERY, + IRCTXT_DCC_CTCP, + IRCTXT_DCC_CHAT, + IRCTXT_DCC_CHAT_CHANNEL, + IRCTXT_DCC_CHAT_NOT_FOUND, + IRCTXT_DCC_CHAT_CONNECTED, + IRCTXT_DCC_CHAT_DISCONNECTED, + IRCTXT_DCC_SEND, + IRCTXT_DCC_SEND_CHANNEL, + IRCTXT_DCC_SEND_EXISTS, + IRCTXT_DCC_SEND_NO_ROUTE, + IRCTXT_DCC_SEND_NOT_FOUND, + IRCTXT_DCC_SEND_FILE_OPEN_ERROR, + IRCTXT_DCC_SEND_CONNECTED, + IRCTXT_DCC_SEND_COMPLETE, + IRCTXT_DCC_SEND_ABORTED, + IRCTXT_DCC_GET_NOT_FOUND, + IRCTXT_DCC_GET_CONNECTED, + IRCTXT_DCC_GET_COMPLETE, + IRCTXT_DCC_GET_ABORTED, + IRCTXT_DCC_GET_WRITE_ERROR, + IRCTXT_DCC_UNKNOWN_CTCP, + IRCTXT_DCC_UNKNOWN_REPLY, + IRCTXT_DCC_UNKNOWN_TYPE, + IRCTXT_DCC_INVALID_CTCP, + IRCTXT_DCC_CONNECT_ERROR, + IRCTXT_DCC_CANT_CREATE, + IRCTXT_DCC_REJECTED, + IRCTXT_DCC_REQUEST_SEND, + IRCTXT_DCC_CLOSE, + IRCTXT_DCC_LOWPORT, + IRCTXT_DCC_LIST_HEADER, + IRCTXT_DCC_LIST_LINE_CHAT, + IRCTXT_DCC_LIST_LINE_FILE, + IRCTXT_DCC_LIST_LINE_QUEUED_SEND, + IRCTXT_DCC_LIST_FOOTER, + IRCTXT_DCC_LIST_LINE_SERVER, + IRCTXT_DCC_SERVER_STARTED, + IRCTXT_DCC_SERVER_CLOSED +}; + +extern FORMAT_REC fecommon_irc_dcc_formats[]; diff --git a/src/fe-common/irc/dcc/module.h b/src/fe-common/irc/dcc/module.h new file mode 100644 index 0000000..5493446 --- /dev/null +++ b/src/fe-common/irc/dcc/module.h @@ -0,0 +1,4 @@ +#include <irssi/src/common.h> +#include <irssi/src/irc/core/irc.h> + +#define MODULE_NAME "fe-common/irc/dcc" diff --git a/src/fe-common/irc/fe-cap.c b/src/fe-common/irc/fe-cap.c new file mode 100644 index 0000000..a75f2bb --- /dev/null +++ b/src/fe-common/irc/fe-cap.c @@ -0,0 +1,84 @@ +/* + fe-cap.c : irssi + + Copyright (C) 2018 dequis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/irc/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/misc.h> + +#include <irssi/src/irc/core/irc-servers.h> + +#include <irssi/src/fe-common/core/printtext.h> + +static const struct { + const char *command; + const int template; +} fe_cap_messages[] = { + {"LS", IRCTXT_CAP_LS}, + {"ACK", IRCTXT_CAP_ACK}, + {"NAK", IRCTXT_CAP_NAK}, + {"LIST", IRCTXT_CAP_LIST}, + {"NEW", IRCTXT_CAP_NEW}, + {"DEL", IRCTXT_CAP_DEL}, +}; + +static void event_cap(IRC_SERVER_REC *server, char *args, char *nick, char *address) +{ + int i; + char *params, *evt, *list, *star; + + params = event_get_params(args, 4, NULL, &evt, &star, &list); + + if (params == NULL) { + return; + } + + /* With multiline CAP LS, if the '*' parameter isn't present, + * adjust the parameter pointer to compensate for this */ + if (strcmp(star, "*") != 0 && list[0] == '\0') { + list = star; + } + + for (i = 0; i < G_N_ELEMENTS(fe_cap_messages); i++) { + if (!g_ascii_strcasecmp(evt, fe_cap_messages[i].command)) { + printformat(server, NULL, MSGLEVEL_CRAP, fe_cap_messages[i].template, list); + } + } + + g_free(params); +} + +static void sig_server_cap_req(IRC_SERVER_REC *server, char *caps) +{ + printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_CAP_REQ, caps); +} + +void fe_cap_init(void) +{ + signal_add("event cap", (SIGNAL_FUNC) event_cap); + signal_add("server cap req", (SIGNAL_FUNC) sig_server_cap_req); +} + +void fe_cap_deinit(void) +{ + signal_remove("event cap", (SIGNAL_FUNC) event_cap); + signal_remove("server cap req", (SIGNAL_FUNC) sig_server_cap_req); +} diff --git a/src/fe-common/irc/fe-common-irc.c b/src/fe-common/irc/fe-common-irc.c new file mode 100644 index 0000000..a36d3ca --- /dev/null +++ b/src/fe-common/irc/fe-common-irc.c @@ -0,0 +1,134 @@ +/* + fe-common-irc.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/modules.h> +#include <irssi/src/fe-common/irc/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/lib-config/iconfig.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/fe-common/core/themes.h> +#include <irssi/src/fe-common/irc/fe-irc-server.h> +#include <irssi/src/fe-common/irc/fe-irc-channels.h> + +void fe_irc_modules_init(void); +void fe_irc_modules_deinit(void); + +void fe_irc_queries_init(void); +void fe_irc_queries_deinit(void); + +void fe_irc_layout_init(void); +void fe_irc_layout_deinit(void); + +void fe_irc_messages_init(void); +void fe_irc_messages_deinit(void); + +void fe_irc_commands_init(void); +void fe_irc_commands_deinit(void); + +void fe_ircnet_init(void); +void fe_ircnet_deinit(void); + +void fe_ctcp_init(void); +void fe_ctcp_deinit(void); + +void fe_events_init(void); +void fe_events_deinit(void); + +void fe_events_numeric_init(void); +void fe_events_numeric_deinit(void); + +void fe_modes_init(void); +void fe_modes_deinit(void); + +void fe_netsplit_init(void); +void fe_netsplit_deinit(void); + +void fe_netjoin_init(void); +void fe_netjoin_deinit(void); + +void fe_whois_init(void); +void fe_whois_deinit(void); + +void fe_sasl_init(void); +void fe_sasl_deinit(void); + +void fe_cap_init(void); +void fe_cap_deinit(void); + +void irc_completion_init(void); +void irc_completion_deinit(void); + +void fe_common_irc_init(void) +{ + settings_add_bool("lookandfeel", "show_away_once", TRUE); + + theme_register(fecommon_irc_formats); + + fe_irc_channels_init(); + fe_irc_queries_init(); + fe_irc_messages_init(); + fe_irc_commands_init(); + fe_ircnet_init(); + fe_irc_server_init(); + fe_ctcp_init(); + fe_events_init(); + fe_events_numeric_init(); + fe_modes_init(); + fe_netsplit_init(); + fe_netjoin_init(); + fe_whois_init(); + fe_sasl_init(); + fe_cap_init(); + irc_completion_init(); + + settings_check(); + module_register("irc", "fe-common"); + + fe_irc_modules_init(); +} + +void fe_common_irc_deinit(void) +{ + fe_irc_modules_deinit(); + + fe_irc_channels_deinit(); + fe_irc_queries_deinit(); + fe_irc_messages_deinit(); + fe_irc_commands_deinit(); + fe_ircnet_deinit(); + fe_irc_server_deinit(); + fe_ctcp_deinit(); + fe_events_deinit(); + fe_events_numeric_deinit(); + fe_modes_deinit(); + fe_netsplit_deinit(); + fe_netjoin_deinit(); + fe_whois_deinit(); + fe_sasl_deinit(); + fe_cap_deinit(); + irc_completion_deinit(); + + theme_unregister(); +} + +MODULE_ABICHECK(fe_common_irc) diff --git a/src/fe-common/irc/fe-ctcp.c b/src/fe-common/irc/fe-ctcp.c new file mode 100644 index 0000000..1489396 --- /dev/null +++ b/src/fe-common/irc/fe-ctcp.c @@ -0,0 +1,170 @@ +/* + fe-ctcp.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/irc/module-formats.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/core/levels.h> +#include <irssi/src/core/servers.h> +#include <irssi/src/core/channels.h> +#include <irssi/src/core/queries.h> +#include <irssi/src/core/ignore.h> + +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/core/window-items.h> +#include <irssi/src/fe-common/core/printtext.h> + +static void ctcp_default_msg(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr, + const char *target) +{ + const char *p; + char *cmd; + + p = strchr(data, ' '); + if (p == NULL) { + cmd = g_strdup(data); + data = ""; + } else { + cmd = g_strndup(data, (int) (p-data)); + data = p+1; + } + + printformat(server, server_ischannel(SERVER(server), target) ? target : nick, MSGLEVEL_CTCPS, + IRCTXT_CTCP_REQUESTED_UNKNOWN, + nick, addr, cmd, data, target); + g_free(cmd); +} + +static void ctcp_ping_msg(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr, + const char *target) +{ + signal_emit("message irc ctcp", 6, server, "PING", + data, nick, addr, target); +} + +static void ctcp_version_msg(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr, + const char *target) +{ + signal_emit("message irc ctcp", 6, server, "VERSION", + data, nick, addr, target); +} + +static void ctcp_time_msg(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr, + const char *target) +{ + signal_emit("message irc ctcp", 6, server, "TIME", + data, nick, addr, target); +} + +static void ctcp_userinfo_msg(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr, + const char *target) +{ + signal_emit("message irc ctcp", 6, server, "USERINFO", + data, nick, addr, target); +} + +static void ctcp_clientinfo_msg(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr, + const char *target) +{ + signal_emit("message irc ctcp", 6, server, "CLIENTINFO", + data, nick, addr, target); +} + +static void ctcp_default_reply(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr, + const char *target) +{ + const char *ctcpdata; + char *ctcp, *ptr; + + g_return_if_fail(data != NULL); + + ctcp = g_strdup(data); + ptr = strchr(ctcp, ' '); + if (ptr == NULL) + ctcpdata = ""; + else { + *ptr = '\0'; + ctcpdata = ptr+1; + } + + printformat(server, server_ischannel(SERVER(server), target) ? target : nick, MSGLEVEL_CTCPS, + server_ischannel(SERVER(server), target) ? IRCTXT_CTCP_REPLY_CHANNEL : + IRCTXT_CTCP_REPLY, ctcp, nick, ctcpdata, target); + g_free(ctcp); +} + +static void ctcp_ping_reply(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr, + const char *target) +{ + gint64 tv, tv2; + long usecs; + + g_return_if_fail(data != NULL); + + if (sscanf(data, + "%" G_GINT64_FORMAT " " + "%" G_GINT64_FORMAT, + &tv, &tv2) < 1) { + char *tmp = g_strconcat("PING ", data, NULL); + ctcp_default_reply(server, tmp, nick, addr, target); + g_free(tmp); + return; + } + + tv2 += tv * G_TIME_SPAN_SECOND; + tv = g_get_real_time(); + usecs = tv - tv2; + printformat(server, server_ischannel(SERVER(server), target) ? target : nick, MSGLEVEL_CTCPS, + IRCTXT_CTCP_PING_REPLY, nick, usecs / G_TIME_SPAN_SECOND, usecs % G_TIME_SPAN_SECOND); +} + +void fe_ctcp_init(void) +{ + signal_add("default ctcp msg", (SIGNAL_FUNC) ctcp_default_msg); + signal_add("ctcp msg ping", (SIGNAL_FUNC) ctcp_ping_msg); + signal_add("ctcp msg version", (SIGNAL_FUNC) ctcp_version_msg); + signal_add("ctcp msg time", (SIGNAL_FUNC) ctcp_time_msg); + signal_add("ctcp msg userinfo", (SIGNAL_FUNC) ctcp_userinfo_msg); + signal_add("ctcp msg clientinfo", (SIGNAL_FUNC) ctcp_clientinfo_msg); + signal_add("default ctcp reply", (SIGNAL_FUNC) ctcp_default_reply); + signal_add("ctcp reply ping", (SIGNAL_FUNC) ctcp_ping_reply); +} + +void fe_ctcp_deinit(void) +{ + signal_remove("default ctcp msg", (SIGNAL_FUNC) ctcp_default_msg); + signal_remove("ctcp msg ping", (SIGNAL_FUNC) ctcp_ping_msg); + signal_remove("ctcp msg version", (SIGNAL_FUNC) ctcp_version_msg); + signal_remove("ctcp msg time", (SIGNAL_FUNC) ctcp_time_msg); + signal_remove("ctcp msg userinfo", (SIGNAL_FUNC) ctcp_userinfo_msg); + signal_remove("ctcp msg clientinfo", (SIGNAL_FUNC) ctcp_clientinfo_msg); + signal_remove("default ctcp reply", (SIGNAL_FUNC) ctcp_default_reply); + signal_remove("ctcp reply ping", (SIGNAL_FUNC) ctcp_ping_reply); +} diff --git a/src/fe-common/irc/fe-events-numeric.c b/src/fe-common/irc/fe-events-numeric.c new file mode 100644 index 0000000..34ba3fe --- /dev/null +++ b/src/fe-common/irc/fe-events-numeric.c @@ -0,0 +1,903 @@ +/* + fe-events-numeric.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/irc/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/settings.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/recode.h> + +#include <irssi/src/irc/core/irc-servers.h> +#include <irssi/src/irc/core/irc-channels.h> +#include <irssi/src/core/nicklist.h> +#include <irssi/src/irc/core/mode-lists.h> + +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/fe-common/core/printtext.h> +#include <irssi/src/fe-common/core/fe-channels.h> +#include <irssi/src/fe-common/irc/fe-irc-server.h> + +static void print_event_received(IRC_SERVER_REC *server, const char *data, + const char *nick, int target_param); + +static char *last_away_nick = NULL; +static char *last_away_msg = NULL; + +static void event_user_mode(IRC_SERVER_REC *server, const char *data) +{ + char *params, *mode; + + g_return_if_fail(data != NULL); + g_return_if_fail(server != NULL); + + params = event_get_params(data, 2, NULL, &mode); + printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_USER_MODE, + g_strchomp(mode)); + g_free(params); +} + +static void event_ison(IRC_SERVER_REC *server, const char *data) +{ + char *params, *online; + + g_return_if_fail(data != NULL); + g_return_if_fail(server != NULL); + + params = event_get_params(data, 2, NULL, &online); + printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_ONLINE, online); + g_free(params); +} + +static void event_names_list(IRC_SERVER_REC *server, const char *data) +{ + IRC_CHANNEL_REC *chanrec; + char *params, *channel, *names; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 4, NULL, NULL, &channel, &names); + + chanrec = irc_channel_find(server, channel); + if (chanrec == NULL || chanrec->names_got) { + printformat_module("fe-common/core", server, channel, + MSGLEVEL_CRAP, TXT_NAMES, + channel, 0, 0, 0, 0, 0); + printtext(server, channel, MSGLEVEL_CRAP, "%s", names); + + } + g_free(params); +} + +static void event_end_of_names(IRC_SERVER_REC *server, const char *data, + const char *nick) +{ + IRC_CHANNEL_REC *chanrec; + char *params, *channel; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2, NULL, &channel); + + chanrec = irc_channel_find(server, channel); + if (chanrec == NULL || chanrec->names_got) + print_event_received(server, data, nick, FALSE); + g_free(params); +} + +static void event_who(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick, *channel, *user, *host, *stat, *realname, *hops; + char *serv, *recoded; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 8, NULL, &channel, &user, + &host, &serv, &nick, &stat, &realname); + + /* split hops/realname */ + hops = realname; + while (*realname != '\0' && *realname != ' ') realname++; + if (*realname == ' ') + *realname++ = '\0'; + + recoded = recode_in(SERVER(server), realname, nick); + printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_WHO, + channel, nick, stat, hops, user, host, recoded, serv); + + g_free(params); + g_free(recoded); +} + +static void event_end_of_who(IRC_SERVER_REC *server, const char *data) +{ + char *params, *channel; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2, NULL, &channel); + printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_END_OF_WHO, channel); + g_free(params); +} + +static void event_ban_list(IRC_SERVER_REC *server, const char *data) +{ + IRC_CHANNEL_REC *chanrec; + BAN_REC *banrec; + const char *channel; + char *params, *ban, *setby, *tims; + long secs; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 5, NULL, &channel, + &ban, &setby, &tims); + secs = *tims == '\0' ? 0 : + (long) (time(NULL) - atol(tims)); + + chanrec = irc_channel_find(server, channel); + banrec = chanrec == NULL ? NULL : banlist_find(chanrec->banlist, ban); + + channel = get_visible_target(server, channel); + printformat(server, channel, MSGLEVEL_CRAP, + *setby == '\0' ? IRCTXT_BANLIST : IRCTXT_BANLIST_LONG, + banrec == NULL ? 0 : g_slist_index(chanrec->banlist, banrec)+1, + channel, ban, setby, secs); + + g_free(params); +} + +static void event_eban_list(IRC_SERVER_REC *server, const char *data) +{ + const char *channel; + char *params, *ban, *setby, *tims; + long secs; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 5, NULL, &channel, + &ban, &setby, &tims); + secs = *tims == '\0' ? 0 : + (long) (time(NULL) - atol(tims)); + + channel = get_visible_target(server, channel); + printformat(server, channel, MSGLEVEL_CRAP, + *setby == '\0' ? IRCTXT_EBANLIST : IRCTXT_EBANLIST_LONG, + channel, ban, setby, secs); + + g_free(params); +} + +static void event_silence_list(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick, *mask; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 3, NULL, &nick, &mask); + printformat(server, NULL, MSGLEVEL_CRAP, + IRCTXT_SILENCE_LINE, nick, mask); + g_free(params); +} + +static void event_accept_list(IRC_SERVER_REC *server, const char *data) +{ + char *params, *accepted; + + g_return_if_fail(data != NULL); + g_return_if_fail(server != NULL); + + params = event_get_params(data, 2 | PARAM_FLAG_GETREST, + NULL, &accepted); + printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_ACCEPT_LIST, accepted); + g_free(params); +} + +static void event_invite_list(IRC_SERVER_REC *server, const char *data) +{ + const char *channel; + char *params, *invite, *setby, *tims; + long secs; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 5, NULL, &channel, &invite, + &setby, &tims); + secs = *tims == '\0' ? 0 : + (long) (time(NULL) - atol(tims)); + + channel = get_visible_target(server, channel); + printformat(server, channel, MSGLEVEL_CRAP, + *setby == '\0' ? IRCTXT_INVITELIST : IRCTXT_INVITELIST_LONG, + channel, invite, setby, secs); + g_free(params); +} + +static void event_nick_in_use(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2, NULL, &nick); + if (server->connected) { + printformat(server, NULL, MSGLEVEL_CRAP, + IRCTXT_NICK_IN_USE, nick); + } + + g_free(params); +} + +static void event_topic_get(IRC_SERVER_REC *server, const char *data) +{ + const char *channel; + char *params, *topic, *recoded; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 3, NULL, &channel, &topic); + recoded = recode_in(SERVER(server), topic, channel); + channel = get_visible_target(server, channel); + printformat(server, channel, MSGLEVEL_CRAP, + IRCTXT_TOPIC, channel, recoded); + g_free(params); + g_free(recoded); +} + +static void event_topic_info(IRC_SERVER_REC *server, const char *data) +{ + const char *channel; + char *params, *timestr, *bynick, *byhost, *topictime; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 4, NULL, &channel, + &bynick, &topictime); + + timestr = my_asctime((time_t) atol(topictime)); + + byhost = strchr(bynick, '!'); + if (byhost != NULL) + *byhost++ = '\0'; + + channel = get_visible_target(server, channel); + printformat(server, channel, MSGLEVEL_CRAP, IRCTXT_TOPIC_INFO, + bynick, timestr, byhost == NULL ? "" : byhost); + g_free(timestr); + g_free(params); +} + +static void event_channel_mode(IRC_SERVER_REC *server, const char *data) +{ + const char *channel; + char *params, *mode; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 3 | PARAM_FLAG_GETREST, + NULL, &channel, &mode); + channel = get_visible_target(server, channel); + printformat(server, channel, MSGLEVEL_CRAP, + IRCTXT_CHANNEL_MODE, channel, g_strchomp(mode)); + g_free(params); +} + +static void event_channel_created(IRC_SERVER_REC *server, const char *data) +{ + const char *channel; + char *params, *createtime, *timestr; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 3, NULL, &channel, &createtime); + + timestr = my_asctime((time_t) atol(createtime)); + channel = get_visible_target(server, channel); + printformat(server, channel, MSGLEVEL_CRAP, + IRCTXT_CHANNEL_CREATED, channel, timestr); + g_free(timestr); + g_free(params); +} + +static void event_nowaway(IRC_SERVER_REC *server, const char *data) +{ + printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_AWAY); +} + +static void event_unaway(IRC_SERVER_REC *server, const char *data) +{ + printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_UNAWAY); +} + +static void event_away(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick, *awaymsg, *recoded; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 3, NULL, &nick, &awaymsg); + recoded = recode_in(SERVER(server), awaymsg, nick); + if (!settings_get_bool("show_away_once") || + last_away_nick == NULL || + g_ascii_strcasecmp(last_away_nick, nick) != 0 || + last_away_msg == NULL || + g_ascii_strcasecmp(last_away_msg, awaymsg) != 0) { + /* don't show the same away message + from the same nick all the time */ + g_free_not_null(last_away_nick); + g_free_not_null(last_away_msg); + last_away_nick = g_strdup(nick); + last_away_msg = g_strdup(awaymsg); + + printformat(server, nick, MSGLEVEL_CRAP, + IRCTXT_NICK_AWAY, nick, recoded); + } + g_free(params); + g_free(recoded); +} + +static void event_userhost(IRC_SERVER_REC *server, const char *data) +{ + char *params, *hosts; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2, NULL, &hosts); + printtext(server, NULL, MSGLEVEL_CRAP, "%s", hosts); + g_free(params); +} + +static void event_sent_invite(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick, *channel; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 3, NULL, &nick, &channel); + printformat(server, nick, MSGLEVEL_CRAP, + IRCTXT_INVITING, nick, channel); + g_free(params); +} + +static void event_chanserv_url(IRC_SERVER_REC *server, const char *data) +{ + const char *channel; + char *params, *url; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 3, NULL, &channel, &url); + channel = get_visible_target(server, channel); + printformat(server, channel, MSGLEVEL_CRAP, + IRCTXT_CHANNEL_URL, channel, url); + g_free(params); +} + +static void event_target_unavailable(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr) +{ + IRC_CHANNEL_REC *chanrec; + char *params, *target; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2, NULL, &target); + if (!server_ischannel(SERVER(server), target)) { + /* nick unavailable */ + printformat(server, NULL, MSGLEVEL_CRAP, + IRCTXT_NICK_UNAVAILABLE, target); + } else { + chanrec = irc_channel_find(server, target); + if (chanrec != NULL && chanrec->joined) { + /* dalnet - can't change nick while being banned */ + print_event_received(server, data, nick, FALSE); + } else { + /* channel is unavailable. */ + printformat(server, NULL, MSGLEVEL_CRAP, + IRCTXT_JOINERROR_UNAVAIL, target); + } + } + + g_free(params); +} + +static void event_no_such_nick(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr) +{ + char *params, *unick; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2, NULL, &unick); + if (!g_strcmp0(unick, "*")) + /* more information will be in the description, + * e.g. * :Target left IRC. Failed to deliver: [hi] */ + print_event_received(server, data, nick, FALSE); + else + printformat(server, unick, MSGLEVEL_CRAP, IRCTXT_NO_SUCH_NICK, unick); + g_free(params); +} + +static void event_no_such_channel(IRC_SERVER_REC *server, const char *data) +{ + char *params, *channel; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2, NULL, &channel); + printformat(server, channel, MSGLEVEL_CRAP, + IRCTXT_NO_SUCH_CHANNEL, channel); + g_free(params); +} + +static void cannot_join(IRC_SERVER_REC *server, const char *data, int format) +{ + char *params, *channel; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2, NULL, &channel); + printformat(server, NULL, MSGLEVEL_CRAP, format, channel); + g_free(params); +} + +static void event_too_many_channels(IRC_SERVER_REC *server, const char *data) +{ + cannot_join(server, data, IRCTXT_JOINERROR_TOOMANY); +} + +static void event_duplicate_channel(IRC_SERVER_REC *server, const char *data, + const char *nick) +{ + char *params, *channel, *p; + + g_return_if_fail(data != NULL); + + /* this new addition to ircd breaks completely with older + "standards", "nick Duplicate ::!!channel ...." */ + params = event_get_params(data, 3, NULL, NULL, &channel); + p = strchr(channel, ' '); + if (p != NULL) *p = '\0'; + + if (channel[0] == '!' && channel[1] == '!') { + printformat(server, NULL, MSGLEVEL_CRAP, + IRCTXT_JOINERROR_DUPLICATE, channel+1); + } else + print_event_received(server, data, nick, FALSE); + + g_free(params); +} + +static void event_channel_is_full(IRC_SERVER_REC *server, const char *data) +{ + cannot_join(server, data, IRCTXT_JOINERROR_FULL); +} + +static void event_invite_only(IRC_SERVER_REC *server, const char *data) +{ + cannot_join(server, data, IRCTXT_JOINERROR_INVITE); +} + +static void event_banned(IRC_SERVER_REC *server, const char *data) +{ + cannot_join(server, data, IRCTXT_JOINERROR_BANNED); +} + +static void event_bad_channel_key(IRC_SERVER_REC *server, const char *data) +{ + cannot_join(server, data, IRCTXT_JOINERROR_BAD_KEY); +} + +static void event_bad_channel_mask(IRC_SERVER_REC *server, const char *data) +{ + cannot_join(server, data, IRCTXT_JOINERROR_BAD_MASK); +} + +static void event_477(IRC_SERVER_REC *server, const char *data, const char *nick) +{ + /* Numeric 477 can mean many things: + * modeless channel, cannot join/send to channel (+r/+R/+M). + * If we tried to join this channel, display the error in the + * status window. Otherwise display it in the channel window. + */ + IRC_CHANNEL_REC *chanrec; + char *params, *channel; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2, NULL, &channel); + + chanrec = irc_channel_find(server, channel); + print_event_received(server, data, nick, chanrec == NULL || chanrec->joined); + g_free(params); +} + +static void event_489(IRC_SERVER_REC *server, const char *data, const char *nick) +{ + /* Numeric 489 can mean one of two things things: + * cannot join to channel (secure only), or not chanop or voice. + * If we tried to join this channel, display the joinerror. + * Otherwise depending on the channel being joined or not + * display the error in the channel or status window. + */ + IRC_CHANNEL_REC *chanrec; + char *params, *channel; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2, NULL, &channel); + + chanrec = irc_channel_find(server, channel); + if (chanrec != NULL && !chanrec->joined) { + cannot_join(server, data, IRCTXT_JOINERROR_SECURE_ONLY); + } else { + print_event_received(server, data, nick, chanrec == NULL || chanrec->joined); + } + g_free(params); +} + +static void event_help(IRC_SERVER_REC *server, int formatnum, const char *data) +{ + /* Common handling for umerics 704 (RPL_HELPSTART), 705 (RPL_HELPTXT), + * and 706 (RPL_ENDOFHELP); sent as a reply to HELP or HELPOP command. + */ + char *params, *topic, *help_text; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 3, NULL, &topic, &help_text); + + g_return_if_fail(help_text != NULL); + + if (help_text[0] == '\0') { + /* Empty lines can be used by servers for styling; and we need to replace + * them with something non-empty or they would be dropped when displayed. + */ + help_text = " "; + } + + printformat(server, NULL, MSGLEVEL_CRAP, formatnum, topic, help_text); + g_free(params); +} + +static void event_helpstart(IRC_SERVER_REC *server, const char *data, const char *nick) +{ + /* Numeric 704 (RPL_HELPSTART) sent as a reply to HELP or HELPOP command. + */ + event_help(server, IRCTXT_SERVER_HELP_START, data); +} + +static void event_helptxt(IRC_SERVER_REC *server, const char *data, const char *nick) +{ + /* Numeric 705 (RPL_HELPTXT), sent as a reply to HELP or HELPOP command. + */ + event_help(server, IRCTXT_SERVER_HELP_TXT, data); +} + +static void event_endofhelp(IRC_SERVER_REC *server, const char *data, const char *nick) +{ + /* Numeric 706 (RPL_ENDOFHELP), sent as a reply to HELP or HELPOP command. + */ + event_help(server, IRCTXT_SERVER_END_OF_HELP, data); +} + +static void event_target_too_fast(IRC_SERVER_REC *server, const char *data, + const char *nick) +{ + /* Target change too fast, could be nick or channel. + * If we tried to join this channel, display the error in the + * status window. Otherwise display it in the channel window. + */ + IRC_CHANNEL_REC *chanrec; + char *params, *channel; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2, NULL, &channel); + + chanrec = irc_channel_find(server, channel); + print_event_received(server, data, nick, chanrec == NULL || chanrec->joined); + g_free(params); +} + +static void event_unknown_mode(IRC_SERVER_REC *server, const char *data) +{ + char *params, *mode; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2, NULL, &mode); + printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_UNKNOWN_MODE, mode); + g_free(params); +} + +static void event_numeric(IRC_SERVER_REC *server, const char *data, + const char *nick) +{ + data = strchr(data, ' '); + if (data != NULL) + print_event_received(server, data+1, nick, FALSE); +} + +static void print_event_received(IRC_SERVER_REC *server, const char *data, + const char *nick, int target_param) +{ + char *target, *args, *ptr, *ptr2, *recoded; + int format; + + g_return_if_fail(data != NULL); + + /* first param is our nick, "*" or a channel */ + ptr = strchr(data, ' '); + if (ptr == NULL) + return; + ptr++; + + if (server_ischannel(SERVER(server), data)) /* directed at channel */ + target = g_strndup(data, (int)(ptr - data - 1)); + else if (!target_param || *ptr == ':' || (ptr2 = strchr(ptr, ' ')) == NULL) + target = NULL; + else { + /* target parameter expected and present */ + target = g_strndup(ptr, (int) (ptr2-ptr)); + } + + /* param1 param2 ... :last parameter */ + if (*ptr == ':') { + /* only one parameter */ + args = g_strdup(ptr+1); + } else { + args = g_strdup(ptr); + ptr = strstr(args, " :"); + if (ptr != NULL) + memmove(ptr+1, ptr+2, strlen(ptr+1)); + } + + recoded = recode_in(SERVER(server), args, NULL); + format = nick == NULL || server->real_address == NULL || + g_strcmp0(nick, server->real_address) == 0 ? + IRCTXT_DEFAULT_EVENT : IRCTXT_DEFAULT_EVENT_SERVER; + printformat(server, target, MSGLEVEL_CRAP, format, + nick, recoded, current_server_event); + + g_free(recoded); + g_free(args); + g_free(target); +} + +static void event_received(IRC_SERVER_REC *server, const char *data, + const char *nick) +{ + print_event_received(server, data, nick, FALSE); +} + +static void event_target_received(IRC_SERVER_REC *server, const char *data, + const char *nick) +{ + print_event_received(server, data, nick, TRUE); +} + +static void event_motd(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr) +{ + /* don't ignore motd anymore after 3 seconds of connection time - + we might have called /MOTD */ + if (settings_get_bool("skip_motd") && !server->motd_got) + return; + + print_event_received(server, data, nick, FALSE); +} + +static void sig_empty(void) +{ +} + +void fe_events_numeric_init(void) +{ + last_away_nick = NULL; + last_away_msg = NULL; + + /* clang-format off */ + signal_add("event 221", (SIGNAL_FUNC) event_user_mode); + signal_add("event 303", (SIGNAL_FUNC) event_ison); + signal_add("event 353", (SIGNAL_FUNC) event_names_list); + signal_add_first("event 366", (SIGNAL_FUNC) event_end_of_names); + signal_add("event 352", (SIGNAL_FUNC) event_who); + signal_add("event 315", (SIGNAL_FUNC) event_end_of_who); + signal_add("event 271", (SIGNAL_FUNC) event_silence_list); + signal_add("event 272", (SIGNAL_FUNC) sig_empty); + signal_add("event 281", (SIGNAL_FUNC) event_accept_list); + signal_add("event 367", (SIGNAL_FUNC) event_ban_list); + signal_add("event 348", (SIGNAL_FUNC) event_eban_list); + signal_add("event 346", (SIGNAL_FUNC) event_invite_list); + signal_add("event 433", (SIGNAL_FUNC) event_nick_in_use); + signal_add("event 332", (SIGNAL_FUNC) event_topic_get); + signal_add("event 333", (SIGNAL_FUNC) event_topic_info); + signal_add("event 324", (SIGNAL_FUNC) event_channel_mode); + signal_add("event 329", (SIGNAL_FUNC) event_channel_created); + signal_add("event 306", (SIGNAL_FUNC) event_nowaway); + signal_add("event 305", (SIGNAL_FUNC) event_unaway); + signal_add("event 301", (SIGNAL_FUNC) event_away); + signal_add("event 328", (SIGNAL_FUNC) event_chanserv_url); + signal_add("event 302", (SIGNAL_FUNC) event_userhost); + signal_add("event 341", (SIGNAL_FUNC) event_sent_invite); + + signal_add("event 437", (SIGNAL_FUNC) event_target_unavailable); + signal_add("event 401", (SIGNAL_FUNC) event_no_such_nick); + signal_add("event 403", (SIGNAL_FUNC) event_no_such_channel); + signal_add("event 405", (SIGNAL_FUNC) event_too_many_channels); + signal_add("event 407", (SIGNAL_FUNC) event_duplicate_channel); + signal_add("event 471", (SIGNAL_FUNC) event_channel_is_full); + signal_add("event 472", (SIGNAL_FUNC) event_unknown_mode); + signal_add("event 473", (SIGNAL_FUNC) event_invite_only); + signal_add("event 474", (SIGNAL_FUNC) event_banned); + signal_add("event 475", (SIGNAL_FUNC) event_bad_channel_key); + signal_add("event 476", (SIGNAL_FUNC) event_bad_channel_mask); + signal_add("event 477", (SIGNAL_FUNC) event_477); + signal_add("event 489", (SIGNAL_FUNC) event_489); /* cannot join to channel (secure only), or not chanop or voice. */ + signal_add("event 375", (SIGNAL_FUNC) event_motd); + signal_add("event 376", (SIGNAL_FUNC) event_motd); + signal_add("event 372", (SIGNAL_FUNC) event_motd); + signal_add("event 422", (SIGNAL_FUNC) event_motd); + signal_add("event 439", (SIGNAL_FUNC) event_target_too_fast); + signal_add("event 704", (SIGNAL_FUNC) event_helpstart); + signal_add("event 705", (SIGNAL_FUNC) event_helptxt); + signal_add("event 706", (SIGNAL_FUNC) event_endofhelp); + signal_add("event 707", (SIGNAL_FUNC) event_target_too_fast); + + signal_add("default event numeric", (SIGNAL_FUNC) event_numeric); + /* Because default event numeric only fires if there is no specific + * event, add all numerics with a handler elsewhere in irssi that + * should not be printed specially here. + */ + signal_add("event 001", (SIGNAL_FUNC) event_received); + signal_add("event 004", (SIGNAL_FUNC) event_received); + signal_add("event 005", (SIGNAL_FUNC) event_received); + signal_add("event 254", (SIGNAL_FUNC) event_received); + signal_add("event 354", (SIGNAL_FUNC) event_received); + signal_add("event 364", (SIGNAL_FUNC) event_received); + signal_add("event 365", (SIGNAL_FUNC) event_received); + signal_add("event 381", (SIGNAL_FUNC) event_received); + signal_add("event 396", (SIGNAL_FUNC) event_received); + signal_add("event 421", (SIGNAL_FUNC) event_received); + signal_add("event 432", (SIGNAL_FUNC) event_received); + signal_add("event 436", (SIGNAL_FUNC) event_received); + signal_add("event 438", (SIGNAL_FUNC) event_received); + signal_add("event 465", (SIGNAL_FUNC) event_received); + signal_add("event 470", (SIGNAL_FUNC) event_received); + signal_add("event 479", (SIGNAL_FUNC) event_received); + + signal_add("event 344", (SIGNAL_FUNC) event_target_received); /* reop list */ + signal_add("event 345", (SIGNAL_FUNC) event_target_received); /* end of reop list */ + signal_add("event 347", (SIGNAL_FUNC) event_target_received); /* end of invite exception list */ + signal_add("event 349", (SIGNAL_FUNC) event_target_received); /* end of ban exception list */ + signal_add("event 368", (SIGNAL_FUNC) event_target_received); /* end of ban list */ + signal_add("event 386", (SIGNAL_FUNC) event_target_received); /* owner list; old rsa challenge (harmless) */ + signal_add("event 387", (SIGNAL_FUNC) event_target_received); /* end of owner list */ + signal_add("event 388", (SIGNAL_FUNC) event_target_received); /* protect list */ + signal_add("event 389", (SIGNAL_FUNC) event_target_received); /* end of protect list */ + signal_add("event 404", (SIGNAL_FUNC) event_target_received); /* cannot send to channel */ + signal_add("event 408", (SIGNAL_FUNC) event_target_received); /* cannot send (+c) */ + signal_add("event 442", (SIGNAL_FUNC) event_target_received); /* you're not on that channel */ + signal_add("event 478", (SIGNAL_FUNC) event_target_received); /* ban list is full */ + signal_add("event 482", (SIGNAL_FUNC) event_target_received); /* not chanop */ + signal_add("event 486", (SIGNAL_FUNC) event_target_received); /* cannot /msg (+R) */ + signal_add("event 494", (SIGNAL_FUNC) event_target_received); /* cannot /msg (own +R) */ + signal_add("event 506", (SIGNAL_FUNC) event_target_received); /* cannot send (+R) */ + signal_add("event 716", (SIGNAL_FUNC) event_target_received); /* cannot /msg (+g) */ + signal_add("event 717", (SIGNAL_FUNC) event_target_received); /* +g notified */ + signal_add("event 728", (SIGNAL_FUNC) event_target_received); /* quiet (or other) list */ + signal_add("event 729", (SIGNAL_FUNC) event_target_received); /* end of quiet (or other) list */ + /* clang-format on */ +} + +void fe_events_numeric_deinit(void) +{ + g_free_not_null(last_away_nick); + g_free_not_null(last_away_msg); + + signal_remove("event 221", (SIGNAL_FUNC) event_user_mode); + signal_remove("event 303", (SIGNAL_FUNC) event_ison); + signal_remove("event 353", (SIGNAL_FUNC) event_names_list); + signal_remove("event 366", (SIGNAL_FUNC) event_end_of_names); + signal_remove("event 352", (SIGNAL_FUNC) event_who); + signal_remove("event 315", (SIGNAL_FUNC) event_end_of_who); + signal_remove("event 271", (SIGNAL_FUNC) event_silence_list); + signal_remove("event 272", (SIGNAL_FUNC) sig_empty); + signal_remove("event 281", (SIGNAL_FUNC) event_accept_list); + signal_remove("event 367", (SIGNAL_FUNC) event_ban_list); + signal_remove("event 348", (SIGNAL_FUNC) event_eban_list); + signal_remove("event 346", (SIGNAL_FUNC) event_invite_list); + signal_remove("event 433", (SIGNAL_FUNC) event_nick_in_use); + signal_remove("event 332", (SIGNAL_FUNC) event_topic_get); + signal_remove("event 333", (SIGNAL_FUNC) event_topic_info); + signal_remove("event 324", (SIGNAL_FUNC) event_channel_mode); + signal_remove("event 329", (SIGNAL_FUNC) event_channel_created); + signal_remove("event 306", (SIGNAL_FUNC) event_nowaway); + signal_remove("event 305", (SIGNAL_FUNC) event_unaway); + signal_remove("event 301", (SIGNAL_FUNC) event_away); + signal_remove("event 328", (SIGNAL_FUNC) event_chanserv_url); + signal_remove("event 302", (SIGNAL_FUNC) event_userhost); + signal_remove("event 341", (SIGNAL_FUNC) event_sent_invite); + + signal_remove("event 437", (SIGNAL_FUNC) event_target_unavailable); + signal_remove("event 401", (SIGNAL_FUNC) event_no_such_nick); + signal_remove("event 403", (SIGNAL_FUNC) event_no_such_channel); + signal_remove("event 405", (SIGNAL_FUNC) event_too_many_channels); + signal_remove("event 407", (SIGNAL_FUNC) event_duplicate_channel); + signal_remove("event 471", (SIGNAL_FUNC) event_channel_is_full); + signal_remove("event 472", (SIGNAL_FUNC) event_unknown_mode); + signal_remove("event 473", (SIGNAL_FUNC) event_invite_only); + signal_remove("event 474", (SIGNAL_FUNC) event_banned); + signal_remove("event 475", (SIGNAL_FUNC) event_bad_channel_key); + signal_remove("event 476", (SIGNAL_FUNC) event_bad_channel_mask); + signal_remove("event 477", (SIGNAL_FUNC) event_477); + signal_remove("event 489", (SIGNAL_FUNC) event_489); + signal_remove("event 375", (SIGNAL_FUNC) event_motd); + signal_remove("event 376", (SIGNAL_FUNC) event_motd); + signal_remove("event 372", (SIGNAL_FUNC) event_motd); + signal_remove("event 422", (SIGNAL_FUNC) event_motd); + signal_remove("event 439", (SIGNAL_FUNC) event_target_too_fast); + signal_remove("event 704", (SIGNAL_FUNC) event_helpstart); + signal_remove("event 705", (SIGNAL_FUNC) event_helptxt); + signal_remove("event 706", (SIGNAL_FUNC) event_endofhelp); + signal_remove("event 707", (SIGNAL_FUNC) event_target_too_fast); + + signal_remove("default event numeric", (SIGNAL_FUNC) event_numeric); + signal_remove("event 001", (SIGNAL_FUNC) event_received); + signal_remove("event 004", (SIGNAL_FUNC) event_received); + signal_remove("event 005", (SIGNAL_FUNC) event_received); + signal_remove("event 254", (SIGNAL_FUNC) event_received); + signal_remove("event 354", (SIGNAL_FUNC) event_received); + signal_remove("event 364", (SIGNAL_FUNC) event_received); + signal_remove("event 365", (SIGNAL_FUNC) event_received); + signal_remove("event 381", (SIGNAL_FUNC) event_received); + signal_remove("event 396", (SIGNAL_FUNC) event_received); + signal_remove("event 421", (SIGNAL_FUNC) event_received); + signal_remove("event 432", (SIGNAL_FUNC) event_received); + signal_remove("event 436", (SIGNAL_FUNC) event_received); + signal_remove("event 438", (SIGNAL_FUNC) event_received); + signal_remove("event 465", (SIGNAL_FUNC) event_received); + signal_remove("event 470", (SIGNAL_FUNC) event_received); + signal_remove("event 479", (SIGNAL_FUNC) event_received); + + signal_remove("event 344", (SIGNAL_FUNC) event_target_received); + signal_remove("event 345", (SIGNAL_FUNC) event_target_received); + signal_remove("event 347", (SIGNAL_FUNC) event_target_received); + signal_remove("event 349", (SIGNAL_FUNC) event_target_received); + signal_remove("event 368", (SIGNAL_FUNC) event_target_received); + signal_remove("event 386", (SIGNAL_FUNC) event_target_received); + signal_remove("event 387", (SIGNAL_FUNC) event_target_received); + signal_remove("event 388", (SIGNAL_FUNC) event_target_received); + signal_remove("event 389", (SIGNAL_FUNC) event_target_received); + signal_remove("event 404", (SIGNAL_FUNC) event_target_received); + signal_remove("event 408", (SIGNAL_FUNC) event_target_received); + signal_remove("event 442", (SIGNAL_FUNC) event_target_received); + signal_remove("event 478", (SIGNAL_FUNC) event_target_received); + signal_remove("event 482", (SIGNAL_FUNC) event_target_received); + signal_remove("event 486", (SIGNAL_FUNC) event_target_received); + signal_remove("event 494", (SIGNAL_FUNC) event_target_received); + signal_remove("event 506", (SIGNAL_FUNC) event_target_received); + signal_remove("event 716", (SIGNAL_FUNC) event_target_received); + signal_remove("event 717", (SIGNAL_FUNC) event_target_received); + signal_remove("event 728", (SIGNAL_FUNC) event_target_received); + signal_remove("event 729", (SIGNAL_FUNC) event_target_received); +} diff --git a/src/fe-common/irc/fe-events.c b/src/fe-common/irc/fe-events.c new file mode 100644 index 0000000..b6b3e75 --- /dev/null +++ b/src/fe-common/irc/fe-events.c @@ -0,0 +1,550 @@ +/* + fe-events.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/irc/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/core/levels.h> +#include <irssi/src/core/servers.h> +#include <irssi/src/irc/core/servers-redirect.h> +#include <irssi/src/core/servers-reconnect.h> +#include <irssi/src/core/queries.h> +#include <irssi/src/core/ignore.h> +#include <irssi/src/core/recode.h> + +#include <irssi/src/irc/core/irc-servers.h> +#include <irssi/src/irc/core/irc-channels.h> +#include <irssi/src/irc/core/irc-nicklist.h> +#include <irssi/src/irc/core/irc-masks.h> + +#include <irssi/src/fe-common/core/printtext.h> +#include <irssi/src/fe-common/core/fe-queries.h> +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/irc/fe-irc-server.h> +#include <irssi/src/fe-common/irc/fe-irc-channels.h> + +static void event_privmsg(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr) +{ + char *params, *target, *msg, *recoded; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2 | PARAM_FLAG_GETREST, &target, &msg); + if (nick == NULL) nick = server->real_address; + if (addr == NULL) addr = ""; + + if (fe_channel_is_opchannel(server, target)) { + /* Hybrid 6 feature, send msg to all ops in channel */ + const char *cleantarget = fe_channel_skip_prefix(server, target); + recoded = recode_in(SERVER(server), msg, cleantarget); + + /* pass the original target to the signal, with the @+ here + * the other one is only needed for recode_in*/ + signal_emit("message irc op_public", 5, + server, recoded, nick, addr, target); + } else { + recoded = recode_in(SERVER(server), msg, server_ischannel(SERVER(server), target) ? target : nick); + signal_emit(server_ischannel(SERVER(server), target) ? + "message public" : "message private", 5, + server, recoded, nick, addr, + get_visible_target(server, target)); + } + + g_free(params); + g_free(recoded); +} + +static void ctcp_action(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr, + const char *target) +{ + char *recoded; + + g_return_if_fail(data != NULL); + recoded = recode_in(SERVER(server), data, target); + signal_emit("message irc action", 5, + server, recoded, nick, addr, + get_visible_target(server, target)); + g_free(recoded); +} + +static void event_notice(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr) +{ + char *params, *target, *msg, *recoded; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2 | PARAM_FLAG_GETREST, &target, &msg); + recoded = recode_in(SERVER(server), msg, target); + if (nick == NULL) { + nick = server->real_address == NULL ? + server->connrec->address : + server->real_address; + } + + signal_emit("message irc notice", 5, server, recoded, nick, addr, + get_visible_target(server, target)); + g_free(params); + g_free(recoded); +} + +static void event_join(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr) +{ + char *params, *channel, *tmp, *account, *realname; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 3, &channel, &account, &realname); + tmp = strchr(channel, 7); /* ^G does something weird.. */ + if (tmp != NULL) *tmp = '\0'; + + signal_emit("message join", 6, server, + get_visible_target(server, channel), nick, addr, account, realname); + g_free(params); +} + +static void event_chghost(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr) +{ + char *params, *user, *host, *new_addr; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2, &user, &host); + new_addr = g_strconcat(user, "@", host, NULL); + + signal_emit("message host_changed", 4, server, nick, new_addr, addr); + + g_free(new_addr); + g_free(params); +} + +static void event_account(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr) +{ + char *params, *account; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 1, &account); + + signal_emit("message account_changed", 4, server, nick, addr, account); + + g_free(params); +} + +static void event_part(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr) +{ + char *params, *channel, *reason, *recoded; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2 | PARAM_FLAG_GETREST, + &channel, &reason); + recoded = recode_in(SERVER(server), reason, channel); + signal_emit("message part", 5, server, + get_visible_target(server, channel), nick, addr, recoded); + g_free(params); + g_free(recoded); +} + +static void event_quit(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr) +{ + char *recoded; + + g_return_if_fail(data != NULL); + + if (*data == ':') data++; /* quit message */ + recoded = recode_in(SERVER(server), data, nick); + signal_emit("message quit", 4, server, nick, addr, recoded); + g_free(recoded); +} + +static void event_kick(IRC_SERVER_REC *server, const char *data, + const char *kicker, const char *addr) +{ + char *params, *channel, *nick, *reason, *recoded; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 3 | PARAM_FLAG_GETREST, + &channel, &nick, &reason); + recoded = recode_in(SERVER(server), reason, channel); + signal_emit("message kick", 6, + server, get_visible_target(server, channel), + nick, kicker, addr, recoded); + g_free(params); + g_free(recoded); +} + +static void event_kill(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr) +{ + char *params, *path, *reason; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2 | PARAM_FLAG_GETREST, + NULL, &path); + reason = strstr(path, " ("); + if (reason == NULL || reason[strlen(reason)-1] != ')') { + /* weird server, maybe it didn't give path */ + reason = path; + path = ""; + } else { + /* reason inside (...) */ + *reason = '\0'; + reason += 2; + reason[strlen(reason)-1] = '\0'; + } + + if (addr != NULL) { + printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_KILL, + nick, addr, reason, path); + } else { + printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_KILL_SERVER, + nick, reason, path); + } + + g_free(params); +} + +static void event_nick(IRC_SERVER_REC *server, const char *data, + const char *sender, const char *addr) +{ + char *params, *newnick; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 1, &newnick); + + /* NOTE: server->nick was already changed in irc/core/irc-nicklist.c */ + signal_emit(g_ascii_strcasecmp(newnick, server->nick) == 0 ? + "message own_nick" : "message nick", 4, + server, newnick, sender, addr); + + g_free(params); +} + +static void event_mode(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr) +{ + char *params, *channel, *mode; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2 | PARAM_FLAG_GETREST, + &channel, &mode); + + signal_emit("message irc mode", 5, + server, get_visible_target(server, channel), + nick, addr, g_strchomp(mode)); + g_free(params); +} + +static void event_away_notify(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr) +{ + char *params, *awaymsg; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 1 | PARAM_FLAG_GETREST, + &awaymsg); + + signal_emit("message away_notify", 4, + server, nick, addr, awaymsg); + g_free(params); +} + +static void event_pong(IRC_SERVER_REC *server, const char *data, const char *nick) +{ + char *params, *host, *reply; + + g_return_if_fail(data != NULL); + if (nick == NULL) nick = server->real_address; + + params = event_get_params(data, 2 | PARAM_FLAG_GETREST, &host, &reply); + printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_PONG, host, reply); + g_free(params); +} + +static void event_invite(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr) +{ + char *params, *invited, *channel; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2, &invited, &channel); + if (server->nick_comp_func(invited, server->nick) == 0) { + signal_emit("message invite", 4, + server, get_visible_target(server, channel), nick, addr); + } else { + signal_emit("message invite_other", 5, + server, get_visible_target(server, channel), invited, nick, addr); + } + g_free(params); +} + +static void event_topic(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr) +{ + char *params, *channel, *topic, *recoded; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2 | PARAM_FLAG_GETREST, + &channel, &topic); + recoded = recode_in(SERVER(server), topic, channel); + signal_emit("message topic", 5, server, + get_visible_target(server, channel), recoded, nick, addr); + g_free(params); + g_free(recoded); +} + +static void event_error(IRC_SERVER_REC *server, const char *data) +{ + g_return_if_fail(data != NULL); + + if (*data == ':') data++; + printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_ERROR, data); +} + +static void event_wallops(IRC_SERVER_REC *server, const char *data, const char *nick, const char *addr) +{ + g_return_if_fail(data != NULL); + + if (*data == ':') data++; + if (ignore_check(SERVER(server), nick, addr, NULL, data, MSGLEVEL_WALLOPS)) + return; + + if (g_ascii_strncasecmp(data, "\001ACTION ", 8) != 0) + printformat(server, NULL, MSGLEVEL_WALLOPS, IRCTXT_WALLOPS, nick, data); + else { + /* Action in WALLOP */ + int len; + char *tmp; + + tmp = g_strdup(data+8); + len = strlen(tmp); + if (len >= 1 && tmp[len-1] == 1) tmp[len-1] = '\0'; + printformat(server, NULL, MSGLEVEL_WALLOPS, IRCTXT_ACTION_WALLOPS, nick, tmp); + g_free(tmp); + } +} + +static void event_silence(IRC_SERVER_REC *server, const char *data, const char *nick, const char *addr) +{ + g_return_if_fail(data != NULL); + + g_return_if_fail(*data == '+' || *data == '-'); + + printformat(server, NULL, MSGLEVEL_CRAP, *data == '+' ? IRCTXT_SILENCED : IRCTXT_UNSILENCED, data+1); +} + +static void channel_sync(CHANNEL_REC *channel) +{ + g_return_if_fail(channel != NULL); + + printformat(channel->server, channel->visible_name, + MSGLEVEL_CLIENTNOTICE|MSGLEVEL_NO_ACT, + IRCTXT_CHANNEL_SYNCED, channel->visible_name, + (long) (time(NULL)-channel->createtime)); +} + +static void event_connected(IRC_SERVER_REC *server) +{ + const char *nick; + + g_return_if_fail(server != NULL); + + nick = server->connrec->nick; + if (g_ascii_strcasecmp(server->nick, nick) == 0) + return; + + /* someone has our nick, find out who. */ + server_redirect_event(server, "whois", 1, nick, TRUE, NULL, + "event 311", "nickfind event whois", + "", "event empty", NULL); + irc_send_cmdv(server, "WHOIS %s", nick); +} + +static void event_nickfind_whois(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick, *user, *host, *realname; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 6, NULL, &nick, &user, &host, NULL, &realname); + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_YOUR_NICK_OWNED, nick, user, host, realname); + g_free(params); +} + +static void event_ban_type_changed(void *ban_typep) +{ + GString *str; + int ban_type; + + ban_type = GPOINTER_TO_INT(ban_typep); + + if (ban_type == 0) { + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + IRCTXT_BANTYPE, "Error, using Normal"); + return; + } + + if (ban_type == (IRC_MASK_USER|IRC_MASK_DOMAIN)) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + IRCTXT_BANTYPE, "Normal"); + } else if (ban_type == (IRC_MASK_HOST|IRC_MASK_DOMAIN)) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + IRCTXT_BANTYPE, "Host"); + } else if (ban_type == IRC_MASK_DOMAIN) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + IRCTXT_BANTYPE, "Domain"); + } else { + str = g_string_new("Custom:"); + if (ban_type & IRC_MASK_NICK) + g_string_append(str, " Nick"); + if (ban_type & IRC_MASK_USER) + g_string_append(str, " User"); + if (ban_type & IRC_MASK_HOST) + g_string_append(str, " Host"); + if (ban_type & IRC_MASK_DOMAIN) + g_string_append(str, " Domain"); + + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + IRCTXT_BANTYPE, str->str); + g_string_free(str, TRUE); + } +} + +static void sig_whois_event_not_found(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2, NULL, &nick); + printformat(server, nick, MSGLEVEL_CRAP, IRCTXT_WHOIS_NOT_FOUND, nick); + g_free(params); +} + +static void sig_whowas_event_end(IRC_SERVER_REC *server, const char *data, + const char *sender, const char *addr) +{ + char *params, *nick; + + g_return_if_fail(data != NULL); + + if (server->whowas_found) { + signal_emit("event 369", 4, server, data, sender, addr); + return; + } + + params = event_get_params(data, 2, NULL, &nick); + printformat(server, nick, MSGLEVEL_CRAP, IRCTXT_WHOIS_NOT_FOUND, nick); + g_free(params); +} + +static void event_received(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr) +{ + if (!i_isdigit(*data)) { + printtext(server, NULL, MSGLEVEL_CRAP, "%s", data); + return; + } + + /* numeric event. */ + signal_emit("default event numeric", 4, server, data, nick, addr); +} + +void fe_events_init(void) +{ + signal_add("event privmsg", (SIGNAL_FUNC) event_privmsg); + signal_add("ctcp action", (SIGNAL_FUNC) ctcp_action); + signal_add("event notice", (SIGNAL_FUNC) event_notice); + signal_add("event join", (SIGNAL_FUNC) event_join); + signal_add("event chghost", (SIGNAL_FUNC) event_chghost); + signal_add("event account", (SIGNAL_FUNC) event_account); + signal_add("event part", (SIGNAL_FUNC) event_part); + signal_add("event quit", (SIGNAL_FUNC) event_quit); + signal_add("event kick", (SIGNAL_FUNC) event_kick); + signal_add("event kill", (SIGNAL_FUNC) event_kill); + signal_add("event nick", (SIGNAL_FUNC) event_nick); + signal_add("event mode", (SIGNAL_FUNC) event_mode); + signal_add("event pong", (SIGNAL_FUNC) event_pong); + signal_add("event invite", (SIGNAL_FUNC) event_invite); + signal_add("event topic", (SIGNAL_FUNC) event_topic); + signal_add("event error", (SIGNAL_FUNC) event_error); + signal_add("event wallops", (SIGNAL_FUNC) event_wallops); + signal_add("event silence", (SIGNAL_FUNC) event_silence); + signal_add("event away", (SIGNAL_FUNC) event_away_notify); + + signal_add("default event", (SIGNAL_FUNC) event_received); + + signal_add("channel sync", (SIGNAL_FUNC) channel_sync); + signal_add("event connected", (SIGNAL_FUNC) event_connected); + signal_add("nickfind event whois", (SIGNAL_FUNC) event_nickfind_whois); + signal_add("ban type changed", (SIGNAL_FUNC) event_ban_type_changed); + signal_add("whois event not found", (SIGNAL_FUNC) sig_whois_event_not_found); + signal_add("whowas event end", (SIGNAL_FUNC) sig_whowas_event_end); +} + +void fe_events_deinit(void) +{ + signal_remove("event privmsg", (SIGNAL_FUNC) event_privmsg); + signal_remove("ctcp action", (SIGNAL_FUNC) ctcp_action); + signal_remove("event notice", (SIGNAL_FUNC) event_notice); + signal_remove("event join", (SIGNAL_FUNC) event_join); + signal_remove("event chghost", (SIGNAL_FUNC) event_chghost); + signal_remove("event account", (SIGNAL_FUNC) event_account); + signal_remove("event part", (SIGNAL_FUNC) event_part); + signal_remove("event quit", (SIGNAL_FUNC) event_quit); + signal_remove("event kick", (SIGNAL_FUNC) event_kick); + signal_remove("event kill", (SIGNAL_FUNC) event_kill); + signal_remove("event nick", (SIGNAL_FUNC) event_nick); + signal_remove("event mode", (SIGNAL_FUNC) event_mode); + signal_remove("event pong", (SIGNAL_FUNC) event_pong); + signal_remove("event invite", (SIGNAL_FUNC) event_invite); + signal_remove("event topic", (SIGNAL_FUNC) event_topic); + signal_remove("event error", (SIGNAL_FUNC) event_error); + signal_remove("event wallops", (SIGNAL_FUNC) event_wallops); + signal_remove("event silence", (SIGNAL_FUNC) event_silence); + signal_remove("event away", (SIGNAL_FUNC) event_away_notify); + + signal_remove("default event", (SIGNAL_FUNC) event_received); + + signal_remove("channel sync", (SIGNAL_FUNC) channel_sync); + signal_remove("event connected", (SIGNAL_FUNC) event_connected); + signal_remove("nickfind event whois", (SIGNAL_FUNC) event_nickfind_whois); + signal_remove("ban type changed", (SIGNAL_FUNC) event_ban_type_changed); + signal_remove("whois event not found", (SIGNAL_FUNC) sig_whois_event_not_found); + signal_remove("whowas event end", (SIGNAL_FUNC) sig_whowas_event_end); +} diff --git a/src/fe-common/irc/fe-irc-channels.c b/src/fe-common/irc/fe-irc-channels.c new file mode 100644 index 0000000..93ff360 --- /dev/null +++ b/src/fe-common/irc/fe-irc-channels.c @@ -0,0 +1,111 @@ +/* + fe-irc-channels.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/irc/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/levels.h> + +#include <irssi/src/irc/core/irc-servers.h> +#include <irssi/src/irc/core/irc-channels.h> +#include <irssi/src/irc/core/channel-rejoin.h> + +#include <irssi/src/fe-common/core/printtext.h> +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/core/window-items.h> + +int fe_channel_is_opchannel(IRC_SERVER_REC *server, const char *target) +{ + const char *statusmsg; + + /* Quick check */ + if (server == NULL || server->prefix[(int)(unsigned char)*target] == 0) + return FALSE; + + statusmsg = g_hash_table_lookup(server->isupport, "statusmsg"); + if (statusmsg == NULL) + statusmsg = "@"; + + return strchr(statusmsg, *target) != NULL; +} + +const char *fe_channel_skip_prefix(IRC_SERVER_REC *server, const char *target) +{ + const char *statusmsg; + + /* Quick check */ + if (server == NULL || server->prefix[(int)(unsigned char)*target] == 0) + return target; + + /* Exit early if target doesn't name a channel */ + if (server_ischannel(SERVER(server), target) == FALSE) + return target; + + statusmsg = g_hash_table_lookup(server->isupport, "statusmsg"); + + /* Hack: for bahamut 1.4 which sends neither STATUSMSG nor + * WALLCHOPS in 005 */ + if (statusmsg == NULL) + statusmsg = "@"; + + /* Strip the leading statusmsg prefixes */ + while (strchr(statusmsg, *target) != NULL) { + target++; + } + + return target; +} + +static void sig_channel_rejoin(SERVER_REC *server, REJOIN_REC *rec) +{ + g_return_if_fail(rec != NULL); + + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, + IRCTXT_CHANNEL_REJOIN, rec->channel); +} + +static void sig_event_forward(SERVER_REC *server, const char *data, + const char *nick) +{ + IRC_CHANNEL_REC *channel; + char *params, *from, *to; + + params = event_get_params(data, 3, NULL, &from, &to); + if (from != NULL && to != NULL && server_ischannel(server, from) && server_ischannel(server, to)) { + channel = irc_channel_find(server, from); + if (channel != NULL && irc_channel_find(server, to) == NULL) { + window_bind_add(window_item_window(channel), + server->tag, to); + } + } + g_free(params); +} + +void fe_irc_channels_init(void) +{ + signal_add("channel rejoin new", (SIGNAL_FUNC) sig_channel_rejoin); + signal_add_first("event 470", (SIGNAL_FUNC) sig_event_forward); +} + +void fe_irc_channels_deinit(void) +{ + signal_remove("channel rejoin new", (SIGNAL_FUNC) sig_channel_rejoin); + signal_remove("event 470", (SIGNAL_FUNC) sig_event_forward); +} diff --git a/src/fe-common/irc/fe-irc-channels.h b/src/fe-common/irc/fe-irc-channels.h new file mode 100644 index 0000000..a577010 --- /dev/null +++ b/src/fe-common/irc/fe-irc-channels.h @@ -0,0 +1,10 @@ +#ifndef IRSSI_FE_COMMON_IRC_FE_IRC_CHANNELS_H +#define IRSSI_FE_COMMON_IRC_FE_IRC_CHANNELS_H + +int fe_channel_is_opchannel(IRC_SERVER_REC *server, const char *target); +const char *fe_channel_skip_prefix(IRC_SERVER_REC *server, const char *target); + +void fe_irc_channels_init(void); +void fe_irc_channels_deinit(void); + +#endif diff --git a/src/fe-common/irc/fe-irc-commands.c b/src/fe-common/irc/fe-irc-commands.c new file mode 100644 index 0000000..40ad36e --- /dev/null +++ b/src/fe-common/irc/fe-irc-commands.c @@ -0,0 +1,448 @@ +/* + fe-irc-commands.c : irssi + + Copyright (C) 1999-2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/irc/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/special-vars.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/core/levels.h> +#include <irssi/src/core/servers.h> +#include <irssi/src/irc/core/mode-lists.h> +#include <irssi/src/core/nicklist.h> +#include <irssi/src/irc/core/irc-commands.h> +#include <irssi/src/irc/core/irc-servers.h> +#include <irssi/src/irc/core/irc-channels.h> +#include <irssi/src/irc/core/irc-queries.h> + +#include <irssi/src/fe-common/core/fe-queries.h> +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/core/window-items.h> +#include <irssi/src/fe-common/core/printtext.h> +#include <irssi/src/fe-common/core/keyboard.h> + +/* SYNTAX: ME <message> */ +static void cmd_me(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item) +{ + const char *target; + char *subdata; + char **splitdata; + int n = 0; + + CMD_IRC_SERVER(server); + if (!IS_IRC_ITEM(item)) + return; + + if (server == NULL || !server->connected) + cmd_return_error(CMDERR_NOT_CONNECTED); + + target = window_item_get_target(item); + splitdata = irc_server_split_action(server, target, data); + while ((subdata = splitdata[n++])) { + irc_server_send_action(server, target, subdata); + signal_emit("message irc own_action", 3, server, subdata, + item->visible_name); + } + g_strfreev(splitdata); +} + +/* SYNTAX: ACTION [-<server tag>] <target> <message> */ +static void cmd_action(const char *data, IRC_SERVER_REC *server) +{ + GHashTable *optlist; + const char *target, *text; + char *subtext; + char **splittexts; + int n = 0; + void *free_arg; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS | + PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST, + "action", &optlist, &target, &text)) + return; + if (*target == '\0' || *text == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + server = IRC_SERVER(cmd_options_get_server("action", optlist, SERVER(server))); + if (server == NULL || !server->connected) + cmd_param_error(CMDERR_NOT_CONNECTED); + + splittexts = irc_server_split_action(server, target, text); + while ((subtext = splittexts[n++])) { + irc_server_send_action(server, target, subtext); + signal_emit("message irc own_action", 3, server, subtext, + target); + } + + g_strfreev(splittexts); + cmd_params_free(free_arg); +} + +static void cmd_notice(const char *data, IRC_SERVER_REC *server, + WI_ITEM_REC *item) +{ + const char *target, *msg; + void *free_arg; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, + &target, &msg)) + return; + if (g_strcmp0(target, "*") == 0) + target = item == NULL ? "" : window_item_get_target(item); + + if (*target == '\0' || *msg == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + signal_emit("message irc own_notice", 3, server, msg, target); + + cmd_params_free(free_arg); +} + +static void cmd_ctcp(const char *data, IRC_SERVER_REC *server, + WI_ITEM_REC *item) +{ + const char *target; + char *ctcpcmd, *ctcpdata; + void *free_arg; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST, + &target, &ctcpcmd, &ctcpdata)) + return; + if (g_strcmp0(target, "*") == 0) + target = item == NULL ? "" : window_item_get_target(item); + if (*target == '\0' || *ctcpcmd == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + if (*target == '=') { + /* don't handle DCC CTCPs */ + cmd_params_free(free_arg); + return; + } + + ascii_strup(ctcpcmd); + signal_emit("message irc own_ctcp", 4, + server, ctcpcmd, ctcpdata, target); + + cmd_params_free(free_arg); +} + +static void cmd_nctcp(const char *data, IRC_SERVER_REC *server, + WI_ITEM_REC *item) +{ + const char *target, *text; + void *free_arg; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, + &target, &text)) + return; + if (g_strcmp0(target, "*") == 0) + target = item == NULL ? "" : window_item_get_target(item); + if (*target == '\0' || *text == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + signal_emit("message irc own_notice", 3, server, text, target); + cmd_params_free(free_arg); +} + +static void cmd_wall(const char *data, IRC_SERVER_REC *server, + WI_ITEM_REC *item) +{ + IRC_CHANNEL_REC *chanrec; + const char *channame, *msg; + void *free_arg; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN | + PARAM_FLAG_GETREST, item, &channame, &msg)) + return; + if (*msg == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + chanrec = irc_channel_find(server, channame); + if (chanrec == NULL) cmd_param_error(CMDERR_CHAN_NOT_FOUND); + + signal_emit("message irc own_wall", 3, server, msg, + chanrec->visible_name); + + cmd_params_free(free_arg); +} + +static void bans_ask_channel(const char *channel, IRC_SERVER_REC *server, + WI_ITEM_REC *item) +{ + GString *str; + + str = g_string_new(NULL); + g_string_printf(str, "%s b", channel); + signal_emit("command mode", 3, str->str, server, item); + if (server->emode_known) { + g_string_printf(str, "%s e", channel); + signal_emit("command mode", 3, str->str, server, item); + } + g_string_free(str, TRUE); +} + +static void bans_show_channel(IRC_CHANNEL_REC *channel, IRC_SERVER_REC *server) +{ + GSList *tmp; + int counter; + + if (channel->banlist == NULL) { + printformat(server, channel->visible_name, + MSGLEVEL_CLIENTNOTICE, + IRCTXT_NO_BANS, channel->visible_name); + return; + } + + /* show bans.. */ + counter = 1; + for (tmp = channel->banlist; tmp != NULL; tmp = tmp->next) { + BAN_REC *rec = tmp->data; + + printformat(server, channel->visible_name, MSGLEVEL_CRAP, + (rec->setby == NULL || *rec->setby == '\0') ? + IRCTXT_BANLIST : IRCTXT_BANLIST_LONG, + counter, channel->visible_name, + rec->ban, rec->setby, + (int) (time(NULL)-rec->time)); + counter++; + } +} + +/* SYNTAX: BAN [<channel>] [<nicks>] */ +static void cmd_ban(const char *data, IRC_SERVER_REC *server, + WI_ITEM_REC *item) +{ + IRC_CHANNEL_REC *chanrec; + char *channel, *nicks; + void *free_arg; + + CMD_IRC_SERVER(server); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN | + PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS, + item, &channel, &nicks)) + return; + + if (*nicks != '\0') { + /* setting ban - don't handle here */ + cmd_params_free(free_arg); + return; + } + + /* display bans */ + chanrec = IRC_CHANNEL(item); + if (chanrec == NULL && *channel == '\0') + cmd_param_error(CMDERR_NOT_JOINED); + + if (*channel != '\0' && g_strcmp0(channel, "*") != 0) + chanrec = irc_channel_find(server, channel); + + if (chanrec == NULL || !chanrec->synced) { + /* not joined to such channel or not yet synced, + ask ban lists from server */ + bans_ask_channel(channel, server, item); + } else { + bans_show_channel(chanrec, server); + } + + signal_stop(); + cmd_params_free(free_arg); +} + +/* SYNTAX: VER [<nick> | <channel> | *] */ +static void cmd_ver(gchar *data, IRC_SERVER_REC *server, WI_ITEM_REC *item) +{ + char *str; + + g_return_if_fail(data != NULL); + + CMD_IRC_SERVER(server); + if (*data == '\0' && !IS_QUERY(item)) + cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS); + + str = g_strdup_printf("%s VERSION", *data == '\0' ? + window_item_get_target(item) : data); + signal_emit("command ctcp", 3, str, server, item); + g_free(str); +} + +static void cmd_topic(const char *data, SERVER_REC *server, WI_ITEM_REC *item) +{ + CHANNEL_REC *channel; + char *timestr, *bynick, *byhost; + + g_return_if_fail(data != NULL); + + channel = *data != '\0' ? channel_find(server, data) : CHANNEL(item); + if (channel == NULL) return; + + printformat(server, channel->visible_name, MSGLEVEL_CRAP, + (channel->topic == NULL || *channel->topic == '\0') ? IRCTXT_NO_TOPIC : IRCTXT_TOPIC, + channel->visible_name, channel->topic); + + if (channel->topic_time > 0) { + byhost = strchr(channel->topic_by, '!'); + if (byhost == NULL) { + bynick = g_strdup(channel->topic_by); + byhost = ""; + } else { + bynick = g_strndup(channel->topic_by, + (int) (byhost-channel->topic_by)); + byhost++; + } + + timestr = my_asctime(channel->topic_time); + printformat(server, channel->visible_name, MSGLEVEL_CRAP, + IRCTXT_TOPIC_INFO, bynick, timestr, byhost); + g_free(timestr); + g_free(bynick); + } + signal_stop(); +} + +/* SYNTAX: TS */ +static void cmd_ts(const char *data) +{ + GSList *tmp; + + g_return_if_fail(data != NULL); + + for (tmp = channels; tmp != NULL; tmp = tmp->next) { + CHANNEL_REC *rec = tmp->data; + + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_TOPIC, + rec->visible_name, + rec->topic == NULL ? "" : rec->topic); + } +} + +typedef struct { + char *server_tag; + char *nick; +} OPER_PASS_REC; + +static void cmd_oper_got_pass(const char *password, OPER_PASS_REC *rec) +{ + SERVER_REC *server_rec = server_find_tag(rec->server_tag); + if (*password != '\0' && IS_IRC_SERVER(server_rec)) + irc_send_cmdv((IRC_SERVER_REC *) server_rec, "OPER %s %s", rec->nick, password); + g_free(rec->nick); + g_free(rec->server_tag); + g_free(rec); +} + +static void cmd_oper(const char *data, IRC_SERVER_REC *server) +{ + char *nick, *password, *format; + void *free_arg; + + g_return_if_fail(data != NULL); + if (!IS_IRC_SERVER(server) || !server->connected) + cmd_return_error(CMDERR_NOT_CONNECTED); + + if (!cmd_get_params(data, &free_arg, 2, &nick, &password)) + return; + if (*password == '\0') { + /* password not given, ask it. + irc/core handles the /OPER when password is given */ + OPER_PASS_REC *rec; + + rec = g_new(OPER_PASS_REC, 1); + rec->server_tag = g_strdup(server->tag); + rec->nick = g_strdup(*nick != '\0' ? nick : server->nick); + + format = format_get_text(MODULE_NAME, NULL, server, NULL, + IRCTXT_ASK_OPER_PASS); + + keyboard_entry_redirect((SIGNAL_FUNC) cmd_oper_got_pass, + format, + ENTRY_REDIRECT_FLAG_HIDDEN, rec); + g_free(format); + + signal_stop(); + } + + cmd_params_free(free_arg); +} + +/* SYNTAX: SETHOST <host> <password> (non-ircops) + SETHOST <ident> <host> (ircops) */ +static void cmd_sethost(const char *data, IRC_SERVER_REC *server) +{ + GSList *tmp; + + g_return_if_fail(data != NULL); + if (!IS_IRC_SERVER(server) || !server->connected) + cmd_return_error(CMDERR_NOT_CONNECTED); + + /* Save all the joined channels in server to window binds, since + the server will soon /PART + /JOIN us in all channels. */ + for (tmp = server->channels; tmp != NULL; tmp = tmp->next) { + CHANNEL_REC *channel = tmp->data; + + window_bind_add(window_item_window(channel), + server->tag, channel->visible_name); + } + + irc_send_cmdv(server, "SETHOST %s", data); +} + +void fe_irc_commands_init(void) +{ + command_bind_irc_last("me", NULL, (SIGNAL_FUNC) cmd_me); + command_bind_irc_last("action", NULL, (SIGNAL_FUNC) cmd_action); + command_bind_irc("notice", NULL, (SIGNAL_FUNC) cmd_notice); + command_bind_irc("ctcp", NULL, (SIGNAL_FUNC) cmd_ctcp); + command_bind_irc("nctcp", NULL, (SIGNAL_FUNC) cmd_nctcp); + command_bind_irc("wall", NULL, (SIGNAL_FUNC) cmd_wall); + command_bind_irc("ban", NULL, (SIGNAL_FUNC) cmd_ban); + command_bind_irc("ver", NULL, (SIGNAL_FUNC) cmd_ver); + command_bind_irc("topic", NULL, (SIGNAL_FUNC) cmd_topic); + command_bind_irc("ts", NULL, (SIGNAL_FUNC) cmd_ts); + command_bind_irc("oper", NULL, (SIGNAL_FUNC) cmd_oper); + command_bind_irc("sethost", NULL, (SIGNAL_FUNC) cmd_sethost); +} + +void fe_irc_commands_deinit(void) +{ + command_unbind("me", (SIGNAL_FUNC) cmd_me); + command_unbind("action", (SIGNAL_FUNC) cmd_action); + command_unbind("notice", (SIGNAL_FUNC) cmd_notice); + command_unbind("ctcp", (SIGNAL_FUNC) cmd_ctcp); + command_unbind("nctcp", (SIGNAL_FUNC) cmd_nctcp); + command_unbind("wall", (SIGNAL_FUNC) cmd_wall); + command_unbind("ban", (SIGNAL_FUNC) cmd_ban); + command_unbind("ver", (SIGNAL_FUNC) cmd_ver); + command_unbind("topic", (SIGNAL_FUNC) cmd_topic); + command_unbind("ts", (SIGNAL_FUNC) cmd_ts); + command_unbind("oper", (SIGNAL_FUNC) cmd_oper); + command_unbind("sethost", (SIGNAL_FUNC) cmd_sethost); +} diff --git a/src/fe-common/irc/fe-irc-messages.c b/src/fe-common/irc/fe-irc-messages.c new file mode 100644 index 0000000..5d820bb --- /dev/null +++ b/src/fe-common/irc/fe-irc-messages.c @@ -0,0 +1,373 @@ +/* + fe-irc-messages.c : irssi + + Copyright (C) 2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/signals.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/channels.h> +#include <irssi/src/core/ignore.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/irc/core/irc-servers.h> +#include <irssi/src/irc/core/irc-channels.h> +#include <irssi/src/irc/core/irc-queries.h> + +#include <irssi/src/fe-common/core/module-formats.h> +#include <irssi/src/fe-common/irc/module-formats.h> +#include <irssi/src/fe-common/core/printtext.h> +#include <irssi/src/fe-common/core/fe-messages.h> + +#include <irssi/src/fe-common/core/fe-queries.h> +#include <irssi/src/fe-common/core/hilight-text.h> +#include <irssi/src/fe-common/core/window-items.h> +#include <irssi/src/fe-common/irc/fe-irc-channels.h> +#include <irssi/src/fe-common/irc/fe-irc-server.h> + +static void sig_message_own_public(SERVER_REC *server, const char *msg, + const char *target, const char *origtarget) +{ + const char *oldtarget; + char *nickmode; + + if (!IS_IRC_SERVER(server)) + return; + oldtarget = target; + target = fe_channel_skip_prefix(IRC_SERVER(server), target); + if (target != oldtarget) { + /* Hybrid 6 / Bahamut feature, send msg to all + ops / ops+voices in channel */ + nickmode = channel_get_nickmode(channel_find(server, target), + server->nick); + + printformat_module("fe-common/core", server, target, + MSGLEVEL_PUBLIC | MSGLEVEL_NOHILIGHT | + MSGLEVEL_NO_ACT, + TXT_OWN_MSG_CHANNEL, + server->nick, oldtarget, msg, nickmode); + g_free(nickmode); + signal_stop(); + } + +} + +/* received msg to all ops in channel. + TODO: this code is a duplication of sig_message_public */ +static void sig_message_irc_op_public(SERVER_REC *server, const char *msg, + const char *nick, const char *address, + const char *target) +{ + CHANNEL_REC *chanrec; + char *nickmode, *optarget, *prefix, *color, *freemsg = NULL; + const char *cleantarget; + int for_me, level; + HILIGHT_REC *hilight; + TEXT_DEST_REC dest; + + /* only skip here so the difference can be stored in prefix */ + cleantarget = fe_channel_skip_prefix(IRC_SERVER(server), target); + prefix = g_strndup(target, cleantarget - target); + + /* and clean the rest here */ + cleantarget = get_visible_target(IRC_SERVER(server), cleantarget); + + chanrec = channel_find(server, cleantarget); + + nickmode = channel_get_nickmode(chanrec, nick); + + optarget = g_strconcat(prefix, cleantarget, NULL); + + /* Check for hilights */ + for_me = !settings_get_bool("hilight_nick_matches") ? FALSE : + !settings_get_bool("hilight_nick_matches_everywhere") ? + nick_match_msg(chanrec, msg, server->nick) : + nick_match_msg_everywhere(chanrec, msg, server->nick); + hilight = for_me ? NULL : + hilight_match_nick(server, cleantarget, nick, address, MSGLEVEL_PUBLIC, msg); + color = (hilight == NULL) ? NULL : hilight_get_color(hilight); + + level = MSGLEVEL_PUBLIC; + if (for_me) + level |= MSGLEVEL_HILIGHT; + + if (ignore_check_plus(server, nick, address, cleantarget, msg, &level, TRUE)) { + g_free(nickmode); + g_free(color); + g_free(optarget); + g_free(prefix); + return; + } + + if (level & MSGLEVEL_NOHILIGHT) { + for_me = FALSE; + g_free_and_null(color); + level &= ~MSGLEVEL_HILIGHT; + } + + if (settings_get_bool("emphasis")) + msg = freemsg = expand_emphasis((WI_ITEM_REC *) chanrec, msg); + + if (color != NULL) { + format_create_dest(&dest, server, cleantarget, level, NULL); + dest.address = address; + dest.nick = nick; + hilight_update_text_dest(&dest,hilight); + printformat_module_dest("fe-common/core", &dest, + TXT_PUBMSG_HILIGHT_CHANNEL, + color, nick, optarget, msg, nickmode); + } else { + printformat_module("fe-common/core", server, cleantarget, level, + for_me ? TXT_PUBMSG_ME_CHANNEL : TXT_PUBMSG_CHANNEL, + nick, optarget, msg, nickmode); + } + + g_free(nickmode); + g_free(freemsg); + g_free(color); + g_free(optarget); + g_free(prefix); +} + +static void sig_message_own_wall(SERVER_REC *server, const char *msg, + const char *target) +{ + char *nickmode, *optarget; + + nickmode = channel_get_nickmode(channel_find(server, target), + server->nick); + + /* this is always @, skip_prefix is not needed here */ + optarget = g_strconcat("@", target, NULL); + printformat_module("fe-common/core", server, target, + MSGLEVEL_PUBLIC | MSGLEVEL_NOHILIGHT | + MSGLEVEL_NO_ACT, + TXT_OWN_MSG_CHANNEL, + server->nick, optarget, msg, nickmode); + g_free(nickmode); + g_free(optarget); +} + +static void sig_message_own_action(IRC_SERVER_REC *server, const char *msg, + const char *target) +{ + void *item; + const char *oldtarget; + char *freemsg = NULL; + + oldtarget = target; + target = fe_channel_skip_prefix(IRC_SERVER(server), target); + if (server_ischannel(SERVER(server), target)) + item = channel_find(SERVER(server), target); + else + item = irc_query_find(server, target); + + if (settings_get_bool("emphasis")) + msg = freemsg = expand_emphasis(item, msg); + + printformat(server, target, + MSGLEVEL_ACTIONS | MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT | + (server_ischannel(SERVER(server), target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS), + item != NULL && oldtarget == target ? IRCTXT_OWN_ACTION : IRCTXT_OWN_ACTION_TARGET, + server->nick, msg, oldtarget); + g_free_not_null(freemsg); +} + +static void sig_message_irc_action(IRC_SERVER_REC *server, const char *msg, + const char *nick, const char *address, + const char *target) +{ + void *item; + const char *oldtarget; + char *freemsg = NULL; + int level; + int own = FALSE; + + oldtarget = target; + target = fe_channel_skip_prefix(IRC_SERVER(server), target); + + level = MSGLEVEL_ACTIONS | + (server_ischannel(SERVER(server), target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS); + + if (ignore_check_plus(SERVER(server), nick, address, target, msg, &level, TRUE)) + return; + + if (server_ischannel(SERVER(server), target)) { + item = channel_find(SERVER(server), target); + } else { + own = (!g_strcmp0(nick, server->nick)); + item = privmsg_get_query(SERVER(server), own ? target : nick, FALSE, level); + } + + if (settings_get_bool("emphasis")) + msg = freemsg = expand_emphasis(item, msg); + + if (server_ischannel(SERVER(server), target)) { + /* channel action */ + if (window_item_is_active(item) && target == oldtarget) { + /* message to active channel in window */ + printformat(server, target, level, + IRCTXT_ACTION_PUBLIC, nick, msg); + } else { + /* message to not existing/active channel, or to @/+ */ + printformat(server, target, level, + IRCTXT_ACTION_PUBLIC_CHANNEL, + nick, oldtarget, msg); + } + } else { + if (own) { + /* own action bounced */ + printformat(server, target, + MSGLEVEL_ACTIONS | MSGLEVEL_MSGS, + item != NULL && oldtarget == target ? IRCTXT_OWN_ACTION : IRCTXT_OWN_ACTION_TARGET, + server->nick, msg, oldtarget); + } else { + /* private action */ + printformat(server, nick, MSGLEVEL_ACTIONS | MSGLEVEL_MSGS, + item == NULL ? IRCTXT_ACTION_PRIVATE : + IRCTXT_ACTION_PRIVATE_QUERY, + nick, address == NULL ? "" : address, msg); + } + } + + g_free_not_null(freemsg); +} + +static char *notice_channel_context(SERVER_REC *server, const char *msg) +{ + if (!settings_get_bool("notice_channel_context")) + return NULL; + + if (*msg == '[') { + char *end, *channel; + end = strpbrk(msg, " ,]"); + if (end != NULL && *end == ']') { + channel = g_strndup(msg + 1, end - msg - 1); + if (server_ischannel(server, channel)) { + return channel; + } + g_free(channel); + } + } + return NULL; +} + +static void sig_message_own_notice(IRC_SERVER_REC *server, const char *msg, const char *target) +{ + char *channel; + /* check if this is a cnotice */ + channel = notice_channel_context((SERVER_REC *) server, msg); + printformat(server, channel != NULL ? channel : fe_channel_skip_prefix(server, target), + MSGLEVEL_NOTICES | MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT, IRCTXT_OWN_NOTICE, + target, msg); + g_free(channel); +} + +static void sig_message_irc_notice(SERVER_REC *server, const char *msg, + const char *nick, const char *address, + const char *target) +{ + const char *oldtarget; + int level = MSGLEVEL_NOTICES; + + oldtarget = target; + target = fe_channel_skip_prefix(IRC_SERVER(server), target); + + if (address == NULL || *address == '\0') { + level = MSGLEVEL_SNOTES; + /* notice from server */ + if (!ignore_check_plus(server, nick, "", + target, msg, &level, TRUE)) { + printformat(server, target, level, + IRCTXT_NOTICE_SERVER, nick, msg); + } + return; + } + + if (ignore_check_plus(server, nick, address, + server_ischannel(SERVER(server), target) ? target : NULL, + msg, &level, TRUE)) + return; + + if (server_ischannel(SERVER(server), target)) { + /* notice in some channel */ + printformat(server, target, level, + IRCTXT_NOTICE_PUBLIC, nick, oldtarget, msg); + } else { + char *channel; + /* check if this is a cnotice */ + channel = notice_channel_context(server, msg); + + if (channel == NULL) { + /* private notice */ + privmsg_get_query(SERVER(server), nick, FALSE, MSGLEVEL_NOTICES); + } + printformat(server, channel == NULL ? nick : channel, level, IRCTXT_NOTICE_PRIVATE, + nick, address, msg); + + g_free(channel); + } +} + +static void sig_message_own_ctcp(IRC_SERVER_REC *server, const char *cmd, + const char *data, const char *target) +{ + printformat(server, fe_channel_skip_prefix(server, target), MSGLEVEL_CTCPS | + MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT, + IRCTXT_OWN_CTCP, target, cmd, data); +} + +static void sig_message_irc_ctcp(IRC_SERVER_REC *server, const char *cmd, + const char *data, const char *nick, + const char *addr, const char *target) +{ + const char *oldtarget; + + oldtarget = target; + target = fe_channel_skip_prefix(server, target); + printformat(server, server_ischannel(SERVER(server), target) ? target : nick, MSGLEVEL_CTCPS, + IRCTXT_CTCP_REQUESTED, nick, addr, cmd, data, oldtarget); +} + +void fe_irc_messages_init(void) +{ + settings_add_bool("misc", "notice_channel_context", TRUE); + + signal_add_last("message own_public", (SIGNAL_FUNC) sig_message_own_public); + signal_add_last("message irc op_public", (SIGNAL_FUNC) sig_message_irc_op_public); + signal_add_last("message irc own_wall", (SIGNAL_FUNC) sig_message_own_wall); + signal_add_last("message irc own_action", (SIGNAL_FUNC) sig_message_own_action); + signal_add_last("message irc action", (SIGNAL_FUNC) sig_message_irc_action); + signal_add_last("message irc own_notice", (SIGNAL_FUNC) sig_message_own_notice); + signal_add_last("message irc notice", (SIGNAL_FUNC) sig_message_irc_notice); + signal_add_last("message irc own_ctcp", (SIGNAL_FUNC) sig_message_own_ctcp); + signal_add_last("message irc ctcp", (SIGNAL_FUNC) sig_message_irc_ctcp); +} + +void fe_irc_messages_deinit(void) +{ + signal_remove("message own_public", (SIGNAL_FUNC) sig_message_own_public); + signal_remove("message irc op_public", (SIGNAL_FUNC) sig_message_irc_op_public); + signal_remove("message irc own_wall", (SIGNAL_FUNC) sig_message_own_wall); + signal_remove("message irc own_action", (SIGNAL_FUNC) sig_message_own_action); + signal_remove("message irc action", (SIGNAL_FUNC) sig_message_irc_action); + signal_remove("message irc own_notice", (SIGNAL_FUNC) sig_message_own_notice); + signal_remove("message irc notice", (SIGNAL_FUNC) sig_message_irc_notice); + signal_remove("message irc own_ctcp", (SIGNAL_FUNC) sig_message_own_ctcp); + signal_remove("message irc ctcp", (SIGNAL_FUNC) sig_message_irc_ctcp); +} diff --git a/src/fe-common/irc/fe-irc-queries.c b/src/fe-common/irc/fe-irc-queries.c new file mode 100644 index 0000000..7a13542 --- /dev/null +++ b/src/fe-common/irc/fe-irc-queries.c @@ -0,0 +1,101 @@ +/* + fe-irc-queries.c : irssi + + Copyright (C) 2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/signals.h> +#include <irssi/src/core/settings.h> +#include <irssi/src/core/servers.h> +#include <irssi/src/core/queries.h> +#include <irssi/src/core/nicklist.h> + +static QUERY_REC *query_find_address(SERVER_REC *server, const char *address) +{ + GSList *tmp; + + g_return_val_if_fail(IS_SERVER(server), NULL); + + for (tmp = server->queries; tmp != NULL; tmp = tmp->next) { + QUERY_REC *rec = tmp->data; + + if (*rec->name != '=' && rec->address != NULL && + g_ascii_strcasecmp(address, rec->address) == 0) + return rec; + } + + return NULL; +} + +static int server_has_nick(SERVER_REC *server, const char *nick) +{ + GSList *tmp; + + for (tmp = server->channels; tmp != NULL; tmp = tmp->next) { + CHANNEL_REC *channel = tmp->data; + + if (nicklist_find(channel, nick) != NULL) + return TRUE; + } + + return FALSE; +} + +static void event_privmsg(SERVER_REC *server, const char *data, + const char *nick, const char *address) +{ + QUERY_REC *query; + + g_return_if_fail(data != NULL); + + if (nick == NULL || address == NULL || server_ischannel(server, data) || + !settings_get_bool("query_track_nick_changes")) + return; + + query = query_find(server, nick); + if (query == NULL) { + /* check if there's query with another nick from the same + address. it was probably a nick change or reconnect to + server, so rename the query. */ + query = query_find_address(server, address); + if (query != NULL) { + /* make sure the old nick doesn't exist anymore */ + if (!server_has_nick(server, query->name)) + query_change_nick(query, nick); + } + } else { + /* process the changes to the query structure now, before the + * privmsg is dispatched. */ + if (g_strcmp0(query->name, nick) != 0) + query_change_nick(query, nick); + if (address != NULL && g_strcmp0(query->address, address) != 0) + query_change_address(query, address); + } +} + +void fe_irc_queries_init(void) +{ + settings_add_bool("lookandfeel", "query_track_nick_changes", TRUE); + + signal_add_first("event privmsg", (SIGNAL_FUNC) event_privmsg); +} + +void fe_irc_queries_deinit(void) +{ + signal_remove("event privmsg", (SIGNAL_FUNC) event_privmsg); +} diff --git a/src/fe-common/irc/fe-irc-server.c b/src/fe-common/irc/fe-irc-server.c new file mode 100644 index 0000000..4aad7ee --- /dev/null +++ b/src/fe-common/irc/fe-irc-server.c @@ -0,0 +1,201 @@ +/* + fe-irc-server.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/irc/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/misc.h> + +#include <irssi/src/core/servers-setup.h> + +#include <irssi/src/core/levels.h> +#include <irssi/src/irc/core/irc-chatnets.h> +#include <irssi/src/irc/core/irc-servers.h> +#include <irssi/src/irc/core/irc-channels.h> +#include <irssi/src/core/servers-reconnect.h> +#include <irssi/src/irc/core/irc-servers-setup.h> + +#include <irssi/src/fe-common/core/fe-windows.h> +#include <irssi/src/fe-common/core/printtext.h> + +const char *get_visible_target(IRC_SERVER_REC *server, const char *target) +{ + IRC_CHANNEL_REC *channel; + + if (*target == '!') { + /* visible_name of !channels is different - don't bother + checking other types for now, they'll just slow up */ + channel = irc_channel_find(server, target); + if (channel != NULL) + return channel->visible_name; + } + + return target; +} + +/* SYNTAX: SERVER ADD|MODIFY [-4 | -6] [-cap | -nocap] [-tls_cert <cert>] [-tls_pkey <pkey>] + [-tls_pass <password>] [-tls_verify] [-tls_cafile <cafile>] + [-tls_capath <capath>] [-tls_ciphers <list>] [-tls | -notls] + [-starttls | -nostarttls | -disallow_starttls | -nodisallow_starttls] + [-auto | -noauto] [-network <network>] [-host <hostname>] + [-cmdspeed <ms>] [-cmdmax <count>] [-port <port>] <address> [<port> + [<password>]] */ +/* NOTE: -network replaces the old -ircnet flag. */ +static void sig_server_add_fill(IRC_SERVER_SETUP_REC *rec, + GHashTable *optlist) +{ + IRC_CHATNET_REC *ircnet; + char *value; + + value = g_hash_table_lookup(optlist, "network"); + /* For backwards compatibility, also allow the old name 'ircnet'. + But of course only if -network was not given. */ + if (!value) + value = g_hash_table_lookup(optlist, "ircnet"); + + if (value != NULL) { + g_free_and_null(rec->chatnet); + if (*value != '\0') { + ircnet = ircnet_find(value); + rec->chatnet = ircnet != NULL ? + g_strdup(ircnet->name) : g_strdup(value); + } + } + + value = g_hash_table_lookup(optlist, "cmdspeed"); + if (value != NULL && *value != '\0') rec->cmd_queue_speed = atoi(value); + value = g_hash_table_lookup(optlist, "cmdmax"); + if (value != NULL && *value != '\0') rec->max_cmds_at_once = atoi(value); + value = g_hash_table_lookup(optlist, "querychans"); + if (value != NULL && *value != '\0') rec->max_query_chans = atoi(value); + if (g_hash_table_lookup(optlist, "nodisallow_starttls") || + g_hash_table_lookup(optlist, "nostarttls")) + rec->starttls = STARTTLS_NOTSET; + if (g_hash_table_lookup(optlist, "disallow_starttls")) + rec->starttls = STARTTLS_DISALLOW; + if (g_hash_table_lookup(optlist, "starttls")) { + rec->starttls = STARTTLS_ENABLED; + rec->use_tls = 0; + } + if (g_hash_table_lookup(optlist, "nocap")) + rec->no_cap = 1; + if (g_hash_table_lookup(optlist, "cap")) + rec->no_cap = 0; +} + +static void sig_server_waiting_info(IRC_SERVER_REC *server, const char *version) +{ + if (!IS_IRC_SERVER(server)) + return; + + printformat(server, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_SERVER_WAITING_CAP_LS, server, + version); +} + +/* SYNTAX: SERVER LIST */ +static void cmd_server_list(const char *data) +{ + GString *str; + GSList *tmp; + + str = g_string_new(NULL); + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_SETUPSERVER_HEADER); + for (tmp = setupservers; tmp != NULL; tmp = tmp->next) { + IRC_SERVER_SETUP_REC *rec = tmp->data; + + if (!IS_IRC_SERVER_SETUP(rec)) + continue; + + g_string_truncate(str, 0); + if (rec->password != NULL) + g_string_append(str, "(pass), "); + if (rec->autoconnect) + g_string_append(str, "autoconnect, "); + if (rec->no_proxy) + g_string_append(str, "noproxy, "); + if (rec->no_cap) + g_string_append(str, "nocap, "); + if (rec->starttls == STARTTLS_DISALLOW) + g_string_append(str, "disallow_starttls, "); + if (rec->starttls == STARTTLS_ENABLED) + g_string_append(str, "starttls, "); + if (rec->use_tls) + g_string_append(str, "tls, "); + if (rec->tls_cert) { + g_string_append_printf(str, "tls_cert: %s, ", rec->tls_cert); + if (rec->tls_pkey) + g_string_append_printf(str, "tls_pkey: %s, ", rec->tls_pkey); + if (rec->tls_pass) + g_string_append_printf(str, "(pass), "); + } + if (!rec->tls_verify) + g_string_append(str, "notls_verify, "); + if (rec->tls_cafile) + g_string_append_printf(str, "tls_cafile: %s, ", rec->tls_cafile); + if (rec->tls_capath) + g_string_append_printf(str, "tls_capath: %s, ", rec->tls_capath); + if (rec->tls_ciphers) + g_string_append_printf(str, "tls_ciphers: %s, ", rec->tls_ciphers); + if (rec->tls_pinned_cert) + g_string_append_printf(str, "tls_pinned_cert: %s, ", rec->tls_pinned_cert); + if (rec->tls_pinned_pubkey) + g_string_append_printf(str, "tls_pinned_pubkey: %s, ", + rec->tls_pinned_pubkey); + + if (rec->max_cmds_at_once > 0) + g_string_append_printf(str, "cmdmax: %d, ", rec->max_cmds_at_once); + if (rec->cmd_queue_speed > 0) + g_string_append_printf(str, "cmdspeed: %d, ", rec->cmd_queue_speed); + if (rec->max_query_chans > 0) + g_string_append_printf(str, "querychans: %d, ", rec->max_query_chans); + if (rec->own_host != NULL) + g_string_append_printf(str, "host: %s, ", rec->own_host); + + if (str->len > 1) g_string_truncate(str, str->len-2); + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_SETUPSERVER_LINE, + rec->address, rec->port, + rec->chatnet == NULL ? "" : rec->chatnet, + str->str); + } + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_SETUPSERVER_FOOTER); + g_string_free(str, TRUE); +} + +void fe_irc_server_init(void) +{ + signal_add("server add fill", (SIGNAL_FUNC) sig_server_add_fill); + signal_add("server waiting cap ls", (SIGNAL_FUNC) sig_server_waiting_info); + command_bind("server list", NULL, (SIGNAL_FUNC) cmd_server_list); + + command_set_options("server add", + "-ircnet -network -cmdspeed -cmdmax -querychans starttls " + "nostarttls disallow_starttls nodisallow_starttls cap nocap"); + command_set_options("server modify", + "-ircnet -network -cmdspeed -cmdmax -querychans starttls nostarttls " + "disallow_starttls nodisallow_starttls cap nocap"); +} + +void fe_irc_server_deinit(void) +{ + signal_remove("server add fill", (SIGNAL_FUNC) sig_server_add_fill); + signal_remove("server waiting cap ls", (SIGNAL_FUNC) sig_server_waiting_info); + command_unbind("server list", (SIGNAL_FUNC) cmd_server_list); +} diff --git a/src/fe-common/irc/fe-irc-server.h b/src/fe-common/irc/fe-irc-server.h new file mode 100644 index 0000000..28a709e --- /dev/null +++ b/src/fe-common/irc/fe-irc-server.h @@ -0,0 +1,9 @@ +#ifndef IRSSI_FE_COMMON_IRC_FE_IRC_SERVER_H +#define IRSSI_FE_COMMON_IRC_FE_IRC_SERVER_H + +const char *get_visible_target(IRC_SERVER_REC *server, const char *target); + +void fe_irc_server_init(void); +void fe_irc_server_deinit(void); + +#endif diff --git a/src/fe-common/irc/fe-ircnet.c b/src/fe-common/irc/fe-ircnet.c new file mode 100644 index 0000000..3484d92 --- /dev/null +++ b/src/fe-common/irc/fe-ircnet.c @@ -0,0 +1,249 @@ +/* + fe-ircnet.c : irssi + + Copyright (C) 2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/irc/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/chatnets.h> + +#include <irssi/src/irc/core/irc-servers.h> +#include <irssi/src/irc/core/irc-chatnets.h> +#include <irssi/src/fe-common/core/printtext.h> +#include <irssi/src/core/servers-setup.h> +#include <irssi/src/core/channels-setup.h> + +static void cmd_network_list(void) +{ + GString *str; + GSList *tmp; + + str = g_string_new(NULL); + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_NETWORK_HEADER); + for (tmp = chatnets; tmp != NULL; tmp = tmp->next) { + IRC_CHATNET_REC *rec = tmp->data; + + if (!IS_IRCNET(rec)) + continue; + + g_string_truncate(str, 0); + if (rec->nick != NULL) + g_string_append_printf(str, "nick: %s, ", rec->nick); + if (rec->alternate_nick != NULL) + g_string_append_printf(str, "alternate_nick: %s, ", rec->alternate_nick); + if (rec->username != NULL) + g_string_append_printf(str, "username: %s, ", rec->username); + if (rec->realname != NULL) + g_string_append_printf(str, "realname: %s, ", rec->realname); + if (rec->own_host != NULL) + g_string_append_printf(str, "host: %s, ", rec->own_host); + if (rec->autosendcmd != NULL) + g_string_append_printf(str, "autosendcmd: %s, ", rec->autosendcmd); + if (rec->usermode != NULL) + g_string_append_printf(str, "usermode: %s, ", rec->usermode); + if (rec->sasl_mechanism != NULL) + g_string_append_printf(str, "sasl_mechanism: %s, ", rec->sasl_mechanism); + if (rec->sasl_username != NULL) + g_string_append_printf(str, "sasl_username: %s, ", rec->sasl_username); + if (rec->sasl_password != NULL) + g_string_append_printf(str, "sasl_password: (pass), "); + if (rec->cmd_queue_speed > 0) + g_string_append_printf(str, "cmdspeed: %d, ", rec->cmd_queue_speed); + if (rec->max_cmds_at_once > 0) + g_string_append_printf(str, "cmdmax: %d, ", rec->max_cmds_at_once); + if (rec->max_query_chans > 0) + g_string_append_printf(str, "querychans: %d, ", rec->max_query_chans); + + if (rec->max_kicks > 0) + g_string_append_printf(str, "max_kicks: %d, ", rec->max_kicks); + if (rec->max_msgs > 0) + g_string_append_printf(str, "max_msgs: %d, ", rec->max_msgs); + if (rec->max_modes > 0) + g_string_append_printf(str, "max_modes: %d, ", rec->max_modes); + if (rec->max_whois > 0) + g_string_append_printf(str, "max_whois: %d, ", rec->max_whois); + + if (str->len > 1) g_string_truncate(str, str->len-2); + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, + IRCTXT_NETWORK_LINE, rec->name, str->str); + } + g_string_free(str, TRUE); + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_NETWORK_FOOTER); +} + +static void cmd_network_add_modify(const char *data, gboolean add) +{ + GHashTable *optlist; + char *name, *value; + void *free_arg; + IRC_CHATNET_REC *rec; + + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS, + "network add", &optlist, &name)) + return; + + if (*name == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + rec = ircnet_find(name); + if (rec == NULL) { + if (add == FALSE) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + IRCTXT_NETWORK_NOT_FOUND, name); + cmd_params_free(free_arg); + return; + } + + rec = g_new0(IRC_CHATNET_REC, 1); + rec->name = g_strdup(name); + } else { + if (g_hash_table_lookup(optlist, "nick")) g_free_and_null(rec->nick); + if (g_hash_table_lookup(optlist, "alternate_nick")) g_free_and_null(rec->alternate_nick); + if (g_hash_table_lookup(optlist, "user")) g_free_and_null(rec->username); + if (g_hash_table_lookup(optlist, "realname")) g_free_and_null(rec->realname); + if (g_hash_table_lookup(optlist, "host")) { + g_free_and_null(rec->own_host); + rec->own_ip4 = rec->own_ip6 = NULL; + } + if (g_hash_table_lookup(optlist, "usermode")) g_free_and_null(rec->usermode); + if (g_hash_table_lookup(optlist, "autosendcmd")) g_free_and_null(rec->autosendcmd); + if (g_hash_table_lookup(optlist, "sasl_mechanism")) g_free_and_null(rec->sasl_mechanism); + if (g_hash_table_lookup(optlist, "sasl_username")) g_free_and_null(rec->sasl_username); + if (g_hash_table_lookup(optlist, "sasl_password")) g_free_and_null(rec->sasl_password); + } + + value = g_hash_table_lookup(optlist, "kicks"); + if (value != NULL) rec->max_kicks = atoi(value); + value = g_hash_table_lookup(optlist, "msgs"); + if (value != NULL) rec->max_msgs = atoi(value); + value = g_hash_table_lookup(optlist, "modes"); + if (value != NULL) rec->max_modes = atoi(value); + value = g_hash_table_lookup(optlist, "whois"); + if (value != NULL) rec->max_whois = atoi(value); + + value = g_hash_table_lookup(optlist, "cmdspeed"); + if (value != NULL) rec->cmd_queue_speed = atoi(value); + value = g_hash_table_lookup(optlist, "cmdmax"); + if (value != NULL) rec->max_cmds_at_once = atoi(value); + value = g_hash_table_lookup(optlist, "querychans"); + if (value != NULL) rec->max_query_chans = atoi(value); + + value = g_hash_table_lookup(optlist, "nick"); + if (value != NULL && *value != '\0') rec->nick = g_strdup(value); + value = g_hash_table_lookup(optlist, "alternate_nick"); + if (value != NULL && *value != '\0') rec->alternate_nick = g_strdup(value); + value = g_hash_table_lookup(optlist, "user"); + if (value != NULL && *value != '\0') rec->username = g_strdup(value); + value = g_hash_table_lookup(optlist, "realname"); + if (value != NULL && *value != '\0') rec->realname = g_strdup(value); + + value = g_hash_table_lookup(optlist, "host"); + if (value != NULL && *value != '\0') { + rec->own_host = g_strdup(value); + rec->own_ip4 = rec->own_ip6 = NULL; + } + + value = g_hash_table_lookup(optlist, "usermode"); + if (value != NULL && *value != '\0') rec->usermode = g_strdup(value); + value = g_hash_table_lookup(optlist, "autosendcmd"); + if (value != NULL && *value != '\0') rec->autosendcmd = g_strdup(value); + + /* the validity of the parameters is checked in sig_server_setup_fill_chatnet */ + value = g_hash_table_lookup(optlist, "sasl_mechanism"); + if (value != NULL) rec->sasl_mechanism = *value != '\0' ? g_strdup(value) : NULL; + value = g_hash_table_lookup(optlist, "sasl_username"); + if (value != NULL) rec->sasl_username = *value != '\0' ? g_strdup(value) : NULL; + value = g_hash_table_lookup(optlist, "sasl_password"); + if (value != NULL) rec->sasl_password = *value != '\0' ? g_strdup(value) : NULL; + + ircnet_create(rec); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_NETWORK_ADDED, name); + + cmd_params_free(free_arg); +} + +/* SYNTAX: NETWORK ADD|MODIFY [-nick <nick>] [-alternate_nick <nick>] [-user <user>] [-realname <name>] + [-host <host>] [-usermode <mode>] [-autosendcmd <cmd>] + [-querychans <count>] [-whois <count>] [-msgs <count>] + [-kicks <count>] [-modes <count>] [-cmdspeed <ms>] + [-cmdmax <count>] [-sasl_mechanism <mechanism>] + [-sasl_username <username>] [-sasl_password <password>] + <name> */ +static void cmd_network_add(const char *data) +{ + cmd_network_add_modify(data, TRUE); +} + +static void cmd_network_modify(const char *data) +{ + cmd_network_add_modify(data, FALSE); +} + +/* SYNTAX: NETWORK REMOVE <network> */ +static void cmd_network_remove(const char *data) +{ + IRC_CHATNET_REC *rec; + + if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS); + + rec = ircnet_find(data); + if (rec == NULL) + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_NETWORK_NOT_FOUND, data); + else { + server_setup_remove_chatnet(data); + channel_setup_remove_chatnet(data); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_NETWORK_REMOVED, data); + chatnet_remove(CHATNET(rec)); + } +} + +static void cmd_network(const char *data, SERVER_REC *server, WI_ITEM_REC *item) +{ + if (*data == '\0') + cmd_network_list(); + else + command_runsub("network", data, server, item); +} + +void fe_ircnet_init(void) +{ + command_bind("ircnet", NULL, (SIGNAL_FUNC) cmd_network); + command_bind("network", NULL, (SIGNAL_FUNC) cmd_network); + command_bind("network list", NULL, (SIGNAL_FUNC) cmd_network_list); + command_bind("network add", NULL, (SIGNAL_FUNC) cmd_network_add); + command_bind("network modify", NULL, (SIGNAL_FUNC) cmd_network_modify); + command_bind("network remove", NULL, (SIGNAL_FUNC) cmd_network_remove); + + command_set_options("network add", "-kicks -msgs -modes -whois -cmdspeed " + "-cmdmax -nick -alternate_nick -user -realname -host -autosendcmd -querychans -usermode -sasl_mechanism -sasl_username -sasl_password"); + command_set_options("network modify", "-kicks -msgs -modes -whois -cmdspeed " + "-cmdmax -nick -alternate_nick -user -realname -host -autosendcmd -querychans -usermode -sasl_mechanism -sasl_username -sasl_password"); +} + +void fe_ircnet_deinit(void) +{ + command_unbind("ircnet", (SIGNAL_FUNC) cmd_network); + command_unbind("network", (SIGNAL_FUNC) cmd_network); + command_unbind("network list", (SIGNAL_FUNC) cmd_network_list); + command_unbind("network add", (SIGNAL_FUNC) cmd_network_add); + command_unbind("network modify", (SIGNAL_FUNC) cmd_network_modify); + command_unbind("network remove", (SIGNAL_FUNC) cmd_network_remove); +} diff --git a/src/fe-common/irc/fe-modes.c b/src/fe-common/irc/fe-modes.c new file mode 100644 index 0000000..623fca0 --- /dev/null +++ b/src/fe-common/irc/fe-modes.c @@ -0,0 +1,236 @@ +/* + fe-modes.c : irssi + + Copyright (C) 2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/irc/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/irc/core/irc-servers.h> +#include <irssi/src/irc/core/irc-channels.h> +#include <irssi/src/irc/core/modes.h> +#include <irssi/src/core/ignore.h> + +#include <irssi/src/fe-common/core/printtext.h> + +#define MODE_WAIT_TIME 3 /* how many seconds to wait for identical modes */ + +typedef struct { + IRC_CHANNEL_REC *channel; + int level; + char *mode; + GSList *nicks; + time_t last_mode; +} MODE_REC; + +static int mode_tag, group_multi_mode; +static GSList *modes; + +static MODE_REC *mode_find_channel(IRC_CHANNEL_REC *channel) +{ + GSList *tmp; + + g_return_val_if_fail(channel != NULL, NULL); + + for (tmp = modes; tmp != NULL; tmp = tmp->next) { + MODE_REC *rec = tmp->data; + + if (rec->channel == channel) + return rec; + } + + return NULL; +} + +static void mode_destroy(MODE_REC *mode) +{ + g_return_if_fail(mode != NULL); + + modes = g_slist_remove(modes, mode); + g_slist_foreach(mode->nicks, (GFunc) g_free, NULL); + g_slist_free(mode->nicks); + g_free(mode->mode); + g_free(mode); +} + +static void print_mode(MODE_REC *rec) +{ + GSList *tmp; + char *nicks; + + if (g_slist_find(channels, rec->channel) == NULL) { + /* channel was destroyed while we were waiting.. */ + return; + } + + tmp = modes; modes = NULL; + + nicks = i_slist_to_string(rec->nicks, ", "); + printformat(rec->channel->server, rec->channel->visible_name, rec->level, + IRCTXT_CHANMODE_CHANGE, rec->channel->visible_name, rec->mode, nicks, ""); + g_free(nicks); + + modes = tmp; +} + +/* something is going to be printed to screen, print our current netsplit + message before it. */ +static void sig_print_starting(void) +{ + while (modes != NULL) { + print_mode(modes->data); + mode_destroy(modes->data); + } + + signal_remove("print starting", sig_print_starting); +} + +static int sig_check_modes(void) +{ + GSList *tmp, *next; + + if (modes == NULL) + return 1; + + for (tmp = modes; tmp != NULL; tmp = next) { + MODE_REC *rec = tmp->data; + + next = tmp->next; + if (time(NULL)-rec->last_mode >= MODE_WAIT_TIME) { + print_mode(rec); + mode_destroy(rec); + } + } + + if (modes == NULL) + signal_remove("print starting", (SIGNAL_FUNC) sig_print_starting); + return 1; +} + +static void msg_multi_mode(IRC_CHANNEL_REC *channel, int level, const char *sender, + const char *addr, const char *mode) +{ + MODE_REC *rec; + + if (modes == NULL) + signal_add("print starting", (SIGNAL_FUNC) sig_print_starting); + + rec = mode_find_channel(channel); + if (rec != NULL && g_strcmp0(rec->mode, mode) != 0) { + /* different mode than last time, show and remove the old */ + print_mode(rec); + mode_destroy(rec); + rec = NULL; + } + + if (rec == NULL) { + /* no previous mode, create new */ + rec = g_new0(MODE_REC, 1); + modes = g_slist_append(modes, rec); + + rec->level = level; + rec->channel = channel; + rec->mode = g_strdup(mode); + } + /* the levels (everything below MSGLEVEL_ALL) are combined (|) + whereas the flags (anything above) must all match (&) */ + rec->level = ((rec->level | level) & MSGLEVEL_ALL) | (rec->level & level); + rec->nicks = g_slist_append(rec->nicks, g_strdup(sender)); + rec->last_mode = time(NULL); + + signal_stop(); +} + +/* FIXME: should be moved to fe-irc-messages.c.. */ +static void sig_message_mode(IRC_SERVER_REC *server, const char *channel, + const char *nick, const char *addr, + const char *mode) +{ + int level = MSGLEVEL_MODES; + + if (nick == NULL) nick = server->real_address; + + if (ignore_check_plus(SERVER(server), nick, addr, channel, + mode, &level, TRUE)) + return; + + if (!server_ischannel(SERVER(server), channel)) { + /* user mode change */ + printformat(server, NULL, level, + IRCTXT_USERMODE_CHANGE, mode, channel); + } else if (addr == NULL) { + /* channel mode changed by server */ + printformat(server, channel, level, + IRCTXT_SERVER_CHANMODE_CHANGE, + channel, mode, nick); + } else { + /* channel mode changed by normal user */ + IRC_CHANNEL_REC *chanrec; + + chanrec = !group_multi_mode ? NULL : + irc_channel_find(server, channel); + + if (chanrec != NULL && g_ascii_strcasecmp(nick, server->nick) != 0) + msg_multi_mode(chanrec, level, nick, addr, mode); + else { + printformat(server, channel, level, + IRCTXT_CHANMODE_CHANGE, + channel, mode, nick, addr); + } + } +} + +static void read_settings(void) +{ + int old_group; + + old_group = group_multi_mode; + group_multi_mode = settings_get_bool("group_multi_mode"); + + if (old_group && !group_multi_mode) { + g_source_remove(mode_tag); + mode_tag = -1; + } else if (!old_group && group_multi_mode) { + mode_tag = g_timeout_add(1000, (GSourceFunc) sig_check_modes, NULL); + } +} + +void fe_modes_init(void) +{ + settings_add_bool("misc", "group_multi_mode", TRUE); + mode_tag = -1; + + read_settings(); + signal_add("message irc mode", (SIGNAL_FUNC) sig_message_mode); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); +} + +void fe_modes_deinit(void) +{ + if (mode_tag != -1) + g_source_remove(mode_tag); + + signal_remove("message irc mode", (SIGNAL_FUNC) sig_message_mode); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); + + signal_remove("print starting", (SIGNAL_FUNC) sig_print_starting); +} diff --git a/src/fe-common/irc/fe-netjoin.c b/src/fe-common/irc/fe-netjoin.c new file mode 100644 index 0000000..ea41f7f --- /dev/null +++ b/src/fe-common/irc/fe-netjoin.c @@ -0,0 +1,515 @@ +/* + fe-netjoin.c : irssi + + Copyright (C) 2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/irc/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/irc/core/irc-servers.h> +#include <irssi/src/irc/core/modes.h> +#include <irssi/src/core/ignore.h> +#include <irssi/src/irc/core/netsplit.h> + +#include <irssi/src/fe-common/core/printtext.h> + +#define NETJOIN_WAIT_TIME 5 /* how many seconds to wait for the netsplitted JOIN messages to stop */ +#define NETJOIN_MAX_WAIT 30 /* how many seconds to wait for nick to join to the rest of the channels she was before the netsplit */ + +typedef struct { + char *nick; + GSList *old_channels; + GSList *now_channels; +} NETJOIN_REC; + +typedef struct { + IRC_SERVER_REC *server; + time_t last_netjoin; + + GSList *netjoins; +} NETJOIN_SERVER_REC; + +typedef struct { + int count; + GString *nicks; +} TEMP_PRINT_REC; + +static int join_tag; +static int netjoin_max_nicks, hide_netsplit_quits; +static int printing_joins; +static GSList *joinservers; + +static NETJOIN_SERVER_REC *netjoin_find_server(IRC_SERVER_REC *server) +{ + GSList *tmp; + + g_return_val_if_fail(server != NULL, NULL); + + for (tmp = joinservers; tmp != NULL; tmp = tmp->next) { + NETJOIN_SERVER_REC *rec = tmp->data; + + if (rec->server == server) + return rec; + } + + return NULL; +} + +static NETJOIN_REC *netjoin_add(IRC_SERVER_REC *server, const char *nick, + GSList *channels) +{ + NETJOIN_REC *rec; + NETJOIN_SERVER_REC *srec; + + g_return_val_if_fail(server != NULL, NULL); + g_return_val_if_fail(nick != NULL, NULL); + + rec = g_new0(NETJOIN_REC, 1); + rec->nick = g_strdup(nick); + while (channels != NULL) { + NETSPLIT_CHAN_REC *channel = channels->data; + + rec->old_channels = g_slist_append(rec->old_channels, + g_strdup(channel->name)); + channels = channels->next; + } + + srec = netjoin_find_server(server); + if (srec == NULL) { + srec = g_new0(NETJOIN_SERVER_REC, 1); + srec->server = server; + joinservers = g_slist_append(joinservers, srec); + } + + srec->last_netjoin = time(NULL); + srec->netjoins = g_slist_append(srec->netjoins, rec); + return rec; +} + +static NETJOIN_REC *netjoin_find(IRC_SERVER_REC *server, const char *nick) +{ + NETJOIN_SERVER_REC *srec; + GSList *tmp; + + g_return_val_if_fail(server != NULL, NULL); + g_return_val_if_fail(nick != NULL, NULL); + + srec = netjoin_find_server(server); + if (srec == NULL) return NULL; + + for (tmp = srec->netjoins; tmp != NULL; tmp = tmp->next) { + NETJOIN_REC *rec = tmp->data; + + if (g_ascii_strcasecmp(rec->nick, nick) == 0) + return rec; + } + + return NULL; +} + +static void netjoin_remove(NETJOIN_SERVER_REC *server, NETJOIN_REC *rec) +{ + server->netjoins = g_slist_remove(server->netjoins, rec); + + g_slist_foreach(rec->old_channels, (GFunc) g_free, NULL); + g_slist_foreach(rec->now_channels, (GFunc) g_free, NULL); + g_slist_free(rec->old_channels); + g_slist_free(rec->now_channels); + + g_free(rec->nick); + g_free(rec); +} + +static void netjoin_server_remove(NETJOIN_SERVER_REC *server) +{ + joinservers = g_slist_remove(joinservers, server); + + while (server->netjoins != NULL) + netjoin_remove(server, server->netjoins->data); + g_free(server); +} + +static void print_channel_netjoins(char *channel, TEMP_PRINT_REC *rec, + NETJOIN_SERVER_REC *server) +{ + if (rec->nicks->len > 0) + g_string_truncate(rec->nicks, rec->nicks->len-2); + + printformat(server->server, channel, MSGLEVEL_JOINS, + rec->count > netjoin_max_nicks ? + IRCTXT_NETSPLIT_JOIN_MORE : IRCTXT_NETSPLIT_JOIN, + rec->nicks->str, rec->count-netjoin_max_nicks); + + g_string_free(rec->nicks, TRUE); + g_free(rec); + g_free(channel); +} + +static void print_netjoins(NETJOIN_SERVER_REC *server, const char *filter_channel) +{ + TEMP_PRINT_REC *temp; + GHashTable *channels; + GSList *tmp, *tmp2, *next, *next2, *old; + + g_return_if_fail(server != NULL); + + printing_joins = TRUE; + + /* save nicks to string, clear now_channels and remove the same + channels from old_channels list */ + channels = g_hash_table_new((GHashFunc) i_istr_hash, (GCompareFunc) i_istr_equal); + for (tmp = server->netjoins; tmp != NULL; tmp = next) { + NETJOIN_REC *rec = tmp->data; + + next = g_slist_next(tmp); + + for (tmp2 = rec->now_channels; tmp2 != NULL; tmp2 = next2) { + char *channel = tmp2->data; + char *realchannel = channel + 1; + + next2 = g_slist_next(tmp2); + + /* Filter the results by channel if asked to do so */ + if (filter_channel != NULL && + strcasecmp(realchannel, filter_channel) != 0) + continue; + + temp = g_hash_table_lookup(channels, realchannel); + if (temp == NULL) { + temp = g_new0(TEMP_PRINT_REC, 1); + temp->nicks = g_string_new(NULL); + g_hash_table_insert(channels, + g_strdup(realchannel), + temp); + } + + temp->count++; + if (temp->count <= netjoin_max_nicks) { + if (*channel != ' ') + g_string_append_c(temp->nicks, + *channel); + g_string_append_printf(temp->nicks, "%s, ", + rec->nick); + } + + /* remove the channel from old_channels too */ + old = i_slist_find_icase_string(rec->old_channels, realchannel); + if (old != NULL) { + void *data = old->data; + rec->old_channels = + g_slist_remove(rec->old_channels, data); + g_free(data); + } + + /* drop tmp2 from the list */ + rec->now_channels = g_slist_delete_link(rec->now_channels, tmp2); + g_free(channel); + } + + if (rec->old_channels == NULL) + netjoin_remove(server, rec); + } + + g_hash_table_foreach(channels, (GHFunc) print_channel_netjoins, + server); + g_hash_table_destroy(channels); + + if (server->netjoins == NULL) + netjoin_server_remove(server); + + printing_joins = FALSE; +} + +/* something is going to be printed to screen, print our current netsplit + message before it. */ +static void sig_print_starting(TEXT_DEST_REC *dest) +{ + NETJOIN_SERVER_REC *rec; + + if (printing_joins) + return; + + if (!IS_IRC_SERVER(dest->server)) + return; + + rec = netjoin_find_server(IRC_SERVER(dest->server)); + if (rec != NULL && rec->netjoins != NULL) { + /* if netjoins exists, the server rec should be + still valid. otherwise, calling server->ischannel + may not be safe. */ + if (dest->target != NULL && + !server_ischannel((SERVER_REC *) rec->server, dest->target)) + return; + + print_netjoins(rec, NULL); + } +} + +static int sig_check_netjoins(void) +{ + GSList *tmp, *next; + int diff; + time_t now; + + now = time(NULL); + /* first print all netjoins which haven't had any new joins + * for NETJOIN_WAIT_TIME; this may cause them to be removed + * (all users who rejoined, rejoined all channels) */ + for (tmp = joinservers; tmp != NULL; tmp = next) { + NETJOIN_SERVER_REC *server = tmp->data; + + next = tmp->next; + diff = now-server->last_netjoin; + if (diff <= NETJOIN_WAIT_TIME) { + /* wait for more JOINs */ + continue; + } + + if (server->netjoins != NULL) + print_netjoins(server, NULL); + } + + /* now remove all netjoins which haven't had any new joins + * for NETJOIN_MAX_WAIT (user rejoined some but not all channels + * after split) */ + for (tmp = joinservers; tmp != NULL; tmp = next) { + NETJOIN_SERVER_REC *server = tmp->data; + + next = tmp->next; + diff = now-server->last_netjoin; + if (diff >= NETJOIN_MAX_WAIT) { + /* waited long enough, forget about the rest */ + netjoin_server_remove(server); + } + } + + if (joinservers == NULL) { + g_source_remove(join_tag); + signal_remove("print starting", (SIGNAL_FUNC) sig_print_starting); + join_tag = -1; + } + return 1; +} + +static void msg_quit(IRC_SERVER_REC *server, const char *nick, + const char *address, const char *reason) +{ + if (IS_IRC_SERVER(server) && quitmsg_is_split(reason)) + signal_stop(); +} + +static void msg_join(IRC_SERVER_REC *server, const char *channel, + const char *nick, const char *address) +{ + NETSPLIT_REC *split; + NETJOIN_REC *netjoin; + GSList *channels; + int rejoin = 1; + + if (!IS_IRC_SERVER(server)) + return; + + if (ignore_check(SERVER(server), nick, address, + channel, NULL, MSGLEVEL_JOINS)) + return; + + split = netsplit_find(server, nick, address); + netjoin = netjoin_find(server, nick); + if (split == NULL && netjoin == NULL) + return; + + /* if this was not a channel they split from, treat it normally */ + if (netjoin != NULL) { + if (!i_slist_find_icase_string(netjoin->old_channels, channel)) + return; + } else { + channels = split->channels; + while (channels != NULL) { + NETSPLIT_CHAN_REC *schannel = channels->data; + + if (!strcasecmp(schannel->name, channel)) + break; + channels = channels->next; + } + /* we still need to create a NETJOIN_REC now as the + * NETSPLIT_REC will be destroyed */ + if (channels == NULL) + rejoin = 0; + } + + if (join_tag == -1) { + join_tag = g_timeout_add(1000, (GSourceFunc) + sig_check_netjoins, NULL); + signal_add("print starting", (SIGNAL_FUNC) sig_print_starting); + } + + if (netjoin == NULL) + netjoin = netjoin_add(server, nick, split->channels); + + if (rejoin) + { + netjoin->now_channels = g_slist_append(netjoin->now_channels, + g_strconcat(" ", channel, NULL)); + signal_stop(); + } +} + +static int netjoin_set_nickmode(IRC_SERVER_REC *server, NETJOIN_REC *rec, + const char *channel, char prefix) +{ + GSList *pos; + const char *flags; + char *found_chan = NULL; + + for (pos = rec->now_channels; pos != NULL; pos = pos->next) { + char *chan = pos->data; + if (strcasecmp(chan+1, channel) == 0) { + found_chan = chan; + break; + } + } + + if (found_chan == NULL) + return FALSE; + + flags = server->get_nick_flags(SERVER(server)); + while (*flags != '\0') { + if (found_chan[0] == *flags) + break; + if (prefix == *flags) { + found_chan[0] = prefix; + break; + } + flags++; + } + return TRUE; +} + +static void msg_mode(IRC_SERVER_REC *server, const char *channel, + const char *sender, const char *addr, const char *data) +{ + NETJOIN_REC *rec; + char *params, *mode, *nicks; + char **nicklist, **nick, type, prefix; + int show; + + g_return_if_fail(data != NULL); + if (!server_ischannel(SERVER(server), channel) || addr != NULL) + return; + + params = event_get_params(data, 2 | PARAM_FLAG_GETREST, + &mode, &nicks); + + /* parse server mode changes - hide operator status changes and + show them in the netjoin message instead as @ before the nick */ + nick = nicklist = g_strsplit(nicks, " ", -1); + + type = '+'; show = FALSE; + for (; *mode != '\0'; mode++) { + if (*mode == '+' || *mode == '-') { + type = *mode; + continue; + } + + if (*nick != NULL && GET_MODE_PREFIX(server, *mode)) { + /* give/remove ops */ + rec = netjoin_find(server, *nick); + prefix = GET_MODE_PREFIX(server, *mode); + if (rec == NULL || type != '+' || prefix == '\0' || + !netjoin_set_nickmode(server, rec, channel, prefix)) + show = TRUE; + nick++; + } else { + if (HAS_MODE_ARG(server, type, *mode) && *nick != NULL) + nick++; + show = TRUE; + } + } + + if (!show) signal_stop(); + + g_strfreev(nicklist); + g_free(params); +} + +static void read_settings(void) +{ + int old_hide; + + old_hide = hide_netsplit_quits; + hide_netsplit_quits = settings_get_bool("hide_netsplit_quits"); + netjoin_max_nicks = settings_get_int("netjoin_max_nicks"); + + if (old_hide && !hide_netsplit_quits) { + signal_remove("message quit", (SIGNAL_FUNC) msg_quit); + signal_remove("message join", (SIGNAL_FUNC) msg_join); + signal_remove("message irc mode", (SIGNAL_FUNC) msg_mode); + } else if (!old_hide && hide_netsplit_quits) { + signal_add("message quit", (SIGNAL_FUNC) msg_quit); + signal_add("message join", (SIGNAL_FUNC) msg_join); + signal_add("message irc mode", (SIGNAL_FUNC) msg_mode); + } +} + +static void sig_server_disconnected(IRC_SERVER_REC *server) +{ + NETJOIN_SERVER_REC *netjoin_server; + + g_return_if_fail(server != NULL); + + if (!IS_IRC_SERVER(server)) + return; + + if ((netjoin_server = netjoin_find_server(server))) { + netjoin_server_remove(netjoin_server); + } +} + +void fe_netjoin_init(void) +{ + settings_add_bool("misc", "hide_netsplit_quits", TRUE); + settings_add_int("misc", "netjoin_max_nicks", 10); + + join_tag = -1; + printing_joins = FALSE; + + read_settings(); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); + signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected); +} + +void fe_netjoin_deinit(void) +{ + while (joinservers != NULL) + netjoin_server_remove(joinservers->data); + if (join_tag != -1) { + g_source_remove(join_tag); + signal_remove("print starting", (SIGNAL_FUNC) sig_print_starting); + } + + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); + signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected); + + signal_remove("message quit", (SIGNAL_FUNC) msg_quit); + signal_remove("message join", (SIGNAL_FUNC) msg_join); + signal_remove("message irc mode", (SIGNAL_FUNC) msg_mode); +} diff --git a/src/fe-common/irc/fe-netsplit.c b/src/fe-common/irc/fe-netsplit.c new file mode 100644 index 0000000..b76d4ce --- /dev/null +++ b/src/fe-common/irc/fe-netsplit.c @@ -0,0 +1,389 @@ +/* + fe-netsplit.c : irssi + + Copyright (C) 2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/irc/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/irc/core/irc-servers.h> +#include <irssi/src/irc/core/irc-commands.h> +#include <irssi/src/core/ignore.h> +#include <irssi/src/irc/core/netsplit.h> + +#include <irssi/src/fe-common/core/printtext.h> + +#define SPLIT_WAIT_TIME 5 /* how many seconds to wait for the QUIT split messages to stop */ + +static int split_tag; +static int netsplit_max_nicks, netsplit_nicks_hide_threshold; +static int printing_splits; + +static int get_last_split(IRC_SERVER_REC *server) +{ + GSList *tmp; + time_t last; + + last = 0; + for (tmp = server->split_servers; tmp != NULL; tmp = tmp->next) { + NETSPLIT_SERVER_REC *rec = tmp->data; + + if (rec->last > last) last = rec->last; + } + + return last; +} + +typedef struct { + char *name; + int nick_count, maxnickpos; + GString *nicks; +} TEMP_SPLIT_CHAN_REC; + +typedef struct { + IRC_SERVER_REC *server_rec; + GSList *servers; /* if many servers splitted from the same one */ + GSList *channels; +} TEMP_SPLIT_REC; + +static GSList *get_source_servers(const char *server, GSList **servers) +{ + GSList *list, *next, *tmp; + + list = NULL; + for (tmp = *servers; tmp != NULL; tmp = next) { + NETSPLIT_SERVER_REC *rec = tmp->data; + next = tmp->next; + + if (g_ascii_strcasecmp(rec->server, server) == 0) { + rec->prints = 0; + list = g_slist_append(list, rec); + *servers = g_slist_remove(*servers, rec); + } + } + + return list; +} + +static TEMP_SPLIT_CHAN_REC *find_split_chan(TEMP_SPLIT_REC *rec, + const char *name) +{ + GSList *tmp; + + for (tmp = rec->channels; tmp != NULL; tmp = tmp->next) { + TEMP_SPLIT_CHAN_REC *chanrec = tmp->data; + + if (g_ascii_strcasecmp(chanrec->name, name) == 0) + return chanrec; + } + + return NULL; +} + +static void get_server_splits(void *key, NETSPLIT_REC *split, + TEMP_SPLIT_REC *rec) +{ + TEMP_SPLIT_CHAN_REC *chanrec; + GSList *tmp; + + if (split->printed || + g_slist_find(rec->servers, split->server) == NULL) + return; + + split->printed = TRUE; + for (tmp = split->channels; tmp != NULL; tmp = tmp->next) { + NETSPLIT_CHAN_REC *splitchan = tmp->data; + + if (ignore_check(SERVER(rec->server_rec), split->nick, + split->address, splitchan->name, "", + MSGLEVEL_QUITS)) + continue; + + chanrec = find_split_chan(rec, splitchan->name); + if (chanrec == NULL) { + chanrec = g_new0(TEMP_SPLIT_CHAN_REC, 1); + chanrec->name = splitchan->name; + chanrec->nicks = g_string_new(NULL); + + rec->channels = g_slist_append(rec->channels, chanrec); + } + + split->server->prints++; + chanrec->nick_count++; + if (netsplit_nicks_hide_threshold <= 0 || + chanrec->nick_count <= netsplit_nicks_hide_threshold) { + if (splitchan->op) + g_string_append_c(chanrec->nicks, '@'); + else if (splitchan->voice) + g_string_append_c(chanrec->nicks, '+'); + g_string_append_printf(chanrec->nicks, "%s, ", split->nick); + + if (chanrec->nick_count == netsplit_max_nicks) + chanrec->maxnickpos = chanrec->nicks->len; + } + } +} + +static void print_server_splits(IRC_SERVER_REC *server, TEMP_SPLIT_REC *rec, const char *filter_channel) +{ + GString *destservers; + char *sourceserver; + GSList *tmp; + + g_return_if_fail(rec->servers != NULL); + + destservers = g_string_new(NULL); + for (tmp = rec->servers; tmp != NULL; tmp = tmp->next) { + NETSPLIT_SERVER_REC *rec = tmp->data; + + if (rec->prints > 0) { + g_string_append_printf(destservers, "%s, ", + rec->destserver); + } + } + if (destservers->len == 0) { + /* no nicks to print in this server */ + g_string_free(destservers, TRUE); + return; + } + g_string_truncate(destservers, destservers->len-2); + + sourceserver = ((NETSPLIT_SERVER_REC *) (rec->servers->data))->server; + for (tmp = rec->channels; tmp != NULL; tmp = tmp->next) { + TEMP_SPLIT_CHAN_REC *chan = tmp->data; + + if (filter_channel != NULL && + strcasecmp(chan->name, filter_channel) != 0) + continue; + + g_string_truncate(chan->nicks, chan->nicks->len-2); + + if (netsplit_max_nicks > 0 && + chan->nick_count > netsplit_max_nicks) { + g_string_truncate(chan->nicks, chan->maxnickpos); + printformat(server, chan->name, MSGLEVEL_QUITS, + IRCTXT_NETSPLIT_MORE, sourceserver, + destservers->str, chan->nicks->str, + chan->nick_count - netsplit_max_nicks); + } else { + printformat(server, chan->name, MSGLEVEL_QUITS, + IRCTXT_NETSPLIT, sourceserver, + destservers->str, chan->nicks->str); + } + } + + g_string_free(destservers, TRUE); +} + +static void temp_split_chan_free(TEMP_SPLIT_CHAN_REC *rec) +{ + g_string_free(rec->nicks, TRUE); + g_free(rec); +} + +static void print_splits(IRC_SERVER_REC *server, const char *filter_channel) +{ + TEMP_SPLIT_REC temp; + GSList *servers; + + printing_splits = TRUE; + + servers = g_slist_copy(server->split_servers); + while (servers != NULL) { + NETSPLIT_SERVER_REC *sserver = servers->data; + + /* get all the splitted servers that have the same + source server */ + temp.servers = get_source_servers(sserver->server, &servers); + temp.server_rec = server; + temp.channels = NULL; + + g_hash_table_foreach(server->splits, + (GHFunc) get_server_splits, &temp); + print_server_splits(server, &temp, filter_channel); + + g_slist_foreach(temp.channels, + (GFunc) temp_split_chan_free, NULL); + g_slist_free(temp.servers); + g_slist_free(temp.channels); + } + + printing_splits = FALSE; +} + +static int check_server_splits(IRC_SERVER_REC *server) +{ + time_t last; + + g_return_val_if_fail(IS_IRC_SERVER(server), FALSE); + + last = get_last_split(server); + if (time(NULL)-last < SPLIT_WAIT_TIME) + return FALSE; + + print_splits(server, NULL); + return TRUE; +} + +/* something is going to be printed to screen, print our current netsplit + message before it. */ +static void sig_print_starting(TEXT_DEST_REC *dest) +{ + IRC_SERVER_REC *rec; + + if (printing_splits) + return; + + if (!IS_IRC_SERVER(dest->server)) + return; + + rec = IRC_SERVER(dest->server); + if (rec->split_servers != NULL) { + /* if split_servers exists, the server rec should be + still valid. otherwise, calling server->ischannel + may not be safe. */ + if (dest->target != NULL && !server_ischannel((SERVER_REC *) rec, dest->target)) + return; + + print_splits(rec, NULL); + } +} + +static int sig_check_splits(void) +{ + GSList *tmp; + int stop; + + stop = TRUE; + for (tmp = servers; tmp != NULL; tmp = tmp->next) { + IRC_SERVER_REC *rec = tmp->data; + + if (!IS_IRC_SERVER(rec)) + continue; + + if (rec->split_servers != NULL) { + if (!check_server_splits(rec)) + stop = FALSE; + } + } + + if (stop) { + g_source_remove(split_tag); + signal_remove("print starting", (SIGNAL_FUNC) sig_print_starting); + split_tag = -1; + } + return 1; +} + +static void sig_netsplit_servers(void) +{ + if (settings_get_bool("hide_netsplit_quits") && split_tag == -1) { + split_tag = g_timeout_add(1000, + (GSourceFunc) sig_check_splits, + NULL); + signal_add("print starting", (SIGNAL_FUNC) sig_print_starting); + } +} + +static int split_equal(NETSPLIT_REC *n1, NETSPLIT_REC *n2) +{ + return g_ascii_strcasecmp(n1->nick, n2->nick); +} + +static void split_get(void *key, NETSPLIT_REC *rec, GSList **list) +{ + *list = g_slist_insert_sorted(*list, rec, + (GCompareFunc) split_equal); +} + +static void split_print(NETSPLIT_REC *rec, SERVER_REC *server) +{ + NETSPLIT_CHAN_REC *chan; + char *chanstr; + + chan = rec->channels->data; + chanstr = chan == NULL ? + g_strdup("") : + g_strconcat(chan->op ? "@" : (chan->voice ? "+" : ""), chan->name, NULL); + + printformat(server, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_NETSPLITS_LINE, + rec->nick, chanstr, rec->server->server, + rec->server->destserver); + + g_free(chanstr); +} + +/* SYNTAX: NETSPLIT */ +static void cmd_netsplit(const char *data, IRC_SERVER_REC *server) +{ + GSList *list; + + CMD_IRC_SERVER(server); + + if (server->split_servers == NULL) { + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, + IRCTXT_NO_NETSPLITS); + return; + } + + printformat(server, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_NETSPLITS_HEADER); + + list = NULL; + g_hash_table_foreach(server->splits, (GHFunc) split_get, &list); + g_slist_foreach(list, (GFunc) split_print, server); + g_slist_free(list); + + printformat(server, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_NETSPLITS_FOOTER); +} + +static void read_settings(void) +{ + netsplit_max_nicks = settings_get_int("netsplit_max_nicks"); + netsplit_nicks_hide_threshold = + settings_get_int("netsplit_nicks_hide_threshold"); + if (netsplit_nicks_hide_threshold < netsplit_max_nicks) + netsplit_max_nicks = netsplit_nicks_hide_threshold; +} + +void fe_netsplit_init(void) +{ + settings_add_int("misc", "netsplit_max_nicks", 10); + settings_add_int("misc", "netsplit_nicks_hide_threshold", 15); + split_tag = -1; + printing_splits = FALSE; + + read_settings(); + signal_add("netsplit new", (SIGNAL_FUNC) sig_netsplit_servers); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); + command_bind_irc("netsplit", NULL, (SIGNAL_FUNC) cmd_netsplit); +} + +void fe_netsplit_deinit(void) +{ + if (split_tag != -1) { + g_source_remove(split_tag); + signal_remove("print starting", (SIGNAL_FUNC) sig_print_starting); + } + + signal_remove("netsplit new", (SIGNAL_FUNC) sig_netsplit_servers); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); + command_unbind("netsplit", (SIGNAL_FUNC) cmd_netsplit); +} diff --git a/src/fe-common/irc/fe-sasl.c b/src/fe-common/irc/fe-sasl.c new file mode 100644 index 0000000..72fec58 --- /dev/null +++ b/src/fe-common/irc/fe-sasl.c @@ -0,0 +1,53 @@ +/* + fe-sasl.c : irssi + + Copyright (C) 2015-2017 The Lemon Man + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/irc/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/irc/core/sasl.h> + +#include <irssi/src/irc/core/irc-servers.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/fe-common/core/printtext.h> + +static void sig_sasl_success(IRC_SERVER_REC *server) +{ + printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_SASL_SUCCESS); +} + +static void sig_sasl_failure(IRC_SERVER_REC *server, const char *reason) +{ + printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_SASL_ERROR, reason); +} + +void fe_sasl_init(void) +{ + signal_add("server sasl success", (SIGNAL_FUNC) sig_sasl_success); + signal_add("server sasl failure", (SIGNAL_FUNC) sig_sasl_failure); +} + +void fe_sasl_deinit(void) +{ + signal_remove("server sasl success", (SIGNAL_FUNC) sig_sasl_success); + signal_remove("server sasl failure", (SIGNAL_FUNC) sig_sasl_failure); +} diff --git a/src/fe-common/irc/fe-whois.c b/src/fe-common/irc/fe-whois.c new file mode 100644 index 0000000..b0eeb19 --- /dev/null +++ b/src/fe-common/irc/fe-whois.c @@ -0,0 +1,456 @@ +/* Copyright (C) 1999-2004 Timo Sirainen */ + +#include "module.h" +#include <irssi/src/fe-common/irc/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/settings.h> +#include <irssi/src/core/recode.h> + +#include <irssi/src/irc/core/irc-servers.h> + +#include <irssi/src/fe-common/core/printtext.h> + +static void event_whois(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick, *user, *host, *realname, *recoded; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 6, NULL, &nick, &user, + &host, NULL, &realname); + recoded = recode_in(SERVER(server), realname, nick); + printformat(server, nick, MSGLEVEL_CRAP, + IRCTXT_WHOIS, nick, user, host, recoded); + g_free(params); + g_free(recoded); +} + +static void event_whois_special(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick, *str; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 3 | PARAM_FLAG_GETREST, NULL, &nick, &str); + printformat(server, nick, MSGLEVEL_CRAP, + IRCTXT_WHOIS_SPECIAL, nick, str); + g_free(params); +} + +static void event_whois_idle(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick, *secstr, *signonstr, *rest, *timestr; + long days, hours, mins, secs; + time_t signon; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 5 | PARAM_FLAG_GETREST, NULL, + &nick, &secstr, &signonstr, &rest); + + secs = atol(secstr); + signon = strstr(rest, "signon time") == NULL ? 0 : + (time_t) atol(signonstr); + + days = secs/3600/24; + hours = (secs%(3600*24))/3600; + mins = (secs%3600)/60; + secs %= 60; + + if (signon == 0) + printformat(server, nick, MSGLEVEL_CRAP, IRCTXT_WHOIS_IDLE, + nick, days, hours, mins, secs); + else { + timestr = my_asctime(signon); + printformat(server, nick, MSGLEVEL_CRAP, + IRCTXT_WHOIS_IDLE_SIGNON, + nick, days, hours, mins, secs, timestr); + g_free(timestr); + } + g_free(params); +} + +static void event_whois_server(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick, *whoserver, *desc; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 4, NULL, &nick, &whoserver, &desc); + printformat(server, nick, MSGLEVEL_CRAP, + IRCTXT_WHOIS_SERVER, nick, whoserver, desc); + g_free(params); +} + +static void event_whois_oper(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick, *type; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 3, NULL, &nick, &type); + + /* Bugfix: http://bugs.irssi.org/?do=details&task_id=99 + * Author: Geert Hauwaerts <geert@irssi.org> + * Date: Wed Sep 15 20:17:24 CEST 2004 + */ + + if ((!strncmp(type, "is an ", 6)) || (!strncmp(type, "is a ", 5))) { + type += 5; + if (*type == ' ') type++; + } + + if (*type == '\0') + type = "IRC Operator"; + + printformat(server, nick, MSGLEVEL_CRAP, + IRCTXT_WHOIS_OPER, nick, type); + g_free(params); +} + +static void event_whois_modes(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick, *modes; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 3 | PARAM_FLAG_GETREST, + NULL, &nick, &modes); + if (!strncmp(modes, "is using modes ", 15)) + modes += 15; + printformat(server, nick, MSGLEVEL_CRAP, + IRCTXT_WHOIS_MODES, nick, modes); + g_free(params); +} + +static void event_whois_realhost(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick, *txt_real, *txt_hostname, *hostname; + + g_return_if_fail(data != NULL); + + /* <yournick> real hostname <nick> <hostname> */ + params = event_get_params(data, 5, NULL, &nick, &txt_real, + &txt_hostname, &hostname); + if (g_strcmp0(txt_real, "real") != 0 || + g_strcmp0(txt_hostname, "hostname") != 0) { + /* <yournick> <nick> :... from <hostname> */ + g_free(params); + params = event_get_params(data, 3, NULL, &nick, &hostname); + + hostname = strstr(hostname, "from "); + if (hostname != NULL) hostname += 5; + } + + if (hostname != NULL) { + if (!strncmp(hostname, "*@", 2)) + hostname += 2; + printformat(server, nick, MSGLEVEL_CRAP, + IRCTXT_WHOIS_REALHOST, nick, hostname, ""); + } else { + event_whois_special(server, data); + } + g_free(params); +} + +static void event_whois_usermode326(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick, *usermode; + + g_return_if_fail(data != NULL); + + /* <yournick> <nick> :has oper privs: <mode> */ + params = event_get_params(data, 3, NULL, &nick, &usermode); + printformat(server, nick, MSGLEVEL_CRAP, + IRCTXT_WHOIS_USERMODE, nick, usermode); + g_free(params); +} + +static void event_whois_realhost327(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick, *hostname, *ip, *text; + + g_return_if_fail(data != NULL); + + /* <yournick> <hostname> <ip> :Real hostname/IP */ + params = event_get_params(data, 5, NULL, &nick, &hostname, &ip, &text); + if (*text != '\0') { + printformat(server, nick, MSGLEVEL_CRAP, + IRCTXT_WHOIS_REALHOST, nick, hostname, ip); + } else { + event_whois_special(server, data); + } + g_free(params); +} + +static void event_whois_realhost338(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick, *arg1, *arg2, *arg3; + + g_return_if_fail(data != NULL); + + /* + * :<server> 338 <yournick> <nick> <user>@<host> <ip> :Actual user@host, actual IP + * (ircu) or + * :<server> 338 <yournick> <nick> <ip> :actually using host + * (ratbox) + */ + params = event_get_params(data, 5, NULL, &nick, &arg1, &arg2, &arg3); + if (*arg3 != '\0') { + printformat(server, nick, MSGLEVEL_CRAP, + IRCTXT_WHOIS_REALHOST, nick, arg1, arg2); + } else if (*arg2 != '\0') { + printformat(server, nick, MSGLEVEL_CRAP, + IRCTXT_WHOIS_REALHOST, nick, arg1, ""); + } else { + event_whois_special(server, data); + } + g_free(params); +} + +static void event_whois_usermode(IRC_SERVER_REC *server, const char *data) +{ + char *params, *txt_usermodes, *nick, *usermode; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 4, NULL, &txt_usermodes, + &nick, &usermode); + + if (g_strcmp0(txt_usermodes, "usermodes") == 0) { + /* <yournick> usermodes <nick> usermode */ + printformat(server, nick, MSGLEVEL_CRAP, + IRCTXT_WHOIS_USERMODE, nick, usermode); + } else { + event_whois_special(server, data); + } + g_free(params); +} + +static void hide_safe_channel_id(IRC_SERVER_REC *server, char *chans) +{ + const char *idchan, *nick_flags; + char *p, *dest, *end, id; + int count, length, chanstart; + + if (!server->isupport_sent) + idchan = "!:5"; + else { + idchan = g_hash_table_lookup(server->isupport, "IDCHAN"); + if (idchan == NULL) + return; + } + nick_flags = server->get_nick_flags(SERVER(server)); + + while (*idchan != '\0') { + id = *idchan; + if (idchan[1] != ':') + return; + + length = strtoul(idchan+2, &end, 10); + if (*end == ',') + end++; + else if (*end != '\0') + return; + idchan = end; + + count = 0; + chanstart = TRUE; + for (dest = p = chans; *p != '\0'; p++) { + if (count > 0) + count--; + else { + if (*p == ' ') + chanstart = TRUE; + else { + if (chanstart && *p == id) + count = length; + chanstart = chanstart && strchr(nick_flags, *p); + } + *dest++ = *p; + } + } + *dest = '\0'; + } +} + +static void event_whois_channels(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick, *chans, *recoded; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 3, NULL, &nick, &chans); + + /* sure - we COULD print the channel names as-is, but since the + colors, bolds, etc. are mostly just to fool people, I think we + should show the channel names as they REALLY are so they could + even be joined without any extra tricks. */ + chans = show_lowascii(chans); + if (settings_get_bool("whois_hide_safe_channel_id")) + hide_safe_channel_id(server, chans); + recoded = recode_in(SERVER(server), chans, nick); + printformat(server, nick, MSGLEVEL_CRAP, + IRCTXT_WHOIS_CHANNELS, nick, recoded); + g_free(chans); + + g_free(params); + g_free(recoded); +} + +static void event_whois_away(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick, *awaymsg, *recoded; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 3, NULL, &nick, &awaymsg); + recoded = recode_in(SERVER(server), awaymsg, nick); + printformat(server, nick, MSGLEVEL_CRAP, + IRCTXT_WHOIS_AWAY, nick, recoded); + g_free(params); + g_free(recoded); +} + +static void event_end_of_whois(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2, NULL, &nick); + if (server->whois_found) { + printformat(server, nick, MSGLEVEL_CRAP, + IRCTXT_END_OF_WHOIS, nick); + } + g_free(params); +} + +static void event_whois_auth(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick, *text; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 3, NULL, &nick, &text); + printformat(server, nick, MSGLEVEL_CRAP, + IRCTXT_WHOIS_EXTRA, nick, text); + g_free(params); +} + +static void event_whowas(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick, *user, *host, *realname, *recoded; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 6, NULL, &nick, &user, + &host, NULL, &realname); + recoded = recode_in(SERVER(server), realname, nick); + printformat(server, nick, MSGLEVEL_CRAP, + IRCTXT_WHOWAS, nick, user, host, recoded); + g_free(params); + g_free(recoded); +} + +static void event_end_of_whowas(IRC_SERVER_REC *server, const char *data) +{ + char *params, *nick; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 2, NULL, &nick); + if (server->whowas_found) { + printformat(server, nick, MSGLEVEL_CRAP, + IRCTXT_END_OF_WHOWAS, nick); + } + g_free(params); +} + +struct whois_event_table { + int num; + void (*func)(IRC_SERVER_REC *, const char *); +}; + +static struct whois_event_table events[] = { + { 312, event_whois_server }, + { 326, event_whois_usermode326 }, + { 327, event_whois_realhost327 }, + { 338, event_whois_realhost338 }, + { 379, event_whois_modes }, + { 378, event_whois_realhost }, + { 377, event_whois_usermode }, + { 317, event_whois_idle }, + { 330, event_whois_auth }, + { 319, event_whois_channels }, + { 0, NULL } +}; + +static void event_whois_default(IRC_SERVER_REC *server, const char *data) +{ + int i, num; + + num = atoi(current_server_event); + for (i = 0; events[i].num != 0; i++) { + if (events[i].num == num) { + events[i].func(server, data); + return; + } + } + + event_whois_special(server, data); +} + +void fe_whois_init(void) +{ + settings_add_bool("lookandfeel", "whois_hide_safe_channel_id", TRUE); + + signal_add("event 311", (SIGNAL_FUNC) event_whois); + signal_add("event 312", (SIGNAL_FUNC) event_whois_server); + /* readding this events fixes the printing of /whois -yes * + Bug http://bugs.irssi.org/?do=details&task_id=123 */ + signal_add("event 317", (SIGNAL_FUNC) event_whois_idle); + signal_add("event 319", (SIGNAL_FUNC) event_whois_channels); + signal_add("event 313", (SIGNAL_FUNC) event_whois_oper); + signal_add("event 330", (SIGNAL_FUNC) event_whois_auth); + signal_add("whois account", (SIGNAL_FUNC) event_whois_auth); + signal_add("event 377", (SIGNAL_FUNC) event_whois_usermode); + signal_add("event 378", (SIGNAL_FUNC) event_whois_realhost); + signal_add("event 379", (SIGNAL_FUNC) event_whois_modes); + signal_add("event 327", (SIGNAL_FUNC) event_whois_realhost327); + signal_add("event 326", (SIGNAL_FUNC) event_whois_usermode326); + signal_add("event 338", (SIGNAL_FUNC) event_whois_realhost338); + signal_add("whois away", (SIGNAL_FUNC) event_whois_away); + signal_add("whois oper", (SIGNAL_FUNC) event_whois_oper); + signal_add("whowas away", (SIGNAL_FUNC) event_whois_away); + signal_add("whois default event", (SIGNAL_FUNC) event_whois_default); + signal_add("event 318", (SIGNAL_FUNC) event_end_of_whois); + signal_add("event 314", (SIGNAL_FUNC) event_whowas); + signal_add("event 369", (SIGNAL_FUNC) event_end_of_whowas); +} + +void fe_whois_deinit(void) +{ + signal_remove("event 311", (SIGNAL_FUNC) event_whois); + signal_remove("event 312", (SIGNAL_FUNC) event_whois_server); + signal_remove("event 317", (SIGNAL_FUNC) event_whois_idle); + signal_remove("event 319", (SIGNAL_FUNC) event_whois_channels); + signal_remove("event 313", (SIGNAL_FUNC) event_whois_oper); + signal_remove("event 330", (SIGNAL_FUNC) event_whois_auth); + signal_remove("whois account", (SIGNAL_FUNC) event_whois_auth); + signal_remove("event 377", (SIGNAL_FUNC) event_whois_usermode); + signal_remove("event 378", (SIGNAL_FUNC) event_whois_realhost); + signal_remove("event 379", (SIGNAL_FUNC) event_whois_modes); + signal_remove("event 327", (SIGNAL_FUNC) event_whois_realhost327); + signal_remove("event 326", (SIGNAL_FUNC) event_whois_usermode326); + signal_remove("event 338", (SIGNAL_FUNC) event_whois_realhost338); + signal_remove("whois away", (SIGNAL_FUNC) event_whois_away); + signal_remove("whois oper", (SIGNAL_FUNC) event_whois_oper); + signal_remove("whowas away", (SIGNAL_FUNC) event_whois_away); + signal_remove("whois default event", (SIGNAL_FUNC) event_whois_default); + signal_remove("event 318", (SIGNAL_FUNC) event_end_of_whois); + signal_remove("event 314", (SIGNAL_FUNC) event_whowas); + signal_remove("event 369", (SIGNAL_FUNC) event_end_of_whowas); +} diff --git a/src/fe-common/irc/irc-completion.c b/src/fe-common/irc/irc-completion.c new file mode 100644 index 0000000..71e2e0c --- /dev/null +++ b/src/fe-common/irc/irc-completion.c @@ -0,0 +1,41 @@ +/* + irc-completion.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/signals.h> +#include <irssi/src/fe-common/core/chat-completion.h> + +static void sig_complete_stats(GList **list, WINDOW_REC *window, + const char *word, const char *line, + int *want_space) +{ + *list = completion_get_servers(word); + if (*list != NULL) signal_stop(); +} + +void irc_completion_init(void) +{ + signal_add("complete command stats", (SIGNAL_FUNC) sig_complete_stats); +} + +void irc_completion_deinit(void) +{ + signal_remove("complete command stats", (SIGNAL_FUNC) sig_complete_stats); +} diff --git a/src/fe-common/irc/irc-modules.c b/src/fe-common/irc/irc-modules.c new file mode 100644 index 0000000..3bf1f32 --- /dev/null +++ b/src/fe-common/irc/irc-modules.c @@ -0,0 +1,4 @@ +void fe_irc_dcc_init(void);void fe_irc_notifylist_init(void); +void fe_irc_notifylist_deinit(void);void fe_irc_dcc_deinit(void); +void fe_irc_modules_init(void) { fe_irc_dcc_init(); fe_irc_notifylist_init(); } +void fe_irc_modules_deinit(void) { fe_irc_notifylist_deinit(); fe_irc_dcc_deinit(); } diff --git a/src/fe-common/irc/meson.build b/src/fe-common/irc/meson.build new file mode 100644 index 0000000..1789133 --- /dev/null +++ b/src/fe-common/irc/meson.build @@ -0,0 +1,44 @@ +# this file is part of irssi + +libfe_common_irc_a = static_library('fe_common_irc', + files( + 'fe-cap.c', + 'fe-common-irc.c', + 'fe-ctcp.c', + 'fe-events-numeric.c', + 'fe-events.c', + 'fe-irc-channels.c', + 'fe-irc-commands.c', + 'fe-irc-messages.c', + 'fe-irc-queries.c', + 'fe-irc-server.c', + 'fe-ircnet.c', + 'fe-modes.c', + 'fe-netjoin.c', + 'fe-netsplit.c', + 'fe-sasl.c', + 'fe-whois.c', + 'irc-completion.c', + 'module-formats.c', + + 'irc-modules.c', + ), + include_directories : rootinc, + implicit_include_directories : false, + c_args : [ + def_helpdir, + def_themesdir, + ], + dependencies : dep) + +install_headers( + files( + 'fe-irc-channels.h', + 'fe-irc-server.h', + 'module-formats.h', + 'module.h', + ), + subdir : incdir / 'src' / 'fe-common' / 'irc') + +subdir('dcc') +subdir('notifylist') diff --git a/src/fe-common/irc/module-formats.c b/src/fe-common/irc/module-formats.c new file mode 100644 index 0000000..0adb268 --- /dev/null +++ b/src/fe-common/irc/module-formats.c @@ -0,0 +1,184 @@ +/* + module-formats.c : irssi + + Copyright (C) 2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/core/formats.h> + +/* clang-format off */ +FORMAT_REC fecommon_irc_formats[] = { + { MODULE_NAME, "IRC", 0 }, + + /* ---- */ + { NULL, "Server", 0 }, + + { "netsplit", "{netsplit Netsplit} {server $0} <-> {server $1} quits: $2", 3, { 0, 0, 0 } }, + { "netsplit_more", "{netsplit Netsplit} {server $0} <-> {server $1} quits: $2 (+$3 more, use /NETSPLIT to show all of them)", 4, { 0, 0, 0, 1 } }, + { "netsplit_join", "{netjoin Netsplit} over, joins: $0", 1, { 0 } }, + { "netsplit_join_more", "{netjoin Netsplit} over, joins: $0 (+$1 more)", 2, { 0, 1 } }, + { "no_netsplits", "There are no net splits", 0 }, + { "netsplits_header", "%#Nick Channel Server Split server", 0 }, + { "netsplits_line", "%#$[9]0 $[10]1 $[20]2 $3", 4, { 0, 0, 0, 0 } }, + { "netsplits_footer", "", 0 }, + { "network_added", "Network $0 saved", 1, { 0 } }, + { "network_removed", "Network $0 removed", 1, { 0 } }, + { "network_not_found", "Network $0 not found", 1, { 0 } }, + { "network_header", "%#Networks:", 0 }, + { "network_line", "%#$0: $1", 2, { 0, 0 } }, + { "network_footer", "", 0 }, + { "setupserver_header", "%#Server Port Network Settings", 0 }, + { "setupserver_line", "%#%|$[!20]0 $[5]1 $[10]2 $3", 4, { 0, 1, 0, 0 } }, + { "setupserver_footer", "", 0 }, + { "server_waiting_cap_ls", "Waiting for CAP LS response...", 2, { 0, 0 } }, + { "sasl_success", "SASL authentication succeeded", 0 }, + { "sasl_error", "Cannot authenticate via SASL ($0)", 1, { 0 } }, + { "cap_req", "Capabilities requested: $0", 1, { 0 } }, + { "cap_ls", "Capabilities supported: $0", 1, { 0 } }, + { "cap_ack", "Capabilities acknowledged: $0", 1, { 0 } }, + { "cap_nak", "Capabilities refused: $0", 1, { 0 } }, + { "cap_list", "Capabilities currently enabled: $0", 1, { 0 } }, + { "cap_new", "Capabilities now available: $0", 1, { 0 } }, + { "cap_del", "Capabilities removed: $0", 1, { 0 } }, + + /* ---- */ + { NULL, "Channels", 0 }, + + { "joinerror_toomany", "Cannot join to channel {channel $0} (You have joined to too many channels)", 1, { 0 } }, + { "joinerror_full", "Cannot join to channel {channel $0} (Channel is full)", 1, { 0 } }, + { "joinerror_invite", "Cannot join to channel {channel $0} (You must be invited)", 1, { 0 } }, + { "joinerror_banned", "Cannot join to channel {channel $0} (You are banned)", 1, { 0 } }, + { "joinerror_bad_key", "Cannot join to channel {channel $0} (Bad channel key)", 1, { 0 } }, + { "joinerror_bad_mask", "Cannot join to channel {channel $0} (Bad channel mask)", 1, { 0 } }, + { "joinerror_secure_only", "Cannot join to channel {channel $0} (Secure clients only)", 1, { 0 } }, + { "joinerror_unavail", "Cannot join to channel {channel $0} (Channel is temporarily unavailable)", 1, { 0 } }, + { "joinerror_duplicate", "Channel {channel $0} already exists - cannot create it", 1, { 0 } }, + { "channel_rejoin", "Channel {channel $0} is temporarily unavailable, this is normally because of netsplits. Irssi will now automatically try to rejoin back to this channel until the join is successful. Use /RMREJOINS command if you wish to abort this.", 1, { 0 } }, + { "inviting", "Inviting {nick $0} to {channel $1}", 2, { 0, 0 } }, + { "channel_created", "Channel {channelhilight $0} created $1", 2, { 0, 0 } }, + { "url", "Home page for {channelhilight $0}: $1", 2, { 0, 0 } }, + { "topic", "Topic for {channelhilight $0}: $1", 2, { 0, 0 } }, + { "no_topic", "No topic set for {channelhilight $0}", 1, { 0 } }, + { "topic_info", "Topic set by {nick $0} {nickhost $2} {comment $1}", 3, { 0, 0, 0 } }, + { "chanmode_change", "mode/{channelhilight $0} {mode $1} by {nick $2}", 4, { 0, 0, 0, 0 } }, + { "server_chanmode_change", "{netsplit ServerMode}/{channelhilight $0} {mode $1} by {nick $2}", 3, { 0, 0, 0 } }, + { "channel_mode", "mode/{channelhilight $0} {mode $1}", 2, { 0, 0 } }, + { "bantype", "Ban type changed to {channel $0}", 1, { 0 } }, + { "no_bans", "No bans in channel {channel $0}", 1, { 0 } }, + { "banlist", "$0 - {channel $1}: ban {ban $2}", 3, { 1, 0, 0 } }, + { "banlist_long", "$0 - {channel $1}: ban {ban $2} {comment by {nick $3}, $4 secs ago}", 5, { 1, 0, 0, 0, 1 } }, + { "ebanlist", "{channel $0}: ban exception {ban $1}", 2, { 0, 0 } }, + { "ebanlist_long", "{channel $0}: ban exception {ban $1} {comment by {nick $2}, $3 secs ago}", 4, { 0, 0, 0, 1 } }, + { "no_invitelist", "Invite list is empty in channel {channel $0}", 1, { 0 } }, + { "invitelist", "{channel $0}: invite {ban $1}", 2, { 0, 0 } }, + { "invitelist_long", "{channel $0}: invite {ban $1} {comment by {nick $2}, $3 secs ago}", 4, { 0, 0, 0, 1 } }, + { "no_such_channel", "{channel $0}: No such channel", 1, { 0 } }, + { "channel_synced", "Join to {channel $0} was synced in {hilight $1} secs", 2, { 0, 2 } }, + { "server_help_start", "$1", 2, { 0, 0 } }, + { "server_help_txt", "$1", 2, { 0, 0 } }, + { "server_end_of_help", "$1", 2, { 0, 0 } }, + + /* ---- */ + { NULL, "Nick", 0 }, + + { "usermode_change", "Mode change {mode $0} for user {nick $1}", 2, { 0, 0 } }, + { "user_mode", "Your user mode is {mode $0}", 1, { 0 } }, + { "away", "You have been marked as being away", 0 }, + { "unaway", "You are no longer marked as being away", 0 }, + { "nick_away", "{nick $0} is away: $1", 2, { 0, 0 } }, + { "no_such_nick", "{nick $0}: No such nick/channel", 1, { 0 } }, + { "nick_in_use", "Nick {nick $0} is already in use", 1, { 0 } }, + { "nick_unavailable", "Nick {nick $0} is temporarily unavailable", 1, { 0 } }, + { "your_nick_owned", "Your nick is in use by {nick $3} {comment $1@$2}", 4, { 0, 0, 0, 0 } }, + + /* ---- */ + { NULL, "Who queries", 0 }, + + { "whois", "{nick $0} {nickhost $1@$2}%:{whois ircname $3}", 4, { 0, 0, 0, 0 } }, + { "whowas", "{nick $0} {nickhost $1@$2}%:{whois was $3}", 4, { 0, 0, 0, 0 } }, + { "whois_idle", "{whois idle %|$1 days $2 hours $3 mins $4 secs}", 5, { 0, 1, 1, 1, 1 } }, + { "whois_idle_signon", "{whois idle %|$1 days $2 hours $3 mins $4 secs {comment signon: $5}}", 6, { 0, 1, 1, 1, 1, 0 } }, + { "whois_server", "{whois server %|$1 {comment $2}}", 3, { 0, 0, 0 } }, + { "whois_oper", "{whois {hilight $1}}", 2, { 0, 0 } }, + { "whois_modes", "{whois modes $1}", 2, { 0, 0 } }, + { "whois_realhost", "{whois hostname $1-}", 3, { 0, 0, 0 } }, + { "whois_usermode", "{whois usermode $1}", 2, { 0, 0 } }, + { "whois_channels", "{whois channels %|$1}", 2, { 0, 0 } }, + { "whois_away", "{whois away %|$1}", 2, { 0, 0 } }, + { "whois_special", "{whois %|$1}", 2, { 0, 0 } }, + { "whois_extra", "{whois account %|$1}", 2, { 0, 0 } }, + { "end_of_whois", "End of WHOIS", 1, { 0 } }, + { "end_of_whowas", "End of WHOWAS", 1, { 0 } }, + { "whois_not_found", "There is no such nick $0", 1, { 0 } }, + { "who", "%#{channelhilight $[-10]0} %|{nick $[!9]1} $[!3]2 $[!2]3 $4@$5 {comment {hilight $6}}", 8, { 0, 0, 0, 0, 0, 0, 0, 0 } }, + { "end_of_who", "End of /WHO list", 1, { 0 } }, + + /* ---- */ + { NULL, "Your messages", 0 }, + + { "own_notice", "{ownnotice notice $0}$1", 2, { 0, 0 } }, + { "own_action", "{ownaction $0}$1", 3, { 0, 0, 0 } }, + { "own_action_target", "{ownaction_target $0 $2}$1", 3, { 0, 0, 0 } }, + { "own_ctcp", "{ownctcp ctcp $0}$1 $2", 3, { 0, 0, 0 } }, + + /* ---- */ + { NULL, "Received messages", 0 }, + + { "notice_server", "{servernotice $0}$1", 2, { 0, 0 } }, + { "notice_public", "{notice $0{pubnotice_channel $1}}$2", 3, { 0, 0, 0 } }, + { "notice_private", "{notice $0{pvtnotice_host $1}}$2", 3, { 0, 0, 0 } }, + { "action_private", "{pvtaction $0}$2", 3, { 0, 0, 0 } }, + { "action_private_query", "{pvtaction_query $0}$2", 3, { 0, 0, 0 } }, + { "action_public", "{pubaction $0}$1", 2, { 0, 0 } }, + { "action_public_channel", "{pubaction $0{msgchannel $1}}$2", 3, { 0, 0, 0 } }, + + /* ---- */ + { NULL, "CTCPs", 0 }, + + { "ctcp_reply", "CTCP {hilight $0} reply from {nick $1}: $2", 3, { 0, 0, 0 } }, + { "ctcp_reply_channel", "CTCP {hilight $0} reply from {nick $1} in channel {channel $3}: $2", 4, { 0, 0, 0, 0 } }, + { "ctcp_ping_reply", "CTCP {hilight PING} reply from {nick $0}: $1.$[-3.0]2 seconds", 3, { 0, 2, 2 } }, + { "ctcp_requested", "{ctcp {hilight $0} {comment $1} requested CTCP {hilight $2} from {nick $4}}: $3", 5, { 0, 0, 0, 0, 0 } }, + { "ctcp_requested_unknown", "{ctcp {hilight $0} {comment $1} requested unknown CTCP {hilight $2} from {nick $4}}: $3", 5, { 0, 0, 0, 0, 0 } }, + + /* ---- */ + { NULL, "Other server events", 0 }, + + { "online", "Users online: {hilight $0}", 1, { 0 } }, + { "pong", "PONG received from $0: $1", 2, { 0, 0 } }, + { "wallops", "{wallop WALLOP {wallop_nick $0}} $1", 2, { 0, 0 } }, + { "action_wallops", "{wallop WALLOP {wallop_action $0}} $1", 2, { 0, 0 } }, + { "kill", "You were {error killed} by {nick $0} {nickhost $1} {reason $2} {comment Path: $3}", 4, { 0, 0, 0, 0 } }, + { "kill_server", "You were {error killed} by {server $0} {reason $1} {comment Path: $2}", 3, { 0, 0, 0 } }, + { "error", "{error ERROR} $0", 1, { 0 } }, + { "unknown_mode", "Unknown mode character $0", 1, { 0 } }, + { "default_event", "$1", 3, { 0, 0, 0 } }, + { "default_event_server", "[$0] $1", 3, { 0, 0, 0 } }, + + /* ---- */ + { NULL, "Misc", 0 }, + + { "silenced", "Silenced {nick $0}", 1, { 0 } }, + { "unsilenced", "Unsilenced {nick $0}", 1, { 0 } }, + { "silence_line", "{nick $0}: silence {ban $1}", 2, { 0, 0 } }, + { "ask_oper_pass", "Operator password:", 0 }, + { "accept_list", "Accepted users: {hilight $0}", 1, { 0 } }, + + { NULL, NULL, 0 } +}; +/* clang-format on */ diff --git a/src/fe-common/irc/module-formats.h b/src/fe-common/irc/module-formats.h new file mode 100644 index 0000000..a9d29cb --- /dev/null +++ b/src/fe-common/irc/module-formats.h @@ -0,0 +1,154 @@ +#include <irssi/src/fe-common/core/formats.h> + +/* clang-format off */ +enum { + IRCTXT_MODULE_NAME, + + IRCTXT_FILL_1, + + IRCTXT_NETSPLIT, + IRCTXT_NETSPLIT_MORE, + IRCTXT_NETSPLIT_JOIN, + IRCTXT_NETSPLIT_JOIN_MORE, + IRCTXT_NO_NETSPLITS, + IRCTXT_NETSPLITS_HEADER, + IRCTXT_NETSPLITS_LINE, + IRCTXT_NETSPLITS_FOOTER, + IRCTXT_NETWORK_ADDED, + IRCTXT_NETWORK_REMOVED, + IRCTXT_NETWORK_NOT_FOUND, + IRCTXT_NETWORK_HEADER, + IRCTXT_NETWORK_LINE, + IRCTXT_NETWORK_FOOTER, + IRCTXT_SETUPSERVER_HEADER, + IRCTXT_SETUPSERVER_LINE, + IRCTXT_SETUPSERVER_FOOTER, + IRCTXT_SERVER_WAITING_CAP_LS, + IRCTXT_SASL_SUCCESS, + IRCTXT_SASL_ERROR, + IRCTXT_CAP_REQ, + IRCTXT_CAP_LS, + IRCTXT_CAP_ACK, + IRCTXT_CAP_NAK, + IRCTXT_CAP_LIST, + IRCTXT_CAP_NEW, + IRCTXT_CAP_DEL, + + IRCTXT_FILL_2, + + IRCTXT_JOINERROR_TOOMANY, + IRCTXT_JOINERROR_FULL, + IRCTXT_JOINERROR_INVITE, + IRCTXT_JOINERROR_BANNED, + IRCTXT_JOINERROR_BAD_KEY, + IRCTXT_JOINERROR_BAD_MASK, + IRCTXT_JOINERROR_SECURE_ONLY, + IRCTXT_JOINERROR_UNAVAIL, + IRCTXT_JOINERROR_DUPLICATE, + IRCTXT_CHANNEL_REJOIN, + IRCTXT_INVITING, + IRCTXT_CHANNEL_CREATED, + IRCTXT_CHANNEL_URL, + IRCTXT_TOPIC, + IRCTXT_NO_TOPIC, + IRCTXT_TOPIC_INFO, + IRCTXT_CHANMODE_CHANGE, + IRCTXT_SERVER_CHANMODE_CHANGE, + IRCTXT_CHANNEL_MODE, + IRCTXT_BANTYPE, + IRCTXT_NO_BANS, + IRCTXT_BANLIST, + IRCTXT_BANLIST_LONG, + IRCTXT_EBANLIST, + IRCTXT_EBANLIST_LONG, + IRCTXT_NO_INVITELIST, + IRCTXT_INVITELIST, + IRCTXT_INVITELIST_LONG, + IRCTXT_NO_SUCH_CHANNEL, + IRCTXT_CHANNEL_SYNCED, + IRCTXT_SERVER_HELP_START, + IRCTXT_SERVER_HELP_TXT, + IRCTXT_SERVER_END_OF_HELP, + + IRCTXT_FILL_4, + + IRCTXT_USERMODE_CHANGE, + IRCTXT_USER_MODE, + IRCTXT_AWAY, + IRCTXT_UNAWAY, + IRCTXT_NICK_AWAY, + IRCTXT_NO_SUCH_NICK, + IRCTXT_NICK_IN_USE, + IRCTXT_NICK_UNAVAILABLE, + IRCTXT_YOUR_NICK_OWNED, + + IRCTXT_FILL_5, + + IRCTXT_WHOIS, + IRCTXT_WHOWAS, + IRCTXT_WHOIS_IDLE, + IRCTXT_WHOIS_IDLE_SIGNON, + IRCTXT_WHOIS_SERVER, + IRCTXT_WHOIS_OPER, + IRCTXT_WHOIS_MODES, + IRCTXT_WHOIS_REALHOST, + IRCTXT_WHOIS_USERMODE, + IRCTXT_WHOIS_CHANNELS, + IRCTXT_WHOIS_AWAY, + IRCTXT_WHOIS_SPECIAL, + IRCTXT_WHOIS_EXTRA, + IRCTXT_END_OF_WHOIS, + IRCTXT_END_OF_WHOWAS, + IRCTXT_WHOIS_NOT_FOUND, + IRCTXT_WHO, + IRCTXT_END_OF_WHO, + + IRCTXT_FILL_6, + + IRCTXT_OWN_NOTICE, + IRCTXT_OWN_ACTION, + IRCTXT_OWN_ACTION_TARGET, + IRCTXT_OWN_CTCP, + + IRCTXT_FILL_7, + + IRCTXT_NOTICE_SERVER, + IRCTXT_NOTICE_PUBLIC, + IRCTXT_NOTICE_PRIVATE, + IRCTXT_ACTION_PRIVATE, + IRCTXT_ACTION_PRIVATE_QUERY, + IRCTXT_ACTION_PUBLIC, + IRCTXT_ACTION_PUBLIC_CHANNEL, + + IRCTXT_FILL_8, + + IRCTXT_CTCP_REPLY, + IRCTXT_CTCP_REPLY_CHANNEL, + IRCTXT_CTCP_PING_REPLY, + IRCTXT_CTCP_REQUESTED, + IRCTXT_CTCP_REQUESTED_UNKNOWN, + + IRCTXT_FILL_10, + + IRCTXT_ONLINE, + IRCTXT_PONG, + IRCTXT_WALLOPS, + IRCTXT_ACTION_WALLOPS, + IRCTXT_KILL, + IRCTXT_KILL_SERVER, + IRCTXT_ERROR, + IRCTXT_UNKNOWN_MODE, + IRCTXT_DEFAULT_EVENT, + IRCTXT_DEFAULT_EVENT_SERVER, + + IRCTXT_FILL_11, + + IRCTXT_SILENCED, + IRCTXT_UNSILENCED, + IRCTXT_SILENCE_LINE, + IRCTXT_ASK_OPER_PASS, + IRCTXT_ACCEPT_LIST +}; +/* clang-format on */ + +extern FORMAT_REC fecommon_irc_formats[]; diff --git a/src/fe-common/irc/module.h b/src/fe-common/irc/module.h new file mode 100644 index 0000000..ad63c95 --- /dev/null +++ b/src/fe-common/irc/module.h @@ -0,0 +1,4 @@ +#include <irssi/src/common.h> +#include <irssi/src/irc/core/irc.h> + +#define MODULE_NAME "fe-common/irc" diff --git a/src/fe-common/irc/notifylist/Makefile.am b/src/fe-common/irc/notifylist/Makefile.am new file mode 100644 index 0000000..5eecb5f --- /dev/null +++ b/src/fe-common/irc/notifylist/Makefile.am @@ -0,0 +1,18 @@ +noinst_LIBRARIES = libfe_irc_notifylist.a + +AM_CPPFLAGS = \ + -I$(top_builddir) \ + $(GLIB_CFLAGS) \ + -DHELPDIR=\""$(datadir)/irssi/help"\" \ + -DSYSCONFDIR=\""$(sysconfdir)"\" + +libfe_irc_notifylist_a_SOURCES = \ + fe-notifylist.c \ + module-formats.c + +pkginc_fe_common_irc_notifylistdir=$(pkgincludedir)/src/fe-common/irc/notifylist +pkginc_fe_common_irc_notifylist_HEADERS = \ + module.h \ + module-formats.h + +EXTRA_DIST = meson.build diff --git a/src/fe-common/irc/notifylist/Makefile.in b/src/fe-common/irc/notifylist/Makefile.in new file mode 100644 index 0000000..733e022 --- /dev/null +++ b/src/fe-common/irc/notifylist/Makefile.in @@ -0,0 +1,725 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/fe-common/irc/notifylist +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/glib-2.0.m4 \ + $(top_srcdir)/m4/glibtests.m4 $(top_srcdir)/m4/libgcrypt.m4 \ + $(top_srcdir)/m4/libotr.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am \ + $(pkginc_fe_common_irc_notifylist_HEADERS) $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/irssi-config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +am__v_AR_1 = +libfe_irc_notifylist_a_AR = $(AR) $(ARFLAGS) +libfe_irc_notifylist_a_LIBADD = +am_libfe_irc_notifylist_a_OBJECTS = fe-notifylist.$(OBJEXT) \ + module-formats.$(OBJEXT) +libfe_irc_notifylist_a_OBJECTS = $(am_libfe_irc_notifylist_a_OBJECTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/fe-notifylist.Po \ + ./$(DEPDIR)/module-formats.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libfe_irc_notifylist_a_SOURCES) +DIST_SOURCES = $(libfe_irc_notifylist_a_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkginc_fe_common_irc_notifylistdir)" +HEADERS = $(pkginc_fe_common_irc_notifylist_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(top_srcdir)/build-aux/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CHAT_MODULES = @CHAT_MODULES@ +COMMON_LIBS = @COMMON_LIBS@ +COMMON_NOUI_LIBS = @COMMON_NOUI_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FUZZER_LIBS = @FUZZER_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GOBJECT_QUERY = @GOBJECT_QUERY@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBOTR_CFLAGS = @LIBOTR_CFLAGS@ +LIBOTR_LIBS = @LIBOTR_LIBS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +OTR_CFLAGS = @OTR_CFLAGS@ +OTR_LDFLAGS = @OTR_LDFLAGS@ +OTR_LINK_FLAGS = @OTR_LINK_FLAGS@ +OTR_LINK_LIBS = @OTR_LINK_LIBS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL_CFLAGS = @PERL_CFLAGS@ +PERL_EXTRA_OPTS = @PERL_EXTRA_OPTS@ +PERL_FE_LINK_LIBS = @PERL_FE_LINK_LIBS@ +PERL_LDFLAGS = @PERL_LDFLAGS@ +PERL_LINK_FLAGS = @PERL_LINK_FLAGS@ +PERL_LINK_LIBS = @PERL_LINK_LIBS@ +PERL_MM_OPT = @PERL_MM_OPT@ +PERL_MM_PARAMS = @PERL_MM_PARAMS@ +PERL_STATIC_LIBS = @PERL_STATIC_LIBS@ +PERL_USE_LIB = @PERL_USE_LIB@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROG_LIBS = @PROG_LIBS@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TEXTUI_LIBS = @TEXTUI_LIBS@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +installed_test_metadir = @installed_test_metadir@ +installed_testdir = @installed_testdir@ +irc_MODULES = @irc_MODULES@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +otr_module_lib = @otr_module_lib@ +otr_static_lib = @otr_static_lib@ +pdfdir = @pdfdir@ +perl_module_fe_lib = @perl_module_fe_lib@ +perl_module_lib = @perl_module_lib@ +perl_static_fe_lib = @perl_static_fe_lib@ +perl_static_lib = @perl_static_lib@ +perlpath = @perlpath@ +pkgconfigdir = @pkgconfigdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sedpath = @sedpath@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +noinst_LIBRARIES = libfe_irc_notifylist.a +AM_CPPFLAGS = \ + -I$(top_builddir) \ + $(GLIB_CFLAGS) \ + -DHELPDIR=\""$(datadir)/irssi/help"\" \ + -DSYSCONFDIR=\""$(sysconfdir)"\" + +libfe_irc_notifylist_a_SOURCES = \ + fe-notifylist.c \ + module-formats.c + +pkginc_fe_common_irc_notifylistdir = $(pkgincludedir)/src/fe-common/irc/notifylist +pkginc_fe_common_irc_notifylist_HEADERS = \ + module.h \ + module-formats.h + +EXTRA_DIST = meson.build +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/fe-common/irc/notifylist/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/fe-common/irc/notifylist/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +libfe_irc_notifylist.a: $(libfe_irc_notifylist_a_OBJECTS) $(libfe_irc_notifylist_a_DEPENDENCIES) $(EXTRA_libfe_irc_notifylist_a_DEPENDENCIES) + $(AM_V_at)-rm -f libfe_irc_notifylist.a + $(AM_V_AR)$(libfe_irc_notifylist_a_AR) libfe_irc_notifylist.a $(libfe_irc_notifylist_a_OBJECTS) $(libfe_irc_notifylist_a_LIBADD) + $(AM_V_at)$(RANLIB) libfe_irc_notifylist.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fe-notifylist.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/module-formats.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkginc_fe_common_irc_notifylistHEADERS: $(pkginc_fe_common_irc_notifylist_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginc_fe_common_irc_notifylist_HEADERS)'; test -n "$(pkginc_fe_common_irc_notifylistdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_fe_common_irc_notifylistdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkginc_fe_common_irc_notifylistdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_fe_common_irc_notifylistdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_fe_common_irc_notifylistdir)" || exit $$?; \ + done + +uninstall-pkginc_fe_common_irc_notifylistHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginc_fe_common_irc_notifylist_HEADERS)'; test -n "$(pkginc_fe_common_irc_notifylistdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkginc_fe_common_irc_notifylistdir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(pkginc_fe_common_irc_notifylistdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/fe-notifylist.Po + -rm -f ./$(DEPDIR)/module-formats.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pkginc_fe_common_irc_notifylistHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/fe-notifylist.Po + -rm -f ./$(DEPDIR)/module-formats.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pkginc_fe_common_irc_notifylistHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-pkginc_fe_common_irc_notifylistHEADERS \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am \ + uninstall-pkginc_fe_common_irc_notifylistHEADERS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/fe-common/irc/notifylist/fe-notifylist.c b/src/fe-common/irc/notifylist/fe-notifylist.c new file mode 100644 index 0000000..68b98a0 --- /dev/null +++ b/src/fe-common/irc/notifylist/fe-notifylist.c @@ -0,0 +1,250 @@ +/* + fe-notifylist.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/irc/notifylist/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/chatnets.h> +#include <irssi/src/lib-config/iconfig.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/core/levels.h> +#include <irssi/src/irc/core/irc-servers.h> +#include <irssi/src/irc/core/irc-chatnets.h> +#include <irssi/src/irc/notifylist/notifylist.h> + +#include <irssi/src/fe-common/core/themes.h> +#include <irssi/src/fe-common/core/printtext.h> + +/* add the nick of a hostmask to list if it isn't there already */ +static GSList *mask_add_once(GSList *list, const char *mask) +{ + char *str, *ptr; + + g_return_val_if_fail(mask != NULL, NULL); + + ptr = strchr(mask, '!'); + str = ptr == NULL ? g_strdup(mask) : + g_strndup(mask, (int) (ptr-mask)); + + if (i_slist_find_icase_string(list, str) == NULL) + return g_slist_append(list, str); + + g_free(str); + return list; +} + +/* search for online people, print them and update offline list */ +static void print_notify_onserver(IRC_SERVER_REC *server, GSList *nicks, + GSList **offline, const char *desc) +{ + GSList *tmp; + GString *str; + + g_return_if_fail(IS_IRC_SERVER(server)); + g_return_if_fail(offline != NULL); + g_return_if_fail(desc != NULL); + + str = g_string_new(NULL); + for (tmp = nicks; tmp != NULL; tmp = tmp->next) { + char *nick = tmp->data; + + if (!notifylist_ison_server(server, nick)) + continue; + + g_string_append_printf(str, "%s, ", nick); + *offline = g_slist_remove(*offline, nick); + } + + if (str->len > 0) { + g_string_truncate(str, str->len-2); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_NOTIFY_ONLINE, desc, str->str); + } + + g_string_free(str, TRUE); +} + +/* show the notify list, displaying who is on which net */ +static void cmd_notify_show(void) +{ + GSList *nicks, *offline, *tmp; + IRC_SERVER_REC *server; + + if (notifies == NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + IRCTXT_NOTIFY_LIST_EMPTY); + return; + } + + /* build a list containing only the nicks */ + nicks = NULL; + for (tmp = notifies; tmp != NULL; tmp = tmp->next) { + NOTIFYLIST_REC *rec = tmp->data; + + nicks = mask_add_once(nicks, rec->mask); + } + offline = g_slist_copy(nicks); + + /* print the notifies on specific ircnets */ + for (tmp = chatnets; tmp != NULL; tmp = tmp->next) { + IRC_CHATNET_REC *rec = tmp->data; + + if (!IS_IRCNET(rec)) + continue; + + server = (IRC_SERVER_REC *) server_find_chatnet(rec->name); + if (!IS_IRC_SERVER(server)) + continue; + + print_notify_onserver(server, nicks, &offline, rec->name); + } + + /* print the notifies on servers without a specified ircnet */ + for (tmp = servers; tmp != NULL; tmp = tmp->next) { + server = tmp->data; + + if (!IS_IRC_SERVER(server) || server->connrec->chatnet != NULL) + continue; + print_notify_onserver(server, nicks, &offline, server->tag); + } + + /* print offline people */ + if (offline != NULL) { + GString *str; + + str = g_string_new(NULL); + for (tmp = offline; tmp != NULL; tmp = tmp->next) + g_string_append_printf(str, "%s, ", (char *) tmp->data); + + g_string_truncate(str, str->len-2); + printformat(NULL,NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_NOTIFY_OFFLINE, str->str); + g_string_free(str, TRUE); + + g_slist_free(offline); + } + + g_slist_foreach(nicks, (GFunc) g_free, NULL); + g_slist_free(nicks); +} + +static void notifylist_print(NOTIFYLIST_REC *rec) +{ + char *ircnets; + + ircnets = rec->ircnets == NULL ? NULL : + g_strjoinv(",", rec->ircnets); + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_NOTIFY_LIST, + rec->mask, ircnets != NULL ? ircnets : "", + rec->away_check ? "-away" : ""); + + g_free_not_null(ircnets); +} + +static void cmd_notifylist_show(void) +{ + if (notifies == NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + IRCTXT_NOTIFY_LIST_EMPTY); + } else { + g_slist_foreach(notifies, (GFunc) notifylist_print, NULL); + } +} + +static void cmd_notify(const char *data) +{ + if (*data == '\0') { + cmd_notify_show(); + signal_stop(); + } + + if (g_ascii_strncasecmp(data, "-list", 4) == 0) { + cmd_notifylist_show(); + signal_stop(); + } +} + +static void notifylist_joined(IRC_SERVER_REC *server, const char *nick, + const char *username, const char *host, + const char *realname, const char *awaymsg) +{ + g_return_if_fail(nick != NULL); + + printformat(server, nick, MSGLEVEL_CLIENTNOTICE, + IRCTXT_NOTIFY_JOIN, nick, username, host, realname, + server->connrec->chatnet == NULL ? "IRC" : server->connrec->chatnet); +} + +static void notifylist_left(IRC_SERVER_REC *server, const char *nick, + const char *username, const char *host, + const char *realname, const char *awaymsg) +{ + g_return_if_fail(nick != NULL); + + printformat(server, nick, MSGLEVEL_CLIENTNOTICE, IRCTXT_NOTIFY_PART, + nick, username, host, realname, + server->connrec->chatnet == NULL ? "IRC" : server->connrec->chatnet); +} + +static void notifylist_away(IRC_SERVER_REC *server, const char *nick, + const char *username, const char *host, + const char *realname, const char *awaymsg) +{ + g_return_if_fail(nick != NULL); + + if (awaymsg != NULL) { + printformat(server, nick, MSGLEVEL_CLIENTNOTICE, + IRCTXT_NOTIFY_AWAY, nick, username, host, realname, awaymsg, + server->connrec->chatnet == NULL ? "IRC" : server->connrec->chatnet); + } else { + printformat(server, nick, MSGLEVEL_CLIENTNOTICE, + IRCTXT_NOTIFY_UNAWAY, nick, username, host, realname, + server->connrec->chatnet == NULL ? "IRC" : server->connrec->chatnet); + } +} + +void fe_irc_notifylist_init(void) +{ + theme_register(fecommon_irc_notifylist_formats); + + command_bind("notify", NULL, (SIGNAL_FUNC) cmd_notify); + signal_add("notifylist joined", (SIGNAL_FUNC) notifylist_joined); + signal_add("notifylist left", (SIGNAL_FUNC) notifylist_left); + signal_add("notifylist away changed", (SIGNAL_FUNC) notifylist_away); + + command_set_options("notify", "list"); + + settings_check(); + module_register("notifylist", "fe-irc"); +} + +void fe_irc_notifylist_deinit(void) +{ + theme_unregister(); + + command_unbind("notify", (SIGNAL_FUNC) cmd_notify); + signal_remove("notifylist joined", (SIGNAL_FUNC) notifylist_joined); + signal_remove("notifylist left", (SIGNAL_FUNC) notifylist_left); + signal_remove("notifylist away changed", (SIGNAL_FUNC) notifylist_away); +} + +MODULE_ABICHECK(fe_irc_notifylist) diff --git a/src/fe-common/irc/notifylist/meson.build b/src/fe-common/irc/notifylist/meson.build new file mode 100644 index 0000000..2e66c78 --- /dev/null +++ b/src/fe-common/irc/notifylist/meson.build @@ -0,0 +1,21 @@ +# this file is part of irssi + +libfe_irc_notifylist_a = static_library('fe_irc_notifylist', + files( + 'fe-notifylist.c', + 'module-formats.c', + ), + include_directories : rootinc, + implicit_include_directories : false, + c_args : [ + def_helpdir, + def_sysconfdir, + ], + dependencies : dep) + +install_headers( + files( + 'module-formats.h', + 'module.h', + ), + subdir : incdir / 'src' / 'fe-common' / 'irc' / 'notifylist') diff --git a/src/fe-common/irc/notifylist/module-formats.c b/src/fe-common/irc/notifylist/module-formats.c new file mode 100644 index 0000000..12f41fb --- /dev/null +++ b/src/fe-common/irc/notifylist/module-formats.c @@ -0,0 +1,41 @@ +/* + module-formats.c : irssi + + Copyright (C) 2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/core/formats.h> + +FORMAT_REC fecommon_irc_notifylist_formats[] = +{ + { MODULE_NAME, "Notifylist", 0 }, + + /* ---- */ + { NULL, "Notifylist", 0 }, + + { "notify_join", "{nick $0} [$1@$2] [{hilight $3}] has joined to $4", 5, { 0, 0, 0, 0, 0 } }, + { "notify_part", "{nick $0} has left $4", 5, { 0, 0, 0, 0, 0 } }, + { "notify_away", "{nick $0} [$5] [$1@$2] [{hilight $3}] is now away: $4", 6, { 0, 0, 0, 0, 0, 0 } }, + { "notify_unaway", "{nick $0} [$4] [$1@$2] [{hilight $3}] is now unaway", 5, { 0, 0, 0, 0, 0 } }, + { "notify_online", "On $0: {hilight $1}", 2, { 0, 0 } }, + { "notify_offline", "Offline: $0", 1, { 0 } }, + { "notify_list", "$0: $1 $2", 4, { 0, 0, 0, 0 } }, + { "notify_list_empty", "The notify list is empty", 0 }, + + { NULL, NULL, 0 }, +}; diff --git a/src/fe-common/irc/notifylist/module-formats.h b/src/fe-common/irc/notifylist/module-formats.h new file mode 100644 index 0000000..ff3085e --- /dev/null +++ b/src/fe-common/irc/notifylist/module-formats.h @@ -0,0 +1,18 @@ +#include <irssi/src/fe-common/core/formats.h> + +enum { + IRCTXT_MODULE_NAME, + + IRCTXT_FILL_1, + + IRCTXT_NOTIFY_JOIN, + IRCTXT_NOTIFY_PART, + IRCTXT_NOTIFY_AWAY, + IRCTXT_NOTIFY_UNAWAY, + IRCTXT_NOTIFY_ONLINE, + IRCTXT_NOTIFY_OFFLINE, + IRCTXT_NOTIFY_LIST, + IRCTXT_NOTIFY_LIST_EMPTY +}; + +extern FORMAT_REC fecommon_irc_notifylist_formats[]; diff --git a/src/fe-common/irc/notifylist/module.h b/src/fe-common/irc/notifylist/module.h new file mode 100644 index 0000000..0e5ca45 --- /dev/null +++ b/src/fe-common/irc/notifylist/module.h @@ -0,0 +1,4 @@ +#include <irssi/src/common.h> +#include <irssi/src/irc/core/irc.h> + +#define MODULE_NAME "fe-common/irc/notifylist" diff --git a/src/fe-common/meson.build b/src/fe-common/meson.build new file mode 100644 index 0000000..05ab38a --- /dev/null +++ b/src/fe-common/meson.build @@ -0,0 +1,6 @@ +# this file is part of irssi + +subdir('core') +foreach s : chat_modules + subdir(s) +endforeach |