From 23c5df1c8b0bfe539d3fa65802186e6e09e044aa Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Thu, 24 May 2001 12:09:25 +0000 Subject: [PATCH] imported irssi. --- apps/irssi/Makefile.am | 30 + apps/irssi/README | 111 ++ apps/irssi/acconfig.h | 38 + apps/irssi/autogen.sh | 105 ++ apps/irssi/config | 65 + apps/irssi/config.h.in | 77 ++ apps/irssi/configure.in | 784 ++++++++++++ apps/irssi/curses.m4 | 295 +++++ apps/irssi/default-config.h | 67 + apps/irssi/default.theme | 260 ++++ apps/irssi/docs/Makefile.am | 12 + apps/irssi/docs/faq.txt | 36 + apps/irssi/docs/formats.txt | 64 + apps/irssi/docs/help/Makefile.am.gen | 12 + apps/irssi/docs/help/in/.cvsignore | 3 + apps/irssi/docs/help/in/Makefile.am.gen | 5 + apps/irssi/docs/help/in/action.in | 9 + apps/irssi/docs/help/in/admin.in | 8 + apps/irssi/docs/help/in/alias.in | 22 + apps/irssi/docs/help/in/away.in | 28 + apps/irssi/docs/help/in/ban.in | 24 + apps/irssi/docs/help/in/beep.in | 6 + apps/irssi/docs/help/in/bind.in | 100 ++ apps/irssi/docs/help/in/cat.in | 8 + apps/irssi/docs/help/in/cd.in | 8 + apps/irssi/docs/help/in/channel.in | 35 + apps/irssi/docs/help/in/clear.in | 9 + apps/irssi/docs/help/in/connect.in | 12 + apps/irssi/docs/help/in/ctcp.in | 7 + apps/irssi/docs/help/in/cycle.in | 8 + apps/irssi/docs/help/in/date.in | 11 + apps/irssi/docs/help/in/dcc.in | 25 + apps/irssi/docs/help/in/dehilight.in | 9 + apps/irssi/docs/help/in/deop.in | 10 + apps/irssi/docs/help/in/devoice.in | 10 + apps/irssi/docs/help/in/die.in | 7 + apps/irssi/docs/help/in/disconnect.in | 9 + apps/irssi/docs/help/in/echo.in | 5 + apps/irssi/docs/help/in/eval.in | 6 + apps/irssi/docs/help/in/exec.in | 37 + apps/irssi/docs/help/in/format.in | 9 + apps/irssi/docs/help/in/hash.in | 5 + apps/irssi/docs/help/in/help.in | 13 + apps/irssi/docs/help/in/hilight.in | 27 + apps/irssi/docs/help/in/ignore.in | 26 + apps/irssi/docs/help/in/info.in | 6 + apps/irssi/docs/help/in/invite.in | 10 + apps/irssi/docs/help/in/invitelist.in | 9 + apps/irssi/docs/help/in/ircnet.in | 21 + apps/irssi/docs/help/in/ison.in | 7 + apps/irssi/docs/help/in/join.in | 13 + apps/irssi/docs/help/in/kick.in | 16 + apps/irssi/docs/help/in/kickban.in | 10 + apps/irssi/docs/help/in/kill.in | 18 + apps/irssi/docs/help/in/knockout.in | 11 + apps/irssi/docs/help/in/lastlog.in | 22 + apps/irssi/docs/help/in/layout.in | 13 + apps/irssi/docs/help/in/levels.in | 35 + apps/irssi/docs/help/in/links.in | 9 + apps/irssi/docs/help/in/list.in | 9 + apps/irssi/docs/help/in/load.in | 14 + apps/irssi/docs/help/in/log.in | 40 + apps/irssi/docs/help/in/lusers.in | 5 + apps/irssi/docs/help/in/map.in | 5 + apps/irssi/docs/help/in/me.in | 8 + apps/irssi/docs/help/in/mircdcc.in | 11 + apps/irssi/docs/help/in/mode.in | 97 ++ apps/irssi/docs/help/in/motd.in | 7 + apps/irssi/docs/help/in/msg.in | 15 + apps/irssi/docs/help/in/names.in | 21 + apps/irssi/docs/help/in/nctcp.in | 7 + apps/irssi/docs/help/in/netsplit.in | 6 + apps/irssi/docs/help/in/nick.in | 6 + apps/irssi/docs/help/in/note.in | 28 + apps/irssi/docs/help/in/notice.in | 10 + apps/irssi/docs/help/in/notify.in | 15 + apps/irssi/docs/help/in/op.in | 8 + apps/irssi/docs/help/in/oper.in | 10 + apps/irssi/docs/help/in/part.in | 8 + apps/irssi/docs/help/in/perlflush.in | 8 + apps/irssi/docs/help/in/ping.in | 10 + apps/irssi/docs/help/in/query.in | 11 + apps/irssi/docs/help/in/quit.in | 10 + apps/irssi/docs/help/in/quote.in | 8 + apps/irssi/docs/help/in/rawlog.in | 15 + apps/irssi/docs/help/in/reconnect.in | 10 + apps/irssi/docs/help/in/rehash.in | 11 + apps/irssi/docs/help/in/reload.in | 7 + apps/irssi/docs/help/in/restart.in | 12 + apps/irssi/docs/help/in/rmreconns.in | 7 + apps/irssi/docs/help/in/rmrejoins.in | 9 + apps/irssi/docs/help/in/rping.in | 12 + apps/irssi/docs/help/in/run.in | 7 + apps/irssi/docs/help/in/save.in | 8 + apps/irssi/docs/help/in/sconnect.in | 8 + apps/irssi/docs/help/in/scrollback.in | 12 + apps/irssi/docs/help/in/server.in | 30 + apps/irssi/docs/help/in/servlist.in | 19 + apps/irssi/docs/help/in/set.in | 23 + apps/irssi/docs/help/in/silence.in | 22 + apps/irssi/docs/help/in/squery.in | 10 + apps/irssi/docs/help/in/squit.in | 7 + apps/irssi/docs/help/in/stats.in | 20 + apps/irssi/docs/help/in/time.in | 11 + apps/irssi/docs/help/in/toggle.in | 11 + apps/irssi/docs/help/in/topic.in | 8 + apps/irssi/docs/help/in/trace.in | 8 + apps/irssi/docs/help/in/ts.in | 7 + apps/irssi/docs/help/in/unalias.in | 7 + apps/irssi/docs/help/in/unban.in | 11 + apps/irssi/docs/help/in/unignore.in | 7 + apps/irssi/docs/help/in/unload.in | 8 + apps/irssi/docs/help/in/unnotify.in | 7 + apps/irssi/docs/help/in/unquery.in | 7 + apps/irssi/docs/help/in/unsilence.in | 9 + apps/irssi/docs/help/in/uping.in | 12 + apps/irssi/docs/help/in/userhost.in | 7 + apps/irssi/docs/help/in/ver.in | 9 + apps/irssi/docs/help/in/version.in | 8 + apps/irssi/docs/help/in/voice.in | 8 + apps/irssi/docs/help/in/wait.in | 10 + apps/irssi/docs/help/in/wall.in | 11 + apps/irssi/docs/help/in/wallchops.in | 9 + apps/irssi/docs/help/in/wallops.in | 9 + apps/irssi/docs/help/in/who.in | 23 + apps/irssi/docs/help/in/whois.in | 15 + apps/irssi/docs/help/in/whowas.in | 19 + apps/irssi/docs/help/in/window.in | 40 + apps/irssi/docs/help/in/wjoin.in | 9 + apps/irssi/docs/help/in/wquery.in | 8 + apps/irssi/docs/manual.txt | 1030 +++++++++++++++ apps/irssi/docs/startup-HOWTO.html | 647 ++++++++++ apps/irssi/file2header.sh | 5 + apps/irssi/findsyntax.pl | 18 + apps/irssi/irssi-config.in | 6 + apps/irssi/irssi-version.h.in | 3 + apps/irssi/irssi.spec.in | 216 ++++ apps/irssi/src/Makefile.am | 8 + apps/irssi/src/common.h | 89 ++ apps/irssi/src/fe-common/Makefile.am | 1 + apps/irssi/src/fe-common/silc/Makefile.am | 33 + apps/irssi/src/fe-common/silc/fe-channels.c | 29 + .../irssi/src/fe-common/silc/fe-common-silc.c | 45 + .../irssi/src/fe-common/silc/fe-common-silc.h | 8 + .../irssi/src/fe-common/silc/module-formats.c | 211 ++++ .../irssi/src/fe-common/silc/module-formats.h | 181 +++ apps/irssi/src/fe-common/silc/module.h | 9 + apps/irssi/src/fe-common/silc/silc-modules.c | 3 + apps/irssi/src/fe-text/Makefile.am | 53 + apps/irssi/src/fe-text/gui-entry.c | 298 +++++ apps/irssi/src/fe-text/gui-entry.h | 31 + apps/irssi/src/fe-text/gui-expandos.c | 61 + apps/irssi/src/fe-text/gui-printtext.c | 275 ++++ apps/irssi/src/fe-text/gui-printtext.h | 13 + apps/irssi/src/fe-text/gui-readline.c | 666 ++++++++++ apps/irssi/src/fe-text/gui-readline.h | 12 + apps/irssi/src/fe-text/gui-windows.c | 333 +++++ apps/irssi/src/fe-text/gui-windows.h | 36 + apps/irssi/src/fe-text/lastlog.c | 267 ++++ apps/irssi/src/fe-text/mainwindow-activity.c | 57 + apps/irssi/src/fe-text/mainwindows-save.c | 195 +++ apps/irssi/src/fe-text/mainwindows.c | 909 ++++++++++++++ apps/irssi/src/fe-text/mainwindows.h | 42 + apps/irssi/src/fe-text/module-formats.c | 42 + apps/irssi/src/fe-text/module-formats.h | 20 + apps/irssi/src/fe-text/module.h | 6 + apps/irssi/src/fe-text/screen.c | 273 ++++ apps/irssi/src/fe-text/screen.h | 46 + apps/irssi/src/fe-text/silc.c | 298 +++++ apps/irssi/src/fe-text/statusbar-items.c | 765 +++++++++++ apps/irssi/src/fe-text/statusbar.c | 369 ++++++ apps/irssi/src/fe-text/statusbar.h | 63 + apps/irssi/src/fe-text/textbuffer-commands.c | 297 +++++ apps/irssi/src/fe-text/textbuffer-view.c | 1118 +++++++++++++++++ apps/irssi/src/fe-text/textbuffer-view.h | 131 ++ apps/irssi/src/fe-text/textbuffer.c | 613 +++++++++ apps/irssi/src/fe-text/textbuffer.h | 90 ++ apps/irssi/src/lib-config/Makefile.am | 15 + apps/irssi/src/lib-config/get.c | 310 +++++ apps/irssi/src/lib-config/iconfig.h | 144 +++ apps/irssi/src/lib-config/module.h | 6 + apps/irssi/src/lib-config/parse.c | 360 ++++++ apps/irssi/src/lib-config/set.c | 171 +++ apps/irssi/src/lib-config/write.c | 341 +++++ apps/irssi/src/lib-popt/.cvsignore | 7 + apps/irssi/src/lib-popt/Makefile.am | 11 + apps/irssi/src/lib-popt/findme.c | 54 + apps/irssi/src/lib-popt/findme.h | 10 + apps/irssi/src/lib-popt/popt.c | 593 +++++++++ apps/irssi/src/lib-popt/popt.h | 123 ++ apps/irssi/src/lib-popt/poptconfig.c | 144 +++ apps/irssi/src/lib-popt/popthelp.c | 301 +++++ apps/irssi/src/lib-popt/poptint.h | 54 + apps/irssi/src/lib-popt/poptparse.c | 98 ++ apps/irssi/src/silc/Makefile.am | 5 + apps/irssi/src/silc/silc.c | 4 + apps/irssi/stamp.h.in | 0 apps/irssi/syntax.pl | 57 + 198 files changed, 16310 insertions(+) create mode 100644 apps/irssi/Makefile.am create mode 100644 apps/irssi/README create mode 100644 apps/irssi/acconfig.h create mode 100755 apps/irssi/autogen.sh create mode 100644 apps/irssi/config create mode 100644 apps/irssi/config.h.in create mode 100644 apps/irssi/configure.in create mode 100644 apps/irssi/curses.m4 create mode 100644 apps/irssi/default-config.h create mode 100644 apps/irssi/default.theme create mode 100644 apps/irssi/docs/Makefile.am create mode 100644 apps/irssi/docs/faq.txt create mode 100644 apps/irssi/docs/formats.txt create mode 100644 apps/irssi/docs/help/Makefile.am.gen create mode 100644 apps/irssi/docs/help/in/.cvsignore create mode 100644 apps/irssi/docs/help/in/Makefile.am.gen create mode 100644 apps/irssi/docs/help/in/action.in create mode 100644 apps/irssi/docs/help/in/admin.in create mode 100644 apps/irssi/docs/help/in/alias.in create mode 100644 apps/irssi/docs/help/in/away.in create mode 100644 apps/irssi/docs/help/in/ban.in create mode 100644 apps/irssi/docs/help/in/beep.in create mode 100644 apps/irssi/docs/help/in/bind.in create mode 100644 apps/irssi/docs/help/in/cat.in create mode 100644 apps/irssi/docs/help/in/cd.in create mode 100644 apps/irssi/docs/help/in/channel.in create mode 100644 apps/irssi/docs/help/in/clear.in create mode 100644 apps/irssi/docs/help/in/connect.in create mode 100644 apps/irssi/docs/help/in/ctcp.in create mode 100644 apps/irssi/docs/help/in/cycle.in create mode 100644 apps/irssi/docs/help/in/date.in create mode 100644 apps/irssi/docs/help/in/dcc.in create mode 100644 apps/irssi/docs/help/in/dehilight.in create mode 100644 apps/irssi/docs/help/in/deop.in create mode 100644 apps/irssi/docs/help/in/devoice.in create mode 100644 apps/irssi/docs/help/in/die.in create mode 100644 apps/irssi/docs/help/in/disconnect.in create mode 100644 apps/irssi/docs/help/in/echo.in create mode 100644 apps/irssi/docs/help/in/eval.in create mode 100644 apps/irssi/docs/help/in/exec.in create mode 100644 apps/irssi/docs/help/in/format.in create mode 100644 apps/irssi/docs/help/in/hash.in create mode 100644 apps/irssi/docs/help/in/help.in create mode 100644 apps/irssi/docs/help/in/hilight.in create mode 100644 apps/irssi/docs/help/in/ignore.in create mode 100644 apps/irssi/docs/help/in/info.in create mode 100644 apps/irssi/docs/help/in/invite.in create mode 100644 apps/irssi/docs/help/in/invitelist.in create mode 100644 apps/irssi/docs/help/in/ircnet.in create mode 100644 apps/irssi/docs/help/in/ison.in create mode 100644 apps/irssi/docs/help/in/join.in create mode 100644 apps/irssi/docs/help/in/kick.in create mode 100644 apps/irssi/docs/help/in/kickban.in create mode 100644 apps/irssi/docs/help/in/kill.in create mode 100644 apps/irssi/docs/help/in/knockout.in create mode 100644 apps/irssi/docs/help/in/lastlog.in create mode 100644 apps/irssi/docs/help/in/layout.in create mode 100644 apps/irssi/docs/help/in/levels.in create mode 100644 apps/irssi/docs/help/in/links.in create mode 100644 apps/irssi/docs/help/in/list.in create mode 100644 apps/irssi/docs/help/in/load.in create mode 100644 apps/irssi/docs/help/in/log.in create mode 100644 apps/irssi/docs/help/in/lusers.in create mode 100644 apps/irssi/docs/help/in/map.in create mode 100644 apps/irssi/docs/help/in/me.in create mode 100644 apps/irssi/docs/help/in/mircdcc.in create mode 100644 apps/irssi/docs/help/in/mode.in create mode 100644 apps/irssi/docs/help/in/motd.in create mode 100644 apps/irssi/docs/help/in/msg.in create mode 100644 apps/irssi/docs/help/in/names.in create mode 100644 apps/irssi/docs/help/in/nctcp.in create mode 100644 apps/irssi/docs/help/in/netsplit.in create mode 100644 apps/irssi/docs/help/in/nick.in create mode 100644 apps/irssi/docs/help/in/note.in create mode 100644 apps/irssi/docs/help/in/notice.in create mode 100644 apps/irssi/docs/help/in/notify.in create mode 100644 apps/irssi/docs/help/in/op.in create mode 100644 apps/irssi/docs/help/in/oper.in create mode 100644 apps/irssi/docs/help/in/part.in create mode 100644 apps/irssi/docs/help/in/perlflush.in create mode 100644 apps/irssi/docs/help/in/ping.in create mode 100644 apps/irssi/docs/help/in/query.in create mode 100644 apps/irssi/docs/help/in/quit.in create mode 100644 apps/irssi/docs/help/in/quote.in create mode 100644 apps/irssi/docs/help/in/rawlog.in create mode 100644 apps/irssi/docs/help/in/reconnect.in create mode 100644 apps/irssi/docs/help/in/rehash.in create mode 100644 apps/irssi/docs/help/in/reload.in create mode 100644 apps/irssi/docs/help/in/restart.in create mode 100644 apps/irssi/docs/help/in/rmreconns.in create mode 100644 apps/irssi/docs/help/in/rmrejoins.in create mode 100644 apps/irssi/docs/help/in/rping.in create mode 100644 apps/irssi/docs/help/in/run.in create mode 100644 apps/irssi/docs/help/in/save.in create mode 100644 apps/irssi/docs/help/in/sconnect.in create mode 100644 apps/irssi/docs/help/in/scrollback.in create mode 100644 apps/irssi/docs/help/in/server.in create mode 100644 apps/irssi/docs/help/in/servlist.in create mode 100644 apps/irssi/docs/help/in/set.in create mode 100644 apps/irssi/docs/help/in/silence.in create mode 100644 apps/irssi/docs/help/in/squery.in create mode 100644 apps/irssi/docs/help/in/squit.in create mode 100644 apps/irssi/docs/help/in/stats.in create mode 100644 apps/irssi/docs/help/in/time.in create mode 100644 apps/irssi/docs/help/in/toggle.in create mode 100644 apps/irssi/docs/help/in/topic.in create mode 100644 apps/irssi/docs/help/in/trace.in create mode 100644 apps/irssi/docs/help/in/ts.in create mode 100644 apps/irssi/docs/help/in/unalias.in create mode 100644 apps/irssi/docs/help/in/unban.in create mode 100644 apps/irssi/docs/help/in/unignore.in create mode 100644 apps/irssi/docs/help/in/unload.in create mode 100644 apps/irssi/docs/help/in/unnotify.in create mode 100644 apps/irssi/docs/help/in/unquery.in create mode 100644 apps/irssi/docs/help/in/unsilence.in create mode 100644 apps/irssi/docs/help/in/uping.in create mode 100644 apps/irssi/docs/help/in/userhost.in create mode 100644 apps/irssi/docs/help/in/ver.in create mode 100644 apps/irssi/docs/help/in/version.in create mode 100644 apps/irssi/docs/help/in/voice.in create mode 100644 apps/irssi/docs/help/in/wait.in create mode 100644 apps/irssi/docs/help/in/wall.in create mode 100644 apps/irssi/docs/help/in/wallchops.in create mode 100644 apps/irssi/docs/help/in/wallops.in create mode 100644 apps/irssi/docs/help/in/who.in create mode 100644 apps/irssi/docs/help/in/whois.in create mode 100644 apps/irssi/docs/help/in/whowas.in create mode 100644 apps/irssi/docs/help/in/window.in create mode 100644 apps/irssi/docs/help/in/wjoin.in create mode 100644 apps/irssi/docs/help/in/wquery.in create mode 100644 apps/irssi/docs/manual.txt create mode 100644 apps/irssi/docs/startup-HOWTO.html create mode 100755 apps/irssi/file2header.sh create mode 100755 apps/irssi/findsyntax.pl create mode 100644 apps/irssi/irssi-config.in create mode 100644 apps/irssi/irssi-version.h.in create mode 100644 apps/irssi/irssi.spec.in create mode 100644 apps/irssi/src/Makefile.am create mode 100644 apps/irssi/src/common.h create mode 100644 apps/irssi/src/fe-common/Makefile.am create mode 100644 apps/irssi/src/fe-common/silc/Makefile.am create mode 100644 apps/irssi/src/fe-common/silc/fe-channels.c create mode 100644 apps/irssi/src/fe-common/silc/fe-common-silc.c create mode 100644 apps/irssi/src/fe-common/silc/fe-common-silc.h create mode 100644 apps/irssi/src/fe-common/silc/module-formats.c create mode 100644 apps/irssi/src/fe-common/silc/module-formats.h create mode 100644 apps/irssi/src/fe-common/silc/module.h create mode 100644 apps/irssi/src/fe-common/silc/silc-modules.c create mode 100644 apps/irssi/src/fe-text/Makefile.am create mode 100644 apps/irssi/src/fe-text/gui-entry.c create mode 100644 apps/irssi/src/fe-text/gui-entry.h create mode 100644 apps/irssi/src/fe-text/gui-expandos.c create mode 100644 apps/irssi/src/fe-text/gui-printtext.c create mode 100644 apps/irssi/src/fe-text/gui-printtext.h create mode 100644 apps/irssi/src/fe-text/gui-readline.c create mode 100644 apps/irssi/src/fe-text/gui-readline.h create mode 100644 apps/irssi/src/fe-text/gui-windows.c create mode 100644 apps/irssi/src/fe-text/gui-windows.h create mode 100644 apps/irssi/src/fe-text/lastlog.c create mode 100644 apps/irssi/src/fe-text/mainwindow-activity.c create mode 100644 apps/irssi/src/fe-text/mainwindows-save.c create mode 100644 apps/irssi/src/fe-text/mainwindows.c create mode 100644 apps/irssi/src/fe-text/mainwindows.h create mode 100644 apps/irssi/src/fe-text/module-formats.c create mode 100644 apps/irssi/src/fe-text/module-formats.h create mode 100644 apps/irssi/src/fe-text/module.h create mode 100644 apps/irssi/src/fe-text/screen.c create mode 100644 apps/irssi/src/fe-text/screen.h create mode 100644 apps/irssi/src/fe-text/silc.c create mode 100644 apps/irssi/src/fe-text/statusbar-items.c create mode 100644 apps/irssi/src/fe-text/statusbar.c create mode 100644 apps/irssi/src/fe-text/statusbar.h create mode 100644 apps/irssi/src/fe-text/textbuffer-commands.c create mode 100644 apps/irssi/src/fe-text/textbuffer-view.c create mode 100644 apps/irssi/src/fe-text/textbuffer-view.h create mode 100644 apps/irssi/src/fe-text/textbuffer.c create mode 100644 apps/irssi/src/fe-text/textbuffer.h create mode 100644 apps/irssi/src/lib-config/Makefile.am create mode 100644 apps/irssi/src/lib-config/get.c create mode 100644 apps/irssi/src/lib-config/iconfig.h create mode 100644 apps/irssi/src/lib-config/module.h create mode 100644 apps/irssi/src/lib-config/parse.c create mode 100644 apps/irssi/src/lib-config/set.c create mode 100644 apps/irssi/src/lib-config/write.c create mode 100644 apps/irssi/src/lib-popt/.cvsignore create mode 100644 apps/irssi/src/lib-popt/Makefile.am create mode 100644 apps/irssi/src/lib-popt/findme.c create mode 100644 apps/irssi/src/lib-popt/findme.h create mode 100644 apps/irssi/src/lib-popt/popt.c create mode 100644 apps/irssi/src/lib-popt/popt.h create mode 100644 apps/irssi/src/lib-popt/poptconfig.c create mode 100644 apps/irssi/src/lib-popt/popthelp.c create mode 100644 apps/irssi/src/lib-popt/poptint.h create mode 100644 apps/irssi/src/lib-popt/poptparse.c create mode 100644 apps/irssi/src/silc/Makefile.am create mode 100644 apps/irssi/src/silc/silc.c create mode 100644 apps/irssi/stamp.h.in create mode 100755 apps/irssi/syntax.pl diff --git a/apps/irssi/Makefile.am b/apps/irssi/Makefile.am new file mode 100644 index 00000000..304c44d8 --- /dev/null +++ b/apps/irssi/Makefile.am @@ -0,0 +1,30 @@ +# create default-config.h +config.h: default-config.h default-theme.h + +default-config.h: $(srcdir)/config + $(srcdir)/file2header.sh $(srcdir)/config default_config > default-config.h +default-theme.h: $(srcdir)/default.theme + $(srcdir)/file2header.sh $(srcdir)/default.theme default_theme > default-theme.h + +SUBDIRS = src docs + +confdir = $(sysconfdir)/irssi +conf_DATA = config default.theme + +noinst_HEADERS = irssi-version.h + +EXTRA_DIST = \ + autogen.sh \ + curses.m4 \ + README \ + file2header.sh \ + irssi.spec \ + irssi.spec.in \ + $(conf_DATA) \ + irssi-config.in \ + irssi-icon.png + +## make rpms +rpm: Makefile + $(MAKE) dist + rpm -ta --clean $(PACKAGE)-$(VERSION).tar.gz diff --git a/apps/irssi/README b/apps/irssi/README new file mode 100644 index 00000000..7f8fdc77 --- /dev/null +++ b/apps/irssi/README @@ -0,0 +1,111 @@ + + +irssi, http://irssi.org + + + * FILES + + - docs/ directory contains several documents: + - startup-HOWTO.txt - new users should read this + - manual.txt - manual I started writing but didn't get it finished :) + - perl.txt - Perl scripting help + - formats.txt - How to use colors, etc. with irssi + - faq.txt - Frequently Asked Questions + - special_vars.txt - some predefined $variables you can use with irssi + + + * ABOUT + +Irssi is a modular IRC client that currently has only text mode user +interface, but 80-90% of the code isn't text mode specific, so other +UIs could be created pretty easily. Also, Irssi isn't really even IRC +specific anymore, there's already a working SILC (http://www.silcnet.org) +module available. Support for other protocols like ICQ could be created +some day too. + + + * FEATURES + +So what's so great about Irssi? Here's a list of some features I can +think of currently: + + - Optional automation - There's lots of things Irssi does for you + automatically that some people like and others just hate. Things like: + nick completion, creating new window for newly joined channel, creating + queries when msgs/notices are received or when you send a msg, closing + queries when it's been idle for some time, etc. + + - Multiserver friendy - I think Irssi has clearly the best support + for handling multiple server connections. You can have as many as you + want in as many ircnets as you want. Having several connections in one + server works too, for example when you hit the (ircnet's) 10 + channels/connection limit you can just create another connection and + you hardly notice it. If connection to server is lost, Irssi tries to + connect back until it's successful. Also channels you were joined + before disconnection are restored, even if they're "temporarily + unavailable" because of netsplits, Irssi keeps rejoining back to them. + Also worth noticing - there's not that stupid "server is bound to this + window, if this window gets closed the connection closes" thing that + ircII based clients have. + + - Channel automation - You can specify what channels to join to + immediately after connected to some server or IRC network. After joined + to channel, Irssi can automatically request ops for you (or do + anything, actually) from channel's bots. + + - Window content saving - Say /LAYOUT SAVE when you've put all the + channels and queries to their correct place, and after restarting + Irssi, the channels will be joined back into windows where they were + saved. + + - Tab completing anything - You can complete lots of things with tab: + nicks, commands, command -options, file names, settings, text format + names, channels and server names. There's also an excellent /msg + completion that works transparently with multiple IRC networks. + Completing channel nicks is also pretty intelligent, it first goes + through the people who have talked to you recently, then the people who + have talked to anyone recently and only then it fallbacks to rest of + the nicks. You can also complete a set of words you've specified, for + example homepage changes it to your actual home page URL. + + - Excellent logging - You can log any way you want and as easily or + hard as you want. With autologging Irssi logs everything to specified + directory, one file per channel/nick. ircII style /WINDOW LOG ON is + also supported. There's also the "hard way" of logging - /LOG command + which lets you specify exactly what you wish to log and where. Log + rotating is supported with all the different logging methods, you can + specify how often you want it to rotate and what kind of time stamp to + use. + + - Excellent ignoring - You can most probably ignore anything any way + you want. Nick masks, words, regular expressions. You can add + exceptions to ignores. You can ignore other people's replies in + channels to nicks you have ignored. You can also specify that the + specific ignores work only in specific channel(s). + + - Lastlog and scrollback handling - /LASTLOG command has some new + features: -new option checks only lines that came since you last did + /LASTLOG command, -away option checks new lines since you last went + away. Regular expression matches work also, of course. Going to some + wanted place at scrollback has always been hard with non-GUI clients. A + search command that jumps around in scrollback in GUI-style is still + missing from Irssi, but there's something that's almost as good as it. + /LASTLOG always shows timestamps when the line was printed, even if you + didn't have timestamps on. Now doing /SB GOTO jumps + directly to the position in scrollback you wanted. Great feature when + you want to browse a bit of the discussion what happened when someone + said your name (as seen in awaylog) or topic was changed (/last + -topics) + + + * BUGS / SUGGESTIONS + +See TODO file if it is already listed in there - if not send me email.. + + + * AUTHOR + + - Timo Sirainen + - tss@iki.fi + - cras@ircnet/efnet/opn/silc + - #irssi@ircnet/opn diff --git a/apps/irssi/acconfig.h b/apps/irssi/acconfig.h new file mode 100644 index 00000000..75350596 --- /dev/null +++ b/apps/irssi/acconfig.h @@ -0,0 +1,38 @@ +/* paths */ +#undef SYSCONFDIR +#undef HELPDIR +#undef PLUGINSDIR + +/* misc.. */ +#undef MEM_DEBUG +#undef HAVE_IPV6 +#undef HAVE_POPT_H +#undef HAVE_SOCKS_H +#undef HAVE_PL_PERL +#undef HAVE_STATIC_PERL +#undef HAVE_GMODULE +#undef WANT_BIG5 + +/* macros/curses checks */ +#undef HAS_CURSES +#undef USE_SUNOS_CURSES +#undef USE_BSD_CURSES +#undef USE_SYSV_CURSES +#undef USE_NCURSES +#undef NO_COLOR_CURSES +#undef SCO_FLAVOR + +/* our own curses checks */ +#undef USE_CURSES_WINDOWS +#undef HAVE_NCURSES_USE_DEFAULT_COLORS +#undef HAVE_CURSES_IDCOK +#undef HAVE_CURSES_RESIZETERM +#undef HAVE_CURSES_WRESIZE +#undef USE_CURSES_WINDOWS + +/* nls */ +#undef ENABLE_NLS +#undef HAVE_CATGETS +#undef HAVE_GETTEXT +#undef HAVE_LC_MESSAGES +#undef HAVE_STPCPY diff --git a/apps/irssi/autogen.sh b/apps/irssi/autogen.sh new file mode 100755 index 00000000..daf8a990 --- /dev/null +++ b/apps/irssi/autogen.sh @@ -0,0 +1,105 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +PKG_NAME="Irssi SILC" + +if test ! -f $srcdir/configure.in; then + echo -n "**Error**: Directory \`$srcdir\' does not look like the" + echo " top-level $PKG_NAME directory" + exit 1 +fi + +# get versions +version_date=`date +%Y%m%d` + +echo "/* automatically created by autogen.sh */" > irssi-version.h.in +echo "#define IRSSI_VERSION \"@VERSION@\"" >> irssi-version.h.in +echo "#define IRSSI_VERSION_DATE \"$version_date\"" >> irssi-version.h.in + +# create help files +echo "Creating help files..." +perl syntax.pl + +files=`echo docs/help/in/*.in|sed -e 's,docs/help/in/Makefile.in ,,' -e 's,docs/help/in/,!,g' -e 's/\.in /.in ?/g'` +cat docs/help/in/Makefile.am.gen|sed "s/@HELPFILES@/$files/g"|sed 's/?/\\?/g'|tr '!?' '\t\n' > docs/help/in/Makefile.am + +files=`echo $files|sed 's/\.in//g'` +cat docs/help/Makefile.am.gen|sed "s/@HELPFILES@/$files/g"|sed 's/?/\\?/g'|tr '!?' '\t\n' > docs/help/Makefile.am + +# .html -> .txt with lynx +echo "Documentation: html -> txt..." +lynx -dump -nolist docs/startup-HOWTO.html > docs/startup-HOWTO.txt + +echo "Checking auto* tools..." + +# *********** a bit modified GNOME's macros/autogen.sh ********** +DIE=0 + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`autoconf' installed to compile $PKG_NAME." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +(grep "^AM_PROG_LIBTOOL" $srcdir/configure.in >/dev/null) && { + (libtool --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`libtool' installed to compile $PKG_NAME." + echo "Get ftp://ftp.gnu.org/pub/gnu/libtool-1.2d.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + } +} + +(automake --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`automake' installed to compile $PKG_NAME." + echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + NO_AUTOMAKE=yes +} + + +# if no automake, don't bother testing for aclocal +test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: Missing \`aclocal'. The version of \`automake'" + echo "installed doesn't appear recent enough." + echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +case $CC in +xlc ) + am_opt=--include-deps;; +esac + +rm -f aclocal.m4 +if grep "^AM_PROG_LIBTOOL" configure.in >/dev/null; then + echo "Running libtoolize..." + libtoolize --force --copy +fi +aclocalinclude="$ACLOCAL_FLAGS -I ." +echo "Running aclocal $aclocalinclude ..." +aclocal $aclocalinclude +if grep "^AM_CONFIG_HEADER" configure.in >/dev/null; then + echo "Running autoheader..." + autoheader +fi +echo "Running autoconf ..." +autoconf +echo "Running automake --gnu $am_opt ..." +automake --add-missing --gnu $am_opt + +conf_flags="--enable-maintainer-mode --enable-compile-warnings" #--enable-iso-c diff --git a/apps/irssi/config b/apps/irssi/config new file mode 100644 index 00000000..876b4020 --- /dev/null +++ b/apps/irssi/config @@ -0,0 +1,65 @@ +lservers = ( + { address = "irc.stealth.net"; chatnet = IRCNet; port = 6668; }, + { address = "irc.efnet.net"; chatnet = EFNet; port = 6667; }, + { address = "irc.undernet.org"; chatnet = Undernet; port = 6667; }, + { address = "irc.dal.net"; chatnet = DALnet; port = 6667; }, + { address = "irc.openprojects.net"; chatnet = OPN; port = 6667; }, + { address = "irc.ptlink.net"; chatnet = PTlink; port = 6667; } + { address = "silc.pspt.fi"; chatnet = SILC; port = 706; } +); + +chatnets = { + IRCNet = { type = "IRC"; max_kicks = 4; max_modes = 3; max_msgs = 5; max_whois = 4; }; + EFNet = { type = "IRC"; max_kicks = 4; max_modes = 4; max_msgs = 3; }; + Undernet = { type = "IRC"; max_kicks = 4; max_modes = 3; max_msgs = 3; max_query_chans = "1"; }; + DALNet = { type = "IRC"; max_kicks = 4; max_modes = 6; max_msgs = 3; }; + OPN = { type = "IRC"; max_kicks = 1; max_modes = 6; max_msgs = 100; }; + PTLink = { type = "IRC"; max_kicks = 1; max_modes = 6; max_msgs = 100; }; + SILC = { type = "SILC"; }; +}; + +channels = ( + { name = "#irssi"; chatnet = ircnet; autojoin = No; }, + { name = "#irssi"; chatnet = opn; autojoin = No; }, + { name = "#silc"; chatnet = silc; autojoin = No; } +); + +aliases = { + J = "join"; + WJOIN = "join -window"; + WQUERY = "query -window"; + LEAVE = "part"; + BYE = "quit"; + EXIT = "quit"; + SIGNOFF = "quit"; + DESCRIBE = "action"; + DATE = "time"; + HOST = "userhost"; + LAST = "lastlog"; + SAY = "msg *"; + WI = "whois"; + WII = "whois $0 $0"; + WW = "whowas"; + W = "who"; + N = "names"; + M = "msg"; + T = "topic"; + C = "clear"; + CL = "clear"; + K = "kick"; + KB = "kickban"; + KN = "knockout"; + BANS = "ban"; + B = "ban"; + MUB = "unban *"; + UB = "unban"; + IG = "ignore"; + UNIG = "unignore"; + SB = "scrollback"; + UMODE = "mode $N"; + WC = "window close"; + WN = "window new hide"; + SV = "say Irssi $J - http://irssi.org/"; + GOTO = "sb goto"; + CHAT = "dcc chat"; +}; diff --git a/apps/irssi/config.h.in b/apps/irssi/config.h.in new file mode 100644 index 00000000..14b09153 --- /dev/null +++ b/apps/irssi/config.h.in @@ -0,0 +1,77 @@ +/* config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define if you need to in order for stat and other things to work. */ +#undef _POSIX_SOURCE + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* misc.. */ +#undef MEM_DEBUG +#undef HAVE_IPV6 +#undef HAVE_POPT_H +#undef HAVE_SOCKS_H +#undef HAVE_PL_PERL +#undef HAVE_STATIC_PERL +#undef HAVE_GMODULE +#undef WANT_BIG5 + +/* macros/curses checks */ +#undef HAS_CURSES +#undef USE_SUNOS_CURSES +#undef USE_BSD_CURSES +#undef USE_SYSV_CURSES +#undef USE_NCURSES +#undef NO_COLOR_CURSES +#undef SCO_FLAVOR + +/* our own curses checks */ +#undef USE_CURSES_WINDOWS +#undef HAVE_NCURSES_USE_DEFAULT_COLORS +#undef HAVE_CURSES_IDCOK +#undef HAVE_CURSES_RESIZETERM +#undef HAVE_CURSES_WRESIZE +#undef USE_CURSES_WINDOWS + +/* Define if you have the fcntl function. */ +#undef HAVE_FCNTL + +/* Define if you have the mkfifo function. */ +#undef HAVE_MKFIFO + +/* Define if you have the header file. */ +#undef HAVE_DIRENT_H + +/* Define if you have the header file. */ +#undef HAVE_LIBINTL_H + +/* Define if you have the header file. */ +#undef HAVE_REGEX_H + +/* Define if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define if you have the header file. */ +#undef HAVE_STRING_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_UTSNAME_H + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Name of package */ +#undef PACKAGE + +/* Version number of package */ +#undef VERSION + +/* Define to 'int' if doesn't define. */ +#undef socklen_t + diff --git a/apps/irssi/configure.in b/apps/irssi/configure.in new file mode 100644 index 00000000..87063b4e --- /dev/null +++ b/apps/irssi/configure.in @@ -0,0 +1,784 @@ +AC_INIT(src) + +AM_CONFIG_HEADER(config.h) +AM_INIT_AUTOMAKE(Irssi SILC, 0.7.98.3) + +AM_MAINTAINER_MODE + +AC_ISC_POSIX +AC_PROG_CC +AC_PROG_CPP +AC_STDC_HEADERS +AC_ARG_PROGRAM +AM_PROG_LIBTOOL + +dnl * ahem.. :) we don't want static libraries for modules +if test "x$lt_target" = "x"; then + if test "$target" = "NONE"; then + lt_target="$host" + else + lt_target="$target" + fi +fi +dnl * --disable-static isn't a good idea, complain if it's used +if test "x$enable_static" = "xno"; then + AC_ERROR([Don't give --disable-static option to configure]) +fi + +${CONFIG_SHELL-/bin/sh} $ac_aux_dir/ltconfig --no-reexec \ +$libtool_flags --disable-static --output=libtool-shared --no-verify $ac_aux_dir/ltmain.sh $lt_target \ +|| { echo "configure: error: libtool configure failed" 1>&2; exit 1; } +${CONFIG_SHELL-/bin/sh} $ac_aux_dir/ltconfig --no-reexec \ +$libtool_flags --no-verify $ac_aux_dir/ltmain.sh $host \ +|| { echo "configure: error: libtool configure failed" 1>&2; exit 1; } + +AC_CHECK_HEADERS(string.h stdlib.h unistd.h dirent.h sys/ioctl.h libintl.h) + +# check posix headers.. +AC_CHECK_HEADERS(sys/time.h sys/utsname.h regex.h) + +AC_ARG_WITH(big5, +[ --with-big5 Build with tr-Chinese Big5 support], + if test x$withval = xyes; then + want_big5=yes + else + if test "x$withval" = xno; then + want_big5=no + else + want_big5=yes + fi + fi, + want_big5=no) + +AC_ARG_WITH(socks, +[ --with-socks Build with socks support], + if test x$withval = xyes; then + want_socks=yes + else + if test "x$withval" = xno; then + want_socks=no + else + want_socks=yes + fi + fi, + want_socks=no) + +AC_ARG_WITH(textui, +[ --with-textui Build text frontend], + if test x$withval = xyes; then + want_textui=yes + else + if test "x$withval" = xno; then + want_textui=no + else + want_textui=yes + fi + fi, + want_textui=yes) + +AC_ARG_WITH(bot, +[ --with-bot Build irssi-bot], + if test x$withval = xyes; then + want_irssibot=yes + else + if test "x$withval" = xno; then + want_irssibot=no + else + want_irssibot=yes + fi + fi, + want_irssibot=no) + +AC_ARG_WITH(proxy, +[ --with-proxy Build irssi-proxy], + if test x$withval = xyes; then + want_irssiproxy=yes + else + if test "x$withval" = xno; then + want_irssiproxy=no + else + want_irssiproxy=yes + fi + fi, + want_irssiproxy=no) + + +AC_ARG_WITH(modules, +[ --with-modules Specify what modules to build in binary], + if test x$withval != xyes -a x$withval != xno; then + build_modules="$withval" + fi) + +if test "x$prefix" = "xNONE"; then + PERL_LIB_DIR="" +else + PERL_LIB_DIR="$prefix" +fi + +AC_ARG_ENABLE(perl-path, +[ --enable-perl-path=dir Specify where to install the Perl libraries for irssi], + if test x$enableval = xyes; then + want_perl=yes + else + if test "x$enableval" = xno; then + want_perl=no + else + want_perl=yes + PERL_LIB_DIR="$enableval" + perl_lib_dir_given=yes + fi + fi, + want_perl=yes) + +AC_ARG_ENABLE(perl, +[ --enable-perl[=yes|no|static] Build with Perl support - also specifies + if it should be built into main irssi binary + (static) or as module (default)], + if test x$enableval = xyes; then + want_perl=yes + elif test x$enableval = xstatic; then + want_perl=static + else + want_perl=no + fi, + want_perl=yes) + +AC_ARG_WITH(tests, +[ --with-tests Run all the tests], + if test x$withval != xno; then + TEST_DIR=test + fi, + TEST_DIR=) +AC_SUBST(TEST_DIR) + +AC_ARG_ENABLE(curses-windows, +[ --enable-curses-windows Use curses windows], + if test x$enableval != xno; then + AC_DEFINE(USE_CURSES_WINDOWS) + fi, + AC_DEFINE(USE_CURSES_WINDOWS)) + +AC_ARG_ENABLE(memdebug, +[ --enable-memdebug Enable memory debugging], + if test x$enableval = xyes; then + want_memdebug=yes + else + if test "x$enableval" = xno; then + want_memdebug=no + else + want_memdebug=yes + fi + fi, + want_memdebug=no) + +AC_ARG_ENABLE(ipv6, +[ --enable-ipv6 Enable IPv6 support], + if test x$enableval = xyes; then + want_ipv6=yes + else + if test "x$enableval" = xno; then + want_ipv6=no + else + want_ipv6=yes + fi + fi, + want_ipv6=no) + +dnl ** +dnl ** just some generic stuff... +dnl ** + +AC_CHECK_FUNCS(mkfifo fcntl) + +AC_CHECK_LIB(socket, socket, [ + PROG_LIBS="$PROG_LIBS -lsocket" +]) + +AC_CHECK_LIB(nsl, inet_addr, [ + PROG_LIBS="$PROG_LIBS -lnsl" +], -lsocket) + +dnl * gcc specific options +if test "x$ac_cv_prog_gcc" = "xyes"; then + CFLAGS="$CFLAGS -Wall" +fi + +dnl * OS specific options +case "$host_os" in + hpux*) + CFLAGS="$CFLAGS -D_XOPEN_SOURCE_EXTENDED" + ;; + *) + ;; +esac + +dnl * socklen_t - AC_CHECK_TYPE() would be _really_ useful if it only would +dnl * accept header files where to find the typedef.. +AC_MSG_CHECKING([for socklen_t]) +AC_CACHE_VAL(irssi_cv_type_socklen_t, +[AC_TRY_COMPILE([ +#include +#include ], +[socklen_t t;], +irssi_cv_type_socklen_t=yes, +irssi_cv_type_socklen_t=no, +)]) +if test $irssi_cv_type_socklen_t = no; then +AC_DEFINE(socklen_t, int, Define to 'int' if doesn't define.) +fi +AC_MSG_RESULT($irssi_cv_type_socklen_t) + +dnl ** +dnl ** check for socks +dnl ** + +if test "x$want_socks" = "xyes"; then + AC_CHECK_LIB(socks, connect, [ + PROG_LIBS="$PROG_LIBS -lsocks" + AC_CHECK_HEADER(socks.h, [ + AC_DEFINE(HAVE_SOCKS_H) + CFLAGS="$CFLAGS -DSOCKS" + AC_MSG_RESULT(["socks5 library found, building with it"]) + ], [ + AC_MSG_RESULT(["socks4 library found, building with it"]) + CFLAGS="$CFLAGS -Dconnect=Rconnect -Dgetsockname=Rgetsockname -Dgetpeername=Rgetpeername -Dbind=Rbind -Daccept=Raccept -Dlisten=Rlisten -Dselect=Rselect" + ]) + ]) +fi + +dnl ** +dnl ** fe-text checks +dnl ** + +AC_PATH_PROG(sedpath, sed) + +AC_DEFUN(AC_CHECK_GLIBDIR,[ + AC_MSG_CHECKING([whether GLib is unpacked to irssi dir]) + + GLIB_DIR=`for d in *; do test -f $d/glib.h && echo $d; done` + if test "x$GLIB_DIR" != "x"; then + dnl * glib in irssi directory, use it + AC_MSG_RESULT([yes, using it]) + + dnl * we have to do this at this point so we know what libs gmodule needs + if test ! -f $GLIB_DIR/.libs/libglib.a; then + echo + echo "configuring GLib ..." + echo + cd $GLIB_DIR + if test ! -f glib-config; then + ./configure + fi + ${MAKE-make} + cd .. + echo + fi + + GLIB_LDEXTRA=`$GLIB_DIR/glib-config --libs gmodule|$sedpath -e 's/-lglib//' -e 's/-lgmodule//' -e 's,-L/usr/local/lib ,,'|$sedpath 's/ \+/ /g'` + full_glib_dir="`pwd`/$GLIB_DIR" + GLIB_CFLAGS="-I$full_glib_dir -I$full_glib_dir/gmodule" + if test -f $full_glib_dir/.libs/libglib.a; then + GLIB_LIBS="$full_glib_dir/.libs/libglib.a $GLIB_LDEXTRA" + if test -f $full_glib_dir/gmodule/.libs/libgmodule.a; then + GLIB_LIBS="$GLIB_LIBS $full_glib_dir/gmodule/.libs/libgmodule.a" + AC_DEFINE(HAVE_GMODULE) + fi + else + GLIB_LIBS="$full_glib_dir/libglib.a $GLIB_LDEXTRA" + if test -f $full_glib_dir/gmodule/libgmodule.a; then + GLIB_LIBS="$GLIB_LIBS $full_glib_dir/gmodule/libgmodule.a" + AC_DEFINE(HAVE_GMODULE) + fi + fi + AC_SUBST(GLIB_CFLAGS) + AC_SUBST(GLIB_LIBS) + else + AC_MSG_RESULT([no]) + fi +]) + +AC_CHECK_GLIBDIR + +if test "x$GLIB_DIR" = "x"; then + AM_PATH_GLIB(1.2.0,,, gmodule) + if test "x$GLIB_LIBS" = "x"; then + echo "*** trying without -lgmodule" + glib_config_args= + AM_PATH_GLIB(1.2.0) + else + AC_DEFINE(HAVE_GMODULE) + fi + + if test "x$GLIB_LIBS" = "x"; then + echo + echo "*** If you don't have GLIB, you can get it from ftp://ftp.gtk.org" + echo "*** If you can't install GLIB anywhere or if you don't want to," + echo "*** you can just unpack it to Irssi's source directory and" + echo "*** Irssi will automatically compile and use it." + echo + + dnl * I think it's pretty safe to assume GLib 1.2.9 since the next + dnl * will be 2.0 (or 1.4?) and it's not sure if irssi compiles + dnl * with it (yea, just a few weeks after I put this text for 1.2.8 + dnl * the 1.2.9 came :) .. and then .10 + glib_file=glib-1.2.10.tar.gz + + dlcmd= + if test "x`ncftpget 2>/dev/null|grep -i ncftp`" != "x"; then + dlcmd="ncftpget ftp://ftp.gtk.org/pub/gtk/v1.2/$glib_file" + fi + if test "x`wget 2>/dev/null|grep -i wget`" != "x"; then + dlcmd="wget http://irssi.org/files/$glib_file" + fi + if test "x$dlcmd" != "x"; then + echo "*** I can download GLib for you now. If you don't want to, press CTRL-C now." + read answer + eval $dlcmd + if `gunzip $glib_file`; then + glib_file=`echo $glib_file|$sedpath s/\.gz$//` + if `tar xf $glib_file`; then + rm -f $glib_file + AC_CHECK_GLIBDIR + fi + fi + fi + + if test "x$GLIB_LIBS" = "x"; then + AC_ERROR([GLIB is required to build irssi.]) + fi + fi +fi + +PROG_LIBS="$PROG_LIBS $GLIB_LIBS" + +dnl ** +dnl ** check if we can link dynamic libraries to modules +dnl ** also checks if libraries are built to .libs dir +dnl ** + +AC_MSG_CHECKING([if we can link dynamic libraries with modules]) +DYNLIB_MODULES=no + +dnl ** compile object file +cat > conftest.c < +int modfunc(void){return (int)floor(1.2);} +EOF + +./libtool --mode=compile $CC $CFLAGS -c conftest.c 2> /dev/null > /dev/null +if test ! -s conftest.lo; then + AC_ERROR([error compiling test module]) +fi + +dnl ** link to library +./libtool --mode=link $CC $CFLAGS $LDFLAGS -rpath /usr/lib conftest.lo -lm -o libconftest.la > /dev/null +if test ! -s .libs/libconftest.a; then + AC_ERROR([error, can't even find .a library]) +fi + +dnl ** check if dynamic linking worked +libfile=`grep '^library_names' libconftest.la|$sedpath "s/library_names='\(.*\)'.*/\1/"|$sedpath 's/.* \([[^ ]]*\)$/\1/'` +if test ! -s .libs/$libfile; then + AC_MSG_RESULT([no, error linking test module]) +else + cat > conftest.c < +main() { +GModule *m; int (*modfunc)(void); +m = g_module_open(".libs/$libfile", 0); +if (!m) g_print("error loading: %s", g_module_error()); +else if (!g_module_symbol(m, "modfunc", (gpointer *) &modfunc)) + g_print("modfunc() symbol not found from module"); +else if (modfunc() == 1) g_print("ok"); else g_print("wrong result?! 1 vs %d", modfunc()); +return 0; } +EOF + $CC $CFLAGS conftest.c -o conftest $GLIB_CFLAGS $GLIB_LIBS 2> /dev/null > /dev/null + if test ! -s conftest; then + AC_MSG_RESULT([no, error compiling test program]) + else + status="`./conftest`" + if test "x$status" = "xok"; then + DYNLIB_MODULES=yes + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no, error running: $status]) + fi + fi +fi +rm -rf conftest conftest.* libconftest.* .libs + +dnl ** +dnl ** curses checks +dnl ** + +if test "x$want_textui" = "xyes"; then + AC_CHECK_CURSES + + if test "x$has_ncurses" != "x"; then + AC_CHECK_LIB(ncurses, use_default_colors, [ + AC_DEFINE(HAVE_NCURSES_USE_DEFAULT_COLORS) + ],, $CURSES_LIBS) + AC_CHECK_LIB(ncurses, idcok, [ + AC_DEFINE(HAVE_CURSES_IDCOK) + ],, $CURSES_LIBS) + AC_CHECK_LIB(ncurses, resizeterm, [ + AC_DEFINE(HAVE_CURSES_RESIZETERM) + ],, $CURSES_LIBS) + AC_CHECK_LIB(ncurses, wresize, [ + AC_DEFINE(HAVE_CURSES_WRESIZE) + ],, $CURSES_LIBS) + elif test "x$has_curses" = "xtrue"; then + AC_CHECK_LIB(curses, idcok, [ + AC_DEFINE(HAVE_CURSES_IDCOK) + ],, $CURSES_LIBS) + AC_CHECK_LIB(curses, resizeterm, [ + AC_DEFINE(HAVE_CURSES_RESIZETERM) + ],, $CURSES_LIBS) + AC_CHECK_LIB(curses, wresize, [ + AC_DEFINE(HAVE_CURSES_WRESIZE) + ],, $CURSES_LIBS) + else + want_textui=no + curses_error=yes + fi +else + has_curses=false +fi + +dnl ** +dnl ** perl checks +dnl ** + +if test "$want_perl" != "no"; then + AC_PATH_PROG(perlpath, perl) + AC_MSG_CHECKING(for working Perl support) + + if test "x$perlpath" = "x"; then + perl_check_error="perl binary not found" + else + PERL_CFLAGS=`$perlpath -MExtUtils::Embed -e ccopts 2>/dev/null` + fi + + if test "x$PERL_CFLAGS" = "x"; then + perl_check_error="Error getting perl CFLAGS" + AC_MSG_RESULT([not found, building without Perl]) + want_perl=no + else + PERL_LDFLAGS=`$perlpath -MExtUtils::Embed -e ldopts 2>/dev/null` + + dnl * Perl 5.004 and older use perl_xxx variables while + dnl * later use PL_perl_xxx variables .. + have_pl_perl=`$perlpath -e 'print $] < 5.005 ? "no" : "yes";'` + if test "x$have_pl_perl" = "xyes"; then + AC_DEFINE(HAVE_PL_PERL) + fi + + if test "x$DYNLIB_MODULES" = "xno" -a "$want_perl" != "static"; then + dnl * wanted perl as module, won't get it. + want_perl=static + perl_mod_error="Dynamic library dependencies don't work with modules" + fi + + if test "$want_perl" != "static"; then + dnl * dynaloader.a -> libperl_dynaloader.la + DYNALOADER_A=`echo $PERL_LDFLAGS | $perlpath -pe 's/^(.* )*([[^ ]]*DynaLoader\.a).*/\2/'` + fi + + dnl * don't check libperl.a if dynaloader.a wasn't found.. + if test "x$DYNALOADER_A" != "x"; then + dnl * find either libperl.a or libperl.so + LIBPERL_A=`echo "$PERL_LDFLAGS -L/usr/lib"|$perlpath -e 'foreach (split(/ /, )) { if (/^-L(.*)/) { my $dir=$1; if (\`ls $dir/libperl.so* 2>/dev/null\`) { print "-lperl"; last; }; if (-e "$dir/libperl.a") { print "$dir/libperl.a"; last } } };'` + if test "x$LIBPERL_A" = "x"; then + perl_mod_error="Didn't find location of -lperl" + DYNALOADER_A= + elif test "$LIBPERL_A" = "-lperl"; then + LIBPERL_A= + fi + fi + + dnl * remove all database stuffs + dnl * nsl is already in ldflags + dnl * libc is of course linked without needing -lc + dnl * -rdynamic must not be in LIBADD line + for word in -ldb -ldbm -lndbm -lgdbm -lc -rdynamic; do + PERL_LDFLAGS=`echo $PERL_LDFLAGS | $sedpath -e "s/$word //" -e "s/$word$//"` + done + + case "$host_os" in + linux*) + PERL_LDFLAGS=`echo $PERL_LDFLAGS | $sedpath -e 's/-lposix //' -e 's/-lposix$//'` + ;; + hpux*) + if test "x$ac_cv_prog_gcc" = "xyes"; then + PERL_CFLAGS=`echo $PERL_CFLAGS | $sedpath -e 's/-Ae //' -e 's/-Ae$//'` + PERL_LDFLAGS=`echo $PERL_LDFLAGS | $sedpath -e 's/-Ae //' -e 's/-Ae$//'` + fi + ;; + *) + ;; + esac + + dnl * check that perl's ldflags actually work + AC_CACHE_VAL(irssi_cv_lib_perl_works, [ + echo "main(){perl_alloc(); return 0;}" > conftest.c + $CC $CFLAGS conftest.c -o conftest $LDFLAGS $PERL_LDFLAGS 2> /dev/null > /dev/null + if test -s conftest; then + irssi_cv_lib_perl_works=yes + else + irssi_cv_lib_perl_works=no + fi + ]) + + if test "x$irssi_cv_lib_perl_works" = "xno"; then + perl_check_error="Error linking with perl libraries: $PERL_LDFLAGS" + AC_MSG_RESULT([error linking with perl libraries, building without Perl]) + want_perl=no + fi + fi + + if test "x$want_perl" != "xno"; then + if test "x$want_perl" = "xstatic"; then + AC_MSG_RESULT(ok) + elif test "x$DYNALOADER_A" = "x"; then + AC_MSG_RESULT([error parsing ldopts, building Perl into irssi binary instead of as module]) + want_perl=static + else + AC_MSG_RESULT(ok) + PERL_LDFLAGS=`echo $PERL_LDFLAGS | $perlpath -pe 's/^(.* )*[[^ ]]*DynaLoader\.a/\1libperl_dynaloader.la/'` + if test "x$LIBPERL_A" != "x"; then + PERL_LDFLAGS=`echo $PERL_LDFLAGS | $sedpath -e 's/-lperl /libperl_orig.la /' -e 's/-lperl$/libperl_orig.la$/'` + fi + AC_SUBST(LIBPERL_A) + AC_SUBST(DYNALOADER_A) + fi + + if test "x$want_perl" = "xstatic"; then + dnl * building with static perl support + dnl * all PERL_LDFLAGS linking is done in fe-text + PERL_LDFLAGS="../perl/libperl_static.la $PERL_LDFLAGS" + PERL_LINK_LIBS="$PERL_LDFLAGS" + PERL_FE_LINK_LIBS="../perl/libfe_perl_static.la" + PERL_LDFLAGS= + AC_DEFINE(HAVE_STATIC_PERL) + + dnl * build only static library of perl module + perl_module_lib= + perl_module_fe_lib= + perl_static_lib=libperl_static.la + perl_static_fe_lib=libfe_perl_static.la + PERL_LIBTOOL='$(SHELL) $(top_builddir)/libtool' + else + dnl * build dynamic library of perl module, + dnl * use libtool-shared to prevent creating of + dnl * libperl.a + perl_module_lib=libperl_core.la + perl_module_fe_lib=libfe_perl.la + perl_static_lib= + perl_static_fe_lib= + PERL_LIBTOOL='$(SHELL) $(top_builddir)/libtool-shared' + fi + AC_SUBST(perl_module_lib) + AC_SUBST(perl_static_lib) + AC_SUBST(perl_module_fe_lib) + AC_SUBST(perl_static_fe_lib) + AC_SUBST(PERL_LIBTOOL) + + AC_SUBST(PERL_LINK_LIBS) + AC_SUBST(PERL_FE_LINK_LIBS) + + AC_SUBST(PERL_LDFLAGS) + AC_SUBST(PERL_CFLAGS) + AC_SUBST(PERL_LIB_DIR) + fi +fi + +dnl ** check what we want to build +AM_CONDITIONAL(BUILD_TEXTUI, test "$want_textui" = "yes") +AM_CONDITIONAL(BUILD_IRSSIBOT, test "$want_irssibot" = "yes") +AM_CONDITIONAL(BUILD_IRSSIPROXY, test "$want_irssiproxy" = "yes") +AM_CONDITIONAL(BUILD_PLUGINS, test "$want_plugins" = "yes") +AM_CONDITIONAL(BUILD_SERVERTEST, test "x$TEST_DIR" != "x") +AM_CONDITIONAL(HAVE_PERL, test "$want_perl" != "no") +AM_CONDITIONAL(HAVE_STATIC_PERL, test "$want_perl" = "static") + +AC_SUBST(PROG_LIBS) + +dnl ** +dnl ** Keep all the libraries here so each frontend doesn't need to +dnl ** keep track of them all +dnl ** +dnl ** (these could be made configurable) + +CHAT_MODULES="silc" +silc_MODULES="" +if test "x$build_modules" != "x"; then + silc_MODULES="$silc_MODULES $build_modules" +fi +#PROG_LIBS="$PROG_LIBS -lsilc" + +dnl **************************************** + +AC_SUBST(CHAT_MODULES) +AC_SUBST(silc_MODULES) + +CORE_LIBS="../core/libcore.a ../lib-config/libirssi_config.a ../lib-popt/libpopt.a" +FE_COMMON_LIBS="" + +CHAT_LIBS="" +for c in $CHAT_MODULES; do + module_inits="" + module_deinits="" + fe_module_inits="" + fe_module_deinits="" + CHAT_LIBS="$CHAT_LIBS ../$c/lib$c.a ../$c/core/lib${c}_core.a" + if test -f $srcdir/src/fe-common/$c/module.h; then + FE_COMMON_LIBS="$FE_COMMON_LIBS../fe-common/$c/libfe_common_$c.a " + fi + for s in `eval echo \\$${c}_MODULES`; do + CHAT_LIBS="$CHAT_LIBS ../$c/$s/.libs/lib${c}_$s.a" + module_inits="$module_inits ${c}_${s}_init();" + module_deinits="${c}_${s}_deinit(); $module_deinits" + if test -f $srcdir/src/fe-common/$c/$s/module.h; then + FE_COMMON_LIBS="$FE_COMMON_LIBS../fe-common/$c/$s/.libs/libfe_${c}_$s.a " + fe_module_inits="$fe_module_inits fe_${c}_${s}_init();" + fe_module_deinits="fe_${c}_${s}_deinit(); $fe_module_deinits" + fi + done + + file="$srcdir/src/$c/$c.c" + echo "/* this file is automatically generated by configure - don't change */" > $file + echo "void ${c}_core_init(void); void ${c}_core_deinit(void);" >> $file + if test "x$module_inits" != "x"; then + echo "$module_inits" | $sedpath -e 's/()/(void)/g' -e 's/ /void /g' >> $file + echo "$module_deinits" | $sedpath -e 's/ *$//' -e 's/()/(void)/g' -e 's/ /void /g' -e 's/^/void /' >> $file + fi + echo "void ${c}_init(void) { ${c}_core_init(); $module_inits }" >> $file + echo "void ${c}_deinit(void) { $module_deinits ${c}_core_deinit(); }" >> $file + + if test -f $srcdir/src/fe-common/$c/module.h; then + file="$srcdir/src/fe-common/$c/${c}-modules.c" + echo "/* this file is automatically generated by configure - don't change */" > $file + if test "x$fe_module_inits" != "x"; then + echo "$fe_module_inits" | $sedpath -e 's/()/(void)/g' -e 's/ /void /g' >> $file + echo "$fe_module_deinits" | $sedpath -e 's/ *$//' -e 's/()/(void)/g' -e 's/ /void /g' -e 's/^/void /' >> $file + fi + echo "void fe_${c}_modules_init(void) { $fe_module_inits }" >> $file + echo "void fe_${c}_modules_deinit(void) { $fe_module_deinits }" >> $file + fi +done + +FE_COMMON_LIBS="$FE_COMMON_LIBS../fe-common/core/libfe_common_core.a" + +dnl ** common libraries needed by frontends +COMMON_NOUI_LIBS="$CHAT_LIBS $CORE_LIBS $INTLLIBS" +COMMON_LIBS="$FE_COMMON_LIBS $COMMON_NOUI_LIBS" +AC_SUBST(COMMON_NOUI_LIBS) +AC_SUBST(COMMON_LIBS) + +dnl ** +dnl ** memory debugging +dnl ** + +if test "x$want_memdebug" = "xyes"; then + AC_DEFINE(MEM_DEBUG) +fi +AM_CONDITIONAL(BUILD_MEMDEBUG, test "x$want_memdebug" = "xyes") + +dnl ** +dnl ** tr-Chinese Big5 support +dnl ** + +if test "x$want_big5" = "xyes"; then + AC_DEFINE(WANT_BIG5) +fi + +dnl ** +dnl ** IPv6 support +dnl ** + +if test "x$want_ipv6" = "xyes"; then + AC_DEFINE(HAVE_IPV6) +fi + +AC_OUTPUT( +Makefile +src/Makefile +src/core/Makefile +src/fe-common/Makefile +src/fe-common/core/Makefile +src/fe-common/silc/Makefile +src/fe-text/Makefile +src/lib-config/Makefile +src/lib-popt/Makefile +src/silc/Makefile +src/silc/core/Makefile +docs/Makefile +docs/help/Makefile +docs/help/in/Makefile +stamp.h +irssi.spec +irssi-version.h +irssi-config) + +dnl ** for building from objdir +if test "x$want_perl" != "xno"; then + old_dir=`pwd` && cd $srcdir && whole_dir=`pwd` && cd $old_dir + + if test "x$old_dir" != "x$whole_dir"; then + for file in $whole_dir/src/perl/*.[[ch]] $whole_dir/src/perl/libperl_orig.la $whole_dir/src/perl/libperl_dynaloader.la $whole_dir/src/perl/common/typemap $whole_dir/src/perl/common/module.h $whole_dir/src/perl/common/*.xs $whole_dir/src/perl/irc/typemap $whole_dir/src/perl/irc/module.h $whole_dir/src/perl/irc/*.xs; do + link=`echo $file|$sedpath "s?$whole_dir/??"` + rm -f $link + $LN_S $file $link + done + fi +fi + +echo + +if test "x$curses_error" != "xyes"; then + echo "Building text frontend ..... : $want_textui" +else + echo "Building text frontend ..... : NO!!" + echo " - Because curses was not found, specify the path to it with" + echo " --with-curses=/dir and make sure you have the curses headers" + echo " installed (usually in ncurses-devel package)" +fi +echo "Building irssi bot ......... : $want_irssibot" +echo "Building irssi proxy ....... : $want_irssiproxy" +echo "Building with IPv6 support . : $want_ipv6" + +if test "x$want_perl" = "xstatic"; then + echo "Building with Perl support . : static (in irssi binary)" +elif test "x$want_perl" = "xyes"; then + echo "Building with Perl support . : module" +else + if test "x$perl_check_error" = "x"; then + echo "Building with Perl support . : no" + else + echo "Building with Perl support . : NO!" + echo " - $perl_check_error" + fi +fi + +if test "x$want_perl" != "xno" -a "x$perl_mod_error" != "x"; then + echo " - NOTE: Perl support will be compiled statically to irssi, not as" + echo " a module as requested. Reason:" + echo " $perl_mod_error" +fi + +if test "x$want_perl" = "xyes"; then + if test "x$PERL_LIB_DIR" = "x"; then + echo "Perl library directory ..... : (default - usually /usr/local/lib/perl_site)" + else + echo "Perl library directory ..... : $PERL_LIB_DIR" + if test "x$perl_lib_dir_given" != "xyes"; then + echo " - NOTE: This was automatically set to the same directory you gave with" + echo " --prefix. If you want the perl libraries to install to their 'correct'" + echo " path, you'll need to give --enable-perl-path= (nothing after '=') option" + echo " to configure. Anyway, installing perl to this directory should work" + echo " just as well.." + fi + fi +fi +echo "Install prefix ............. : $prefix" + diff --git a/apps/irssi/curses.m4 b/apps/irssi/curses.m4 new file mode 100644 index 00000000..7865b1cb --- /dev/null +++ b/apps/irssi/curses.m4 @@ -0,0 +1,295 @@ +dnl Curses detection: Munged from Midnight Commander's configure.in +dnl +dnl What it does: +dnl ============= +dnl +dnl - Determine which version of curses is installed on your system +dnl and set the -I/-L/-l compiler entries and add a few preprocessor +dnl symbols +dnl - Do an AC_SUBST on the CURSES_INCLUDEDIR and CURSES_LIBS so that +dnl @CURSES_INCLUDEDIR@ and @CURSES_LIBS@ will be available in +dnl Makefile.in's +dnl - Modify the following configure variables (these are the only +dnl curses.m4 variables you can access from within configure.in) +dnl CURSES_INCLUDEDIR - contains -I's and possibly -DRENAMED_CURSES if +dnl an ncurses.h that's been renamed to curses.h +dnl is found. +dnl CURSES_LIBS - sets -L and -l's appropriately +dnl CFLAGS - if --with-sco, add -D_SVID3 +dnl has_curses - exports result of tests to rest of configure +dnl +dnl Usage: +dnl ====== +dnl 1) Add lines indicated below to acconfig.h +dnl 2) call AC_CHECK_CURSES after AC_PROG_CC in your configure.in +dnl 3) Instead of #include you should use the following to +dnl properly locate ncurses or curses header file +dnl +dnl #if defined(USE_NCURSES) && !defined(RENAMED_NCURSES) +dnl #include +dnl #else +dnl #include +dnl #endif +dnl +dnl 4) Make sure to add @CURSES_INCLUDEDIR@ to your preprocessor flags +dnl 5) Make sure to add @CURSES_LIBS@ to your linker flags or LIBS +dnl +dnl Notes with automake: +dnl - call AM_CONDITIONAL(HAS_CURSES, test "$has_curses" = true) from +dnl configure.in +dnl - your Makefile.am can look something like this +dnl ----------------------------------------------- +dnl INCLUDES= blah blah blah $(CURSES_INCLUDEDIR) +dnl if HAS_CURSES +dnl CURSES_TARGETS=name_of_curses_prog +dnl endif +dnl bin_PROGRAMS = other_programs $(CURSES_TARGETS) +dnl other_programs_SOURCES = blah blah blah +dnl name_of_curses_prog_SOURCES = blah blah blah +dnl other_programs_LDADD = blah +dnl name_of_curses_prog_LDADD = blah $(CURSES_LIBS) +dnl ----------------------------------------------- +dnl +dnl +dnl The following lines should be added to acconfig.h: +dnl ================================================== +dnl +dnl /*=== Curses version detection defines ===*/ +dnl /* Found some version of curses that we're going to use */ +dnl #undef HAS_CURSES +dnl +dnl /* Use SunOS SysV curses? */ +dnl #undef USE_SUNOS_CURSES +dnl +dnl /* Use old BSD curses - not used right now */ +dnl #undef USE_BSD_CURSES +dnl +dnl /* Use SystemV curses? */ +dnl #undef USE_SYSV_CURSES +dnl +dnl /* Use Ncurses? */ +dnl #undef USE_NCURSES +dnl +dnl /* If you Curses does not have color define this one */ +dnl #undef NO_COLOR_CURSES +dnl +dnl /* Define if you want to turn on SCO-specific code */ +dnl #undef SCO_FLAVOR +dnl +dnl /* Set to reflect version of ncurses * +dnl * 0 = version 1.* +dnl * 1 = version 1.9.9g +dnl * 2 = version 4.0/4.1 */ +dnl #undef NCURSES_970530 +dnl +dnl /*=== End new stuff for acconfig.h ===*/ +dnl + + +AC_DEFUN(AC_CHECK_CURSES,[ + search_ncurses=true + screen_manager="" + has_curses=false + + CFLAGS=${CFLAGS--O} + + AC_SUBST(CURSES_LIBS) + AC_SUBST(CURSES_INCLUDEDIR) + + AC_ARG_WITH(sco, + [ --with-sco Use this to turn on SCO-specific code],[ + if test x$withval = xyes; then + AC_DEFINE(SCO_FLAVOR) + CFLAGS="$CFLAGS -D_SVID3" + fi + ]) + + AC_ARG_WITH(sunos-curses, + [ --with-sunos-curses Used to force SunOS 4.x curses],[ + if test x$withval = xyes; then + AC_USE_SUNOS_CURSES + fi + ]) + + AC_ARG_WITH(osf1-curses, + [ --with-osf1-curses Used to force OSF/1 curses],[ + if test x$withval = xyes; then + AC_USE_OSF1_CURSES + fi + ]) + + AC_ARG_WITH(vcurses, + [ --with-vcurses[=incdir] Used to force SysV curses], + if test x$withval != xyes; then + CURSES_INCLUDEDIR="-I$withval" + fi + AC_USE_SYSV_CURSES + ) + + AC_ARG_WITH(ncurses, + [ --with-ncurses[=dir] Compile with ncurses/locate base dir], + if test x$withval = xno ; then + search_ncurses=false + elif test x$withval != xyes ; then + AC_NCURSES($withval/include, ncurses.h, -L$withval/lib -lncurses, -I$withval/include, "ncurses on $withval/include") + fi + ) + + if $search_ncurses + then + AC_SEARCH_NCURSES() + fi +]) + + +AC_DEFUN(AC_USE_SUNOS_CURSES, [ + search_ncurses=false + screen_manager="SunOS 4.x /usr/5include curses" + AC_MSG_RESULT(Using SunOS 4.x /usr/5include curses) + AC_DEFINE(USE_SUNOS_CURSES) + AC_DEFINE(HAS_CURSES) + has_curses=true + AC_DEFINE(NO_COLOR_CURSES) + AC_DEFINE(USE_SYSV_CURSES) + CURSES_INCLUDEDIR="-I/usr/5include" + CURSES_LIBS="/usr/5lib/libcurses.a /usr/5lib/libtermcap.a" + AC_MSG_RESULT(Please note that some screen refreshs may fail) +]) + +AC_DEFUN(AC_USE_OSF1_CURSES, [ + AC_MSG_RESULT(Using OSF1 curses) + search_ncurses=false + screen_manager="OSF1 curses" + AC_DEFINE(HAS_CURSES) + has_curses=true + AC_DEFINE(NO_COLOR_CURSES) + AC_DEFINE(USE_SYSV_CURSES) + CURSES_LIBS="-lcurses" +]) + +AC_DEFUN(AC_USE_SYSV_CURSES, [ + AC_MSG_RESULT(Using SysV curses) + AC_DEFINE(HAS_CURSES) + has_curses=true + AC_DEFINE(USE_SYSV_CURSES) + search_ncurses=false + screen_manager="SysV/curses" + CURSES_LIBS="-lcurses" +]) + +dnl AC_ARG_WITH(bsd-curses, +dnl [--with-bsd-curses Used to compile with bsd curses, not very fancy], +dnl search_ncurses=false +dnl screen_manager="Ultrix/cursesX" +dnl if test $system = ULTRIX +dnl then +dnl THIS_CURSES=cursesX +dnl else +dnl THIS_CURSES=curses +dnl fi +dnl +dnl CURSES_LIBS="-l$THIS_CURSES -ltermcap" +dnl AC_DEFINE(HAS_CURSES) +dnl has_curses=true +dnl AC_DEFINE(USE_BSD_CURSES) +dnl AC_MSG_RESULT(Please note that some screen refreshs may fail) +dnl AC_WARN(Use of the bsdcurses extension has some) +dnl AC_WARN(display/input problems.) +dnl AC_WARN(Reconsider using xcurses) +dnl) + + +dnl +dnl Parameters: directory filename cureses_LIBS curses_INCLUDEDIR nicename +dnl +AC_DEFUN(AC_NCURSES, [ + if $search_ncurses + then + if test -f $1/$2 + then + AC_MSG_RESULT(Found ncurses on $1/$2) + + CURSES_LIBS="$3" + AC_CHECK_LIB(ncurses, initscr, [ + ], [ + CHECKLIBS=`echo "$3"|sed 's/-lncurses/-lcurses/g'` + AC_CHECK_LIB(curses, initscr, [ + CURSES_LIBS="$CHECKLIBS" + ],, $CHECKLIBS) + ], $CURSES_LIBS) + CURSES_INCLUDEDIR="$4" + search_ncurses=false + screen_manager=$5 + AC_DEFINE(HAS_CURSES) + has_curses=true + has_ncurses=true + AC_DEFINE(USE_NCURSES) + fi + fi +]) + +AC_DEFUN(AC_SEARCH_NCURSES, [ + AC_CHECKING("location of ncurses.h file") + + AC_NCURSES(/usr/include, ncurses.h, -lncurses,, "ncurses on /usr/include") + AC_NCURSES(/usr/include/ncurses, ncurses.h, -lncurses, -I/usr/include/ncurses, "ncurses on /usr/include/ncurses") + AC_NCURSES(/usr/local/include, ncurses.h, -L/usr/local/lib -lncurses, -I/usr/local/include, "ncurses on /usr/local") + AC_NCURSES(/usr/pkg/include, ncurses.h, -L/usr/pkg/lib -lncurses, -I/usr/pkg/include, "ncurses on /usr/pkg") + AC_NCURSES(/usr/contrib/include, ncurses.h, -L/usr/contrib/lib -lncurses, -I/usr/contrib/include, "ncurses on /usr/contrib") + AC_NCURSES(/usr/local/include/ncurses, ncurses.h, -L/usr/local/lib -L/usr/local/lib/ncurses -lncurses, -I/usr/local/include/ncurses, "ncurses on /usr/local/include/ncurses") + + AC_NCURSES(/usr/local/include/ncurses, curses.h, -L/usr/local/lib -lncurses, -I/usr/local/include/ncurses -DRENAMED_NCURSES, "renamed ncurses on /usr/local/.../ncurses") + + AC_NCURSES(/usr/include/ncurses, curses.h, -lncurses, -I/usr/include/ncurses -DRENAMED_NCURSES, "renamed ncurses on /usr/include/ncurses") + + dnl + dnl We couldn't find ncurses, try SysV curses + dnl + if $search_ncurses + then + AC_EGREP_HEADER(init_color, /usr/include/curses.h, + AC_USE_SYSV_CURSES) + AC_EGREP_CPP(USE_NCURSES,[ +#include +#ifdef __NCURSES_H +#undef USE_NCURSES +USE_NCURSES +#endif +],[ + CURSES_INCLUDEDIR="$CURSES_INCLUDEDIR -DRENAMED_NCURSES" + AC_DEFINE(HAS_CURSES) + has_curses=true + has_ncurses=true + AC_DEFINE(USE_NCURSES) + search_ncurses=false + screen_manager="ncurses installed as curses" +]) + fi + + dnl + dnl Try SunOS 4.x /usr/5{lib,include} ncurses + dnl The flags USE_SUNOS_CURSES, USE_BSD_CURSES and BUGGY_CURSES + dnl should be replaced by a more fine grained selection routine + dnl + if $search_ncurses + then + if test -f /usr/5include/curses.h + then + AC_USE_SUNOS_CURSES + fi + fi + + dnl use whatever curses there happens to be + if $search_ncurses + then + if test -f /usr/include/curses.h + then + CURSES_LIBS="-lcurses" + AC_DEFINE(HAS_CURSES) + has_curses=true + search_ncurses=false + screen_manager="curses" + fi + fi +]) + diff --git a/apps/irssi/default-config.h b/apps/irssi/default-config.h new file mode 100644 index 00000000..635030b8 --- /dev/null +++ b/apps/irssi/default-config.h @@ -0,0 +1,67 @@ +const char *default_config = +"lservers = (\n" +" { address = \"irc.stealth.net\"; chatnet = IRCNet; port = 6668; },\n" +" { address = \"irc.efnet.net\"; chatnet = EFNet; port = 6667; },\n" +" { address = \"irc.undernet.org\"; chatnet = Undernet; port = 6667; },\n" +" { address = \"irc.dal.net\"; chatnet = DALnet; port = 6667; },\n" +" { address = \"irc.openprojects.net\"; chatnet = OPN; port = 6667; },\n" +" { address = \"irc.ptlink.net\"; chatnet = PTlink; port = 6667; }\n" +" { address = \"silc.pspt.fi\"; chatnet = SILC; port = 706; }\n" +");\n" +"\n" +"chatnets = {\n" +" IRCNet = { type = \"IRC\"; max_kicks = 4; max_modes = 3; max_msgs = 5; max_whois = 4; };\n" +" EFNet = { type = \"IRC\"; max_kicks = 4; max_modes = 4; max_msgs = 3; };\n" +" Undernet = { type = \"IRC\"; max_kicks = 4; max_modes = 3; max_msgs = 3; max_query_chans = \"1\"; };\n" +" DALNet = { type = \"IRC\"; max_kicks = 4; max_modes = 6; max_msgs = 3; };\n" +" OPN = { type = \"IRC\"; max_kicks = 1; max_modes = 6; max_msgs = 100; };\n" +" PTLink = { type = \"IRC\"; max_kicks = 1; max_modes = 6; max_msgs = 100; };\n" +" SILC = { type = \"SILC\"; };\n" +"};\n" +"\n" +"channels = (\n" +" { name = \"#irssi\"; chatnet = ircnet; autojoin = No; },\n" +" { name = \"#irssi\"; chatnet = opn; autojoin = No; },\n" +" { name = \"#silc\"; chatnet = silc; autojoin = No; }\n" +");\n" +"\n" +"aliases = {\n" +" J = \"join\";\n" +" WJOIN = \"join -window\";\n" +" WQUERY = \"query -window\";\n" +" LEAVE = \"part\";\n" +" BYE = \"quit\";\n" +" EXIT = \"quit\";\n" +" SIGNOFF = \"quit\";\n" +" DESCRIBE = \"action\";\n" +" DATE = \"time\";\n" +" HOST = \"userhost\";\n" +" LAST = \"lastlog\";\n" +" SAY = \"msg *\";\n" +" WI = \"whois\";\n" +" WII = \"whois $0 $0\";\n" +" WW = \"whowas\";\n" +" W = \"who\";\n" +" N = \"names\";\n" +" M = \"msg\";\n" +" T = \"topic\";\n" +" C = \"clear\";\n" +" CL = \"clear\";\n" +" K = \"kick\";\n" +" KB = \"kickban\";\n" +" KN = \"knockout\";\n" +" BANS = \"ban\";\n" +" B = \"ban\";\n" +" MUB = \"unban *\";\n" +" UB = \"unban\";\n" +" IG = \"ignore\";\n" +" UNIG = \"unignore\";\n" +" SB = \"scrollback\";\n" +" UMODE = \"mode $N\";\n" +" WC = \"window close\";\n" +" WN = \"window new hide\";\n" +" SV = \"say Irssi $J - http://irssi.org/\";\n" +" GOTO = \"sb goto\";\n" +" CHAT = \"dcc chat\";\n" +"};\n" +; diff --git a/apps/irssi/default.theme b/apps/irssi/default.theme new file mode 100644 index 00000000..501a1617 --- /dev/null +++ b/apps/irssi/default.theme @@ -0,0 +1,260 @@ +# When testing changes, the easiest way to reload the theme is with /RELOAD. +# This reloads the configuration file too, so if you did any changes remember +# to /SAVE it first. Remember also that /SAVE overwrites the theme file with +# old data so keep backups :) + +# TEMPLATES: + +# The real text formats that irssi uses are the ones you can find with +# /FORMAT command. Back in the old days all the colors and texts were mixed +# up in those formats, and it was really hard to change the colors since you +# might have had to change them in tens of different places. So, then came +# this templating system. + +# Now the /FORMATs don't have any colors in them, and they also have very +# little other styling. Most of the stuff you need to change is in this +# theme file. If you can't change something here, you can always go back +# to change the /FORMATs directly, they're also saved in these .theme files. + +# So .. the templates. They're those {blahblah} parts you see all over the +# /FORMATs and here. Their usage is simply {name parameter1 parameter2}. +# When irssi sees this kind of text, it goes to find "name" from abstracts +# block below and sets "parameter1" into $0 and "parameter2" into $1 (you +# can have more parameters of course). Templates can have subtemplates. +# Here's a small example: +# /FORMAT format hello {colorify {underline world}} +# abstracts = { colorify = "%G$0-%n"; underline = "%U$0-%U"; } +# When irssi expands the templates in "format", the final string would be: +# hello %G%Uworld%U%n +# ie. underlined bright green "world" text. +# and why "$0-", why not "$0"? $0 would only mean the first parameter, +# $0- means all the parameters. With {underline hello world} you'd really +# want to underline both of the words, not just the hello (and world would +# actually be removed entirely). + +# COLORS: + +# You can find definitions for the color format codes in docs/formats.txt. + +# There's one difference here though. %n format. Normally it means the +# default color of the terminal (white mostly), but here it means the +# "reset color back to the one it was in higher template". For example +# if there was /FORMAT test %g{foo}bar, and foo = "%Y$0%n", irssi would +# print yellow "foo" (as set with %Y) but "bar" would be green, which was +# set at the beginning before the {foo} template. If there wasn't the %g +# at start, the normal behaviour of %n would occur. If you _really_ want +# to use the terminal's default color, use %N. + +############################################################################# + +# default foreground color (%N) - 0 is the "default terminal color" +default_color = 0; +# default foreground color when "0" can't be used, +# such as with bolds and reverses. white is default. +default_real_color = 7; + +# these characters are automatically replaced with specified color +# (dark grey by default) +replaces = { "[]<>=" = "%K$0-%n"; }; + +abstracts = { + ## + ## generic + ## + + # text to insert at the beginning of each non-message line + line_start = "%B-%W!%B-%n "; + + # timestamp styling, nothing by default + timestamp = "$0-"; + + # any kind of text that needs hilighting, default is to bold + hilight = "%_$0-%_"; + + # any kind of error message, default is bright red + error = "%R$0-%n"; + + # channel name is printed + channel = "%_$0-%_"; + + # nick is printed + nick = "%_$0-%_"; + + # nick host is printed + nickhost = "[$0-]"; + + # server name is printed + server = "%_$0-%_"; + + # some kind of comment is printed + comment = "[$0-]"; + + # reason for something is printed (part, quit, kick, ..) + reason = "{comment $0-}"; + + # mode change is printed ([+o nick]) + mode = "{comment $0-}"; + + ## + ## channel specific messages + ## + + # highlighted nick/host is printed (joins) + channick_hilight = "%C$0-%n"; + chanhost_hilight = "{nickhost %c$0-%n}"; + + # nick/host is printed (parts, quits, etc.) + channick = "%c$0-%n"; + chanhost = "{nickhost $0-}"; + + # highlighted channel name is printed + channelhilight = "%c$0-%n"; + + # ban/ban exception/invite list mask is printed + ban = "%c$0-%n"; + + ## + ## messages + ## + + # the basic styling of how to print message, $0 = nick mode, $1 = nick + msgnick = "<$0$1-> %|"; + + # message from you is printed. "msgownnick" specifies the styling of the + # nick ($0 part in msgnick) and "ownmsgnick" specifies the styling of the + # whole line. + + # Example1: You want the message text to be green: + # ownmsgnick = "{msgnick $0 $1-}%g"; + # Example2.1: You want < and > chars to be yellow: + # ownmsgnick = "%Y{msgnick $0 $1-%Y}%n"; + # (you'll also have to remove <> from replaces list above) + # Example2.2: But you still want to keep <> grey for other messages: + # pubmsgnick = "%K{msgnick $0 $1-%K}%n"; + # pubmsgmenick = "%K{msgnick $0 $1-%K}%n"; + # pubmsghinick = "%K{msgnick $1 $0$2-%n%K}%n"; + # ownprivmsgnick = "%K{msgnick $0-%K}%n"; + # privmsgnick = "%K{msgnick %R$0-%K}%n"; + + # $0 = nick mode, $1 = nick + ownmsgnick = "{msgnick $0 $1-}"; + ownnick = "%W$0-%n"; + + # public message in channel, $0 = nick mode, $1 = nick + pubmsgnick = "{msgnick $0 $1-}"; + pubnick = "%N$0-%n"; + + # public message in channel meant for me, $0 = nick mode, $1 = nick + pubmsgmenick = "{msgnick $0 $1-}"; + menick = "%Y$0-%n"; + + # public highlighted message in channel + # $0 = highlight color, $1 = nick mode, $2 = nick + pubmsghinick = "{msgnick $1 $0$2-%n}"; + + # channel name is printed with message + msgchannel = "%K:%c$0-%n"; + + # private message, $0 = nick, $1 = host + privmsg = "[%R$0%K(%r$1-%K)%n] "; + + # private message from you, $0 = "msg", $1 = target nick + ownprivmsg = "[%r$0%K(%R$1-%K)%n] "; + + # own private message in query + ownprivmsgnick = "{msgnick $0-}"; + ownprivnick = "%W$0-%n"; + + # private message in query + privmsgnick = "{msgnick %R$0-%n}"; + + ## + ## Actions (/ME stuff) + ## + + # used internally by this theme + action_core = "%W * $0-%n"; + + # generic one that's used by most actions + action = "{action_core $0-} "; + + # own action, both private/public + ownaction = "{action $0-}"; + + # own action with target, both private/public + ownaction_target = "{action_core $0}%K:%c$1%n "; + + # private action sent by others + pvtaction = "%W (*) $0-%n "; + pvtaction_query = "{action $0-}"; + + # public action sent by others + pubaction = "{action $0-}"; + + + ## + ## other IRC events + ## + + # notices + ownnotice = "[%r$0%K(%R$1-%K)]%n "; + notice = "%K-%M$0-%K-%n "; + pubnotice_channel = "%K:%m$0-"; + pvtnotice_host = "%K(%m$0-%K)"; + servernotice = "%g!$0-%n "; + + # CTCPs + ownctcp = "[%r$0%K(%R$1-%K)] "; + ctcp = "%g$0-%n"; + + # wallops + wallop = "%W$0-%n: "; + wallop_nick = "%n$0-"; + wallop_action = "%W * $0-%n "; + + # netsplits + netsplit = "%R$0-%n"; + netjoin = "%C$0-%n"; + + # /names list + names_nick = "[%_$0%_$1-] "; + names_users = "[%g$0-%n]"; + names_channel = "%G$0-%n"; + + # DCC + dcc = "%g$0-%n"; + dccfile = "%_$0-%_"; + + # DCC chat, own msg/action + dccownmsg = "[%r$0%K($1-%K)%n] "; + dccownnick = "%R$0-%n"; + dccownaction = "{action $0-}"; + dccownaction_target = "{action_core $0}%K:%c$1%n "; + + # DCC chat, others + dccmsg = "[%G$1-%K(%g$0%K)%n] "; + dccquerynick = "%G$0-%n"; + dccaction = "%W (*dcc*) $0-%n %|"; + + ## + ## statusbar + ## + + # background of statusbar + sb_background = "%4"; + + # default statusbar item style + sb = "%c[%n$0-%c]%n"; + + sbmode = "(%c+%n$0-)"; + sbaway = " (%GzZzZ%n)"; + sbservertag = "%c:%n$0 (change with ^X)"; + sbmore = "%_-- more --%_"; + sblag = "{sb Lag: $0-}"; + sbmail = "{sb Mail: $0-}"; + + # activity. Det is used for hilights when display doesn't support colors + sbact = "{sb {sbact_act $0}{sbact_det $1}}"; + sbact_act = "Act: $0-"; + sbact_det = " Det: $0-"; +}; diff --git a/apps/irssi/docs/Makefile.am b/apps/irssi/docs/Makefile.am new file mode 100644 index 00000000..80499614 --- /dev/null +++ b/apps/irssi/docs/Makefile.am @@ -0,0 +1,12 @@ +docdir = $(prefix)/doc/irssi + +doc_DATA = \ + formats.txt \ + manual.txt \ + faq.txt \ + startup-HOWTO.html \ + startup-HOWTO.txt + +EXTRA_DIST = $(doc_DATA) + +SUBDIRS = help diff --git a/apps/irssi/docs/faq.txt b/apps/irssi/docs/faq.txt new file mode 100644 index 00000000..6f603e8d --- /dev/null +++ b/apps/irssi/docs/faq.txt @@ -0,0 +1,36 @@ +Q: Why doesn't irssi display colors even when ircii etc. displays them? +A: Irssi uses curses, ircii and others don't (no, not even if ldd says they + use it). Curses decides if terminal supports colors based on the TERM + variable, so changing it should help. Some values to try when running + in xterm are: xterm, xterm-color and color_xterm. + +Q: Why does irssi crash when pressing Ctrl-4? +A: Most unices are usually configured to send SIGQUIT to active process when + Ctrl-\ is pressed. Some terminals also treat Ctrl-4 and Ctrl-\ equally, so + Irssi will die to SIGQUIT. Two ways to fix this: change it to something + else with stty (stty quit undef) or in irssi /SET ignore_signals quit + +Q: Where's the GUI version? +A: Read http://irssi.org/?page=about + +Q: How do I easily write text to channel that starts with '/' character? +A: / /text + +Q: I connected to some server which isn't responding but now irssi tries + to connect back to it all the time! How can I stop it? +A: Two ways. The "good way" to do it is with /DISCONNECT. Check the server + tags first with /SERVER without giving it any parameters, reconnections + are those that have tag starting with "recon" text. So most probably you're + going to do /DISCONNECT recon-1. The other way is to remove all the + reconnections with /RMRECONNS, easier but may remove some connections + you actually wanted to reconnect (if you used multiple servers..). + +Q: Why does irssi say "Irssi: Channel not fully synchronized yet, try again + after a while" when I try to use /BAN etc? +A: IRC server you use is coded badly, do something like this: + /IRCNET ADD -querychans 1 quakenet + /SERVER ADD -ircnet quakenet irc.quakenet.org + /SAVE + After that /CONNECT quakenet should work properly (NOTE: when you do this + the first time you'll have to /DISCONNECT and /CONNECT again, /SERVER + doesn't work correctly). diff --git a/apps/irssi/docs/formats.txt b/apps/irssi/docs/formats.txt new file mode 100644 index 00000000..6dabc43c --- /dev/null +++ b/apps/irssi/docs/formats.txt @@ -0,0 +1,64 @@ + + Irssi's colors that you can use in text formats, hilights, etc. : + + text text background + --------------------------------------------------------------------- + %k %K %0 black bold black black + %r %R %1 red bold red red + %g %G %2 green bold green green + %y %Y %3 yellow bold yellow yellow + %b %B %4 blue bold blue blue + %m %M %5 magenta bold magenta magenta + %p %P magenta (think: purple) + %c %C %6 cyan bold cyan cyan + %w %W %7 white bold white white + %n %N Turn off all colors and other formatting + %F Blinking on/off (think: flash) + %U Underline on/off + %8 Reverse on/off + %9 %_ Bold on/off + %: Insert newline + %| Marks the indentation position + %% A single % + + In .theme files %n works a bit differently. See default.theme + for more information. + + + MIRC colors that you can use when writing text to channel: + + foreground (fg) background (bg) + ------------------------------------------------------- + 0 white black with 0 as fg + light gray with fg > 0 + 1 ligh gray black + 2 blue blue + 3 green green + 4 light red red + blinking fg + 5 orange orange + 6 magenta (purple) magenta + 7 red red + 8 yellow orange + blinking fg + 9 light green light green + blinking fg + 10 cyan cyan + 11 light cyan cyan + blinking fg + 12 light blue blue + blinking fg + 13 light magenta magenta + blinking fg + 14 gray black + blinking fg + 15 light gray black on black + gray on light gray (with bold) + + These colors may differ depending on your terminal. + + How to use these colors ('#' means a number as MIRC color code): + + -b set bold + -c#[,#] set foreground and optionally background color + -o reset all formats to plain text + -v set inverted color mode + -_ set underline + -7 same as -_ + + To reset a mode set it again, f.e. + 3FOOBAR + creates black on green FOO followed by a green on black BAR diff --git a/apps/irssi/docs/help/Makefile.am.gen b/apps/irssi/docs/help/Makefile.am.gen new file mode 100644 index 00000000..abf824b4 --- /dev/null +++ b/apps/irssi/docs/help/Makefile.am.gen @@ -0,0 +1,12 @@ +# Makefile.am is autogenerated by autogen.sh from Makefile.am.gen + +helpdir = $(datadir)/irssi/help + +help_DATA = \ +@HELPFILES@ + +EXTRA_DIST = \ + Makefile.am.gen \ + $(help_DATA) + +SUBDIRS = in diff --git a/apps/irssi/docs/help/in/.cvsignore b/apps/irssi/docs/help/in/.cvsignore new file mode 100644 index 00000000..4717c0ca --- /dev/null +++ b/apps/irssi/docs/help/in/.cvsignore @@ -0,0 +1,3 @@ +Makefile.am +Makefile.in +Makefile diff --git a/apps/irssi/docs/help/in/Makefile.am.gen b/apps/irssi/docs/help/in/Makefile.am.gen new file mode 100644 index 00000000..9b0f2aaa --- /dev/null +++ b/apps/irssi/docs/help/in/Makefile.am.gen @@ -0,0 +1,5 @@ +# Makefile.am is autogenerated by autogen.sh from Makefile.am.gen + +EXTRA_DIST = \ + Makefile.am.gen \ +@HELPFILES@ diff --git a/apps/irssi/docs/help/in/action.in b/apps/irssi/docs/help/in/action.in new file mode 100644 index 00000000..896e8d19 --- /dev/null +++ b/apps/irssi/docs/help/in/action.in @@ -0,0 +1,9 @@ + +@SYNTAX:action@ + +Same as ME, but gets channel or nick as an additional parameter. +Example: /ACTION #irssi yawns +(This outputs the following to #irssi: * Nick yawns) + +See also: ME + diff --git a/apps/irssi/docs/help/in/admin.in b/apps/irssi/docs/help/in/admin.in new file mode 100644 index 00000000..b5d95254 --- /dev/null +++ b/apps/irssi/docs/help/in/admin.in @@ -0,0 +1,8 @@ + +@SYNTAX:admin@ + +Displays the administrative details about the given server. If +no server is specified, the server you are connected to is +used. If a nickname is supplied then it gives the administrative +information for that person's current server. + diff --git a/apps/irssi/docs/help/in/alias.in b/apps/irssi/docs/help/in/alias.in new file mode 100644 index 00000000..97fe395e --- /dev/null +++ b/apps/irssi/docs/help/in/alias.in @@ -0,0 +1,22 @@ + +@SYNTAX:alias@ + +Creates a new alias or shows matching defined aliases. Without +parameters shows all defined aliases. Multiple commands can be run if +separated with ';' character. Parameters given to alias are in $0..$9 +variables, if you don't use them in your alias, all parameters are +automatically appended after it. + +Examples: + +/ALIAS w + - shows all defined aliases starting with letter w. + +/ALIAS send echo Sending file $1 to $0;dcc send $0- + - creates alias 'send'. + +/ALIAS -send + - removes alias 'send'. + +See also: UNALIAS + diff --git a/apps/irssi/docs/help/in/away.in b/apps/irssi/docs/help/in/away.in new file mode 100644 index 00000000..8283d63d --- /dev/null +++ b/apps/irssi/docs/help/in/away.in @@ -0,0 +1,28 @@ + +@SYNTAX:away@ + + -one + -all + +This command marks you as being "away". It is used to tell people that +you currently aren't paying attention to your screen. You might use it +if you are taking a nap, in the shower, getting some food, or otherwise +just aren't there at the moment. When you're "away" you will see "(zZzZ)" +in your statusbar. + +Anyone who does a WHOIS on your nickname will see that you are away, +as well as your away message. Anyone doing a WHO that returns information +about you will also see that you're gone. + +By default, if someone sends you a MSG while you are away, your client +will beep. You can turn this off by setting BEEP_WHEN_AWAY to OFF. + +If you send a MSG to someone who is away, you will automatically be +notified of this. By default, you will only receive this notification +once. If you wish to see it every time (to tell when a person is no +longer marked away, for instance), change SHOW_AWAY_ONCE to OFF. + +You can remove your away status by using AWAY with no arguments. + +See also: SET AWAY + diff --git a/apps/irssi/docs/help/in/ban.in b/apps/irssi/docs/help/in/ban.in new file mode 100644 index 00000000..4f8a541d --- /dev/null +++ b/apps/irssi/docs/help/in/ban.in @@ -0,0 +1,24 @@ + +@SYNTAX:ban@ + +Bans the specified nick or userhost mask. + +If nick is given as parameter, the ban type is used to generate the ban +mask. /SET ban_type specified the default ban type. Ban type is one of +the following: + + Normal - *!user@*.domain.net + Host - *!*@host.domain.net + Domain - *!*@*.domain.net + Custom [nick] [user] [host] [domain] + +Examples: + /BAN looser - This bans the nick 'looser' + /BAN *!*@*.org - This bans all the users coming from any + .org domain. + + /SET ban_type custom nick domain - nick!*@*.domain.net + /SET ban_type custom user host - *!user@host.domain.net + +See also: KNOCKOUT, KICKBAN + diff --git a/apps/irssi/docs/help/in/beep.in b/apps/irssi/docs/help/in/beep.in new file mode 100644 index 00000000..12df1ae7 --- /dev/null +++ b/apps/irssi/docs/help/in/beep.in @@ -0,0 +1,6 @@ + +@SYNTAX:beep@ + +Outputs the bell-character, usually causing +your terminal beep. + diff --git a/apps/irssi/docs/help/in/bind.in b/apps/irssi/docs/help/in/bind.in new file mode 100644 index 00000000..0d0d2ab1 --- /dev/null +++ b/apps/irssi/docs/help/in/bind.in @@ -0,0 +1,100 @@ + +@SYNTAX:bind@ + +Bind some action to specified keystroke. Remember that all characters +in keystrokes are case-sensitive! Uppercase letter usually means that +you need to keep SHIFT pressed to get the key to work. + +Most most commonly used keystrokes are: + + ^X - Ctrl-X + meta-x - Meta-x (Meta is quite often Alt-key in PCs, ESC-x works too) + +Irssi has by default also defined several other keys which you can use: + + return - The return/enter key + up, down, left, right - Arrow keys + home, end, prior, next - prior = Page Up, next = Page Down + insert, delete + +The keystroke can contain as many key presses as you want, and you can +define names for different key sequences to use them more easily (the +keys above are done like that). For example, you may want to manage +windows with ^W key, so that ^W^C creates new window, ^W^K kills the +active window, etc. you may do it like: + + /BIND ^W^C /WINDOW NEW HIDE + /BIND ^W^K /WINDOW KILL + +But maybe you wish to give these binds to other people who want to use +some other key than ^W, then it would be better done as: + + /BIND ^W key window + /BIND window-^C /WINDOW NEW HIDE + /BIND window-^K /WINDOW KILL + + +Command can be one of: + + command - Run any /COMMAND (you could use /COMMAND directly without + specifying this) + +(Cursor movement) + backward_character + forward_character + backward_word + forward_word + beginning_of_line + end_of_line + +(Scrollback movement) + scroll_backward - Previous page + scroll_forward - Next page + scroll_start - Beginning of the window + scroll_end - End of the window + +(Switching windows) + change_window + previous_window + next_window + upper_window + lower_window + active_window - Go to next window with the highest activity + next_window_item - Next channel/query. In empty windows change + to next server + previous_window_item - Previous channel/query. In empty windows change + to previous server + +(History) + backward_history + forward_history + +(Deleting text) + backspace + delete_character + delete_character + delete_next_word + delete_previous_word + delete_to_previous_space + erase_line + erase_to_beg_of_line + erase_to_end_of_line + +(Word completion) + word_completion + check_replaces - Check word replaces + +(Misc) + refresh_screen + yank_from_cutbuffer - "Undelete" line + transpose_characters - Swap current and previous character + insert_text - Insert data to entry line, data may contain $variables. + +Examples: + +Clear screen: + /BIND meta-c /CLEAR + +People with qwertz layout probably want to swap meta-y and meta-z: + /BIND meta-z change_window 16 + /BIND -delete meta-y diff --git a/apps/irssi/docs/help/in/cat.in b/apps/irssi/docs/help/in/cat.in new file mode 100644 index 00000000..ea324aa3 --- /dev/null +++ b/apps/irssi/docs/help/in/cat.in @@ -0,0 +1,8 @@ + +@SYNTAX:cat@ + +Outputs the contents of the specified file. Equivalent to +UNIX 'cat' command. + +See also: CD + diff --git a/apps/irssi/docs/help/in/cd.in b/apps/irssi/docs/help/in/cd.in new file mode 100644 index 00000000..dd62f710 --- /dev/null +++ b/apps/irssi/docs/help/in/cd.in @@ -0,0 +1,8 @@ + +@SYNTAX:cd@ + +Changes the current working directory. Equivalent to UNIX +'cd' command. + +See also: DCC GET + diff --git a/apps/irssi/docs/help/in/channel.in b/apps/irssi/docs/help/in/channel.in new file mode 100644 index 00000000..7484ac15 --- /dev/null +++ b/apps/irssi/docs/help/in/channel.in @@ -0,0 +1,35 @@ + +@SYNTAX:channel@ + +Irssi can automatically join to specified channels in specified +IRC networks. It can also automatically send the password when +manually joining to channel without specifying the password. + +/CHANNEL ADD [-auto | -noauto] [-bots ] [-botcmd ] + [] + +With -bots and -botcmd arguments you can automatically send +commands to someone in channel. This is useful for automatically +getting ops for channels, for example + +/CHANNEL ADD -auto -bots "*!bot@bothost.org bot*!*@host2.org" + -botcmd "msg $0 op mypass" #channel ircnet + +You can also use the -botcmd without -bots argument. The command is +then sent whenever you join the channel. + +If you want to remove some settings from existing channel record, +for example bots, just give the -bots "" parameters to it. Password +can be removed by setting it to - (or actually, "" works too). + +You can remove the channels with +/CHANNEL REMOVE + +/CHANNEL LIST displays list of channels with settings. + +/CHANNEL without any arguments displays list of channels you have +joined. You can also use /CHANNEL to join to channels just as with +/JOIN, like /CHANNEL #a. + +See also: TS, JOIN + diff --git a/apps/irssi/docs/help/in/clear.in b/apps/irssi/docs/help/in/clear.in new file mode 100644 index 00000000..b3db54ef --- /dev/null +++ b/apps/irssi/docs/help/in/clear.in @@ -0,0 +1,9 @@ + +@SYNTAX:clear@ + +This command clears the current window of all text. It is useful +for wiping a screen that has rendered improperly (such as due +to a bad termcap entry) or that contains sensitive information +(such as one's OPER password). + + diff --git a/apps/irssi/docs/help/in/connect.in b/apps/irssi/docs/help/in/connect.in new file mode 100644 index 00000000..f4671ab4 --- /dev/null +++ b/apps/irssi/docs/help/in/connect.in @@ -0,0 +1,12 @@ + +@SYNTAX:connect@ + + -4, -6: specify explicitly whether to use IPv4 or IPv6 address + -ircnet: the IRCNet + -host: the host + +This command makes irssi to connect to specified server. +Current connections are kept and a new one is created. + +See also: SERVER + diff --git a/apps/irssi/docs/help/in/ctcp.in b/apps/irssi/docs/help/in/ctcp.in new file mode 100644 index 00000000..77c78b7c --- /dev/null +++ b/apps/irssi/docs/help/in/ctcp.in @@ -0,0 +1,7 @@ + +@SYNTAX:ctcp@ + +Sends a CTCP-message. For example CTCP ACTION, or CTCP VERSION. + +See also: ME, ACTION + diff --git a/apps/irssi/docs/help/in/cycle.in b/apps/irssi/docs/help/in/cycle.in new file mode 100644 index 00000000..7e8a8fb5 --- /dev/null +++ b/apps/irssi/docs/help/in/cycle.in @@ -0,0 +1,8 @@ + +@SYNTAX:cycle@ + +Cycles (leaves and joins) the current channel or the specified +channel. + +See also: JOIN, LEAVE, PART + diff --git a/apps/irssi/docs/help/in/date.in b/apps/irssi/docs/help/in/date.in new file mode 100644 index 00000000..5b2ca4b9 --- /dev/null +++ b/apps/irssi/docs/help/in/date.in @@ -0,0 +1,11 @@ + +@SYNTAX:time@ + +This displays the time of day, local to the server queried (thus, +the time returned may not be the same as the client's local time). + +If the server name is omitted, the client's current server is used. +If a nickname is given, that client's server is queried. + +Same as /TIME. + diff --git a/apps/irssi/docs/help/in/dcc.in b/apps/irssi/docs/help/in/dcc.in new file mode 100644 index 00000000..eb9caa34 --- /dev/null +++ b/apps/irssi/docs/help/in/dcc.in @@ -0,0 +1,25 @@ + +@SYNTAX:dcc@ + +This is a command to handle different DCC-connections. DCC is mainly +used for more reliable and faster chatting and for sending and receiving +files. + +/DCC LIST + - Shows all the open DCC connections. +/DCC RESUME [ []] + - Resumes a DCC SEND/GET connection. +/DCC CHAT [] + - Sends a chat connection request to remote client or accepts + a chat connection if the remote end has already sent a request. +/DCC GET [ []] + - Gets the file offered by remote client. The file is downloaded and + saved into the current working directory. +/DCC SEND + - Sends a DCC SEND request to remote client. Remote end has to accept + the request before the transmission can be started. +/DCC CLOSE [] + - Closes a DCC-connection. Type can be either SEND, GET or CHAT. + +See also: CD + diff --git a/apps/irssi/docs/help/in/dehilight.in b/apps/irssi/docs/help/in/dehilight.in new file mode 100644 index 00000000..34f856a5 --- /dev/null +++ b/apps/irssi/docs/help/in/dehilight.in @@ -0,0 +1,9 @@ + +@SYNTAX:dehilight@ + +Makes irssi not to highlight items containing the text. +If parameter is a number, deletes the specified hilight +entry from the list. + +See also: HILIGHT + diff --git a/apps/irssi/docs/help/in/deop.in b/apps/irssi/docs/help/in/deop.in new file mode 100644 index 00000000..e9893e6a --- /dev/null +++ b/apps/irssi/docs/help/in/deop.in @@ -0,0 +1,10 @@ + +@SYNTAX:deop@ + +Takes off the channel operator privileges from the +specified nick(s). + +Wildcards in the nick are allowed. + +See also: OP + diff --git a/apps/irssi/docs/help/in/devoice.in b/apps/irssi/docs/help/in/devoice.in new file mode 100644 index 00000000..ce090191 --- /dev/null +++ b/apps/irssi/docs/help/in/devoice.in @@ -0,0 +1,10 @@ + +@SYNTAX:devoice@ + +Takes off the voice from the specified nick(s). This makes them +not to be able to send messages to the moderated (+m) channel. + +Wildcards in the nick are allowed. + +See also: VOICE, MODE + diff --git a/apps/irssi/docs/help/in/die.in b/apps/irssi/docs/help/in/die.in new file mode 100644 index 00000000..4ef6da0d --- /dev/null +++ b/apps/irssi/docs/help/in/die.in @@ -0,0 +1,7 @@ + +@SYNTAX:die@ + +IRC-operator command. Makes IRC-server to die. + +See also: OPER + diff --git a/apps/irssi/docs/help/in/disconnect.in b/apps/irssi/docs/help/in/disconnect.in new file mode 100644 index 00000000..cfd221d5 --- /dev/null +++ b/apps/irssi/docs/help/in/disconnect.in @@ -0,0 +1,9 @@ + +@SYNTAX:disconnect@ + +Disconnects from the specified IRC-server. +The server tags can be seen with: +/SERVER LIST + +See also: CONNECT, SERVER + diff --git a/apps/irssi/docs/help/in/echo.in b/apps/irssi/docs/help/in/echo.in new file mode 100644 index 00000000..0a70e354 --- /dev/null +++ b/apps/irssi/docs/help/in/echo.in @@ -0,0 +1,5 @@ + +@SYNTAX:echo@ + +Prints text into the current window. Useful for scripts. + diff --git a/apps/irssi/docs/help/in/eval.in b/apps/irssi/docs/help/in/eval.in new file mode 100644 index 00000000..8ea0652a --- /dev/null +++ b/apps/irssi/docs/help/in/eval.in @@ -0,0 +1,6 @@ + +@SYNTAX:eval@ + +Evaluates the given commands and executes them. Internal variables +are expanded. See the special_vars.txt file in the docs-directory. + diff --git a/apps/irssi/docs/help/in/exec.in b/apps/irssi/docs/help/in/exec.in new file mode 100644 index 00000000..c33ec59f --- /dev/null +++ b/apps/irssi/docs/help/in/exec.in @@ -0,0 +1,37 @@ + +@SYNTAX:exec@ + + -: Don't print "process terminated ..." message + -nosh: Don't start command through /bin/sh + -out: Send output to active channel/query + -msg: Send output to specified nick/channel + -notice: Send output to specified nick/channel as notices + -name: Name the process so it could be accessed easier + + -window: Move the output of specified process to active window + -close: Forcibly close (or "forget") a process that doesn't die. + This only removes all information from irssi concerning the + process, it doesn't send SIGKILL or anything the process. + -: Send a signal to process. can be either numeric + or one of the few most common ones (hup, term, kill, ...) + + -in: Send text to standard input of the specified process + -interactive: Creates a query-like window item. Text written to it is + sent to executed process, like /EXEC -in. + +Execute specified command in background. Output of process is printed +to active window by default, but can be also sent as messages or +notices to specified nick or channel. + +Processes can be accessed either by their ID or name if you named it. +Process identifier must always begin with '%%' character, like %%0 or +%%name. + +Once the process is started, it's output can still be redirected +elsewhere with the -window, -msg, etc. options. You can send text to +standard input of the process with -in option. + +-close option shouldn't probably be used if there's a better way to +kill the process. It is meant to remove the processes that don't die +even with SIGKILL. This option just closes the pipes used to +communicate with the process and frees all memory it used. diff --git a/apps/irssi/docs/help/in/format.in b/apps/irssi/docs/help/in/format.in new file mode 100644 index 00000000..ed576c95 --- /dev/null +++ b/apps/irssi/docs/help/in/format.in @@ -0,0 +1,9 @@ + +@SYNTAX:format@ + + -reset + -delete + +Allows you to view/change irssi's messages. +Use this command with care. + diff --git a/apps/irssi/docs/help/in/hash.in b/apps/irssi/docs/help/in/hash.in new file mode 100644 index 00000000..62a889f8 --- /dev/null +++ b/apps/irssi/docs/help/in/hash.in @@ -0,0 +1,5 @@ + +@SYNTAX:hash@ + +Not available. + diff --git a/apps/irssi/docs/help/in/help.in b/apps/irssi/docs/help/in/help.in new file mode 100644 index 00000000..318b8204 --- /dev/null +++ b/apps/irssi/docs/help/in/help.in @@ -0,0 +1,13 @@ + +@SYNTAX:help@ + +Shows help on commands. Try: + /HELP command + +Also try, for example: + /SET beep +or + /SET auto + +See also: + diff --git a/apps/irssi/docs/help/in/hilight.in b/apps/irssi/docs/help/in/hilight.in new file mode 100644 index 00000000..a38d10c9 --- /dev/null +++ b/apps/irssi/docs/help/in/hilight.in @@ -0,0 +1,27 @@ + +@SYNTAX:hilight@ + + -mask: Match only for nick, is a nick mask + -regexp: is a regular expression + -full: must match to full words + -nick: Hilight only the nick, not the whole line (default) + -word: Hilight only the word (default with non-public messages) + -line: Hilight the whole line with the hilight color. + -color: Print the message with . color is in %code format + (see docs/formats.txt) + -level: Match only for messages, default is + publics,msgs,notices,actions + -channels: Match only in + +Examples: + +Hilight lines that have "mynick" word: + /HILIGHT mynick + +Hilight lines that were written by nicks from *.fi with bold green + /HILIGHT -color %G -mask *!*@*.fi + +For regular expressions, see `man 7 regex`. + +See also: DEHILIGHT, SET HILIGHT + diff --git a/apps/irssi/docs/help/in/ignore.in b/apps/irssi/docs/help/in/ignore.in new file mode 100644 index 00000000..34a49dec --- /dev/null +++ b/apps/irssi/docs/help/in/ignore.in @@ -0,0 +1,26 @@ + +@SYNTAX:ignore@ + + -regexp: is a regular expression + -word: must match to full words + -pattern: must match to the message's text + -replies: Ignore replies to nick in channels. For example + "/IGNORE -replies *!*@*.fi PUBLIC" ignores everyone + from Finland, but also anyone sending message + "tofinnishnick: blahblah". + -except: *DON'T* ignore + -channels: Ignore only in channels + : Either a nick mask or list of channels + : List of levels to ignore + <^levels>: List of levels to NOT ignore + (/ignore -except nick notices = /ignore nick ^notices) + + +/IGNORE without any arguments displays list of ignores. + +The best match always wins, so you can have: + /IGNORE * CTCPS + /IGNORE -except *!*@host.org CTCPS + +See also: UNIGNORE + diff --git a/apps/irssi/docs/help/in/info.in b/apps/irssi/docs/help/in/info.in new file mode 100644 index 00000000..e9aad17d --- /dev/null +++ b/apps/irssi/docs/help/in/info.in @@ -0,0 +1,6 @@ + +@SYNTAX:info@ + +Shows information about the IRC creators, debuggers, slaves and +a lot of other people who no longer have much to do with irc. + diff --git a/apps/irssi/docs/help/in/invite.in b/apps/irssi/docs/help/in/invite.in new file mode 100644 index 00000000..1e7f69b1 --- /dev/null +++ b/apps/irssi/docs/help/in/invite.in @@ -0,0 +1,10 @@ + +@SYNTAX:invite@ + +Invites the specified nick to the current or specified channel. + +Example: + /INVITE buddy #mychannel + +See also: MODE + diff --git a/apps/irssi/docs/help/in/invitelist.in b/apps/irssi/docs/help/in/invitelist.in new file mode 100644 index 00000000..ed6bc060 --- /dev/null +++ b/apps/irssi/docs/help/in/invitelist.in @@ -0,0 +1,9 @@ + +@SYNTAX:invitelist@ + +Shows the +I modes of the current channel. +I mode +allows free joins of clients with certain userhost mask +even if the channel is invite only. + +See also: INVITE, MODE + diff --git a/apps/irssi/docs/help/in/ircnet.in b/apps/irssi/docs/help/in/ircnet.in new file mode 100644 index 00000000..51a123fe --- /dev/null +++ b/apps/irssi/docs/help/in/ircnet.in @@ -0,0 +1,21 @@ + +@SYNTAX:ircnet@ + + -kicks: Maximum number of nicks in one /KICK command + -msgs: Maximum number of nicks in one /MSG command + -modes: Maximum number of mode changes in one /MODE command + -whois: Maximum number of nicks in one /WHOIS command + -cmdspeed: Same as /SET cmd_queue_speed, see section 3.1 + -cmdmax: Same as /SET cmd_max_at_once, see section 3.1 + -nick, -user, -realname: Specify what nick/user/name to use + -host: Specify what host name to use, if you have multiple + -autosendcmd: Command to send after connecting to a server + +With -autosendcmd argument you can automatically run any commands +after connecting to ircnet. This is useful for automatically +identifying yourself to NickServ, for example + +Shows and changes the settings of defined IRC networks. + +See also: CONNECT + diff --git a/apps/irssi/docs/help/in/ison.in b/apps/irssi/docs/help/in/ison.in new file mode 100644 index 00000000..b97ff5de --- /dev/null +++ b/apps/irssi/docs/help/in/ison.in @@ -0,0 +1,7 @@ + +@SYNTAX:ison@ + +Tells whether specified nicks are online. + +See also: WHOIS, WHOWAS, NOTIFY + diff --git a/apps/irssi/docs/help/in/join.in b/apps/irssi/docs/help/in/join.in new file mode 100644 index 00000000..bdd4b15c --- /dev/null +++ b/apps/irssi/docs/help/in/join.in @@ -0,0 +1,13 @@ + +@SYNTAX:join@ + +Joins a specified channel. Channel names usually begin with #-sign, +which may be omitted here. + +JOIN is aliased to J by default. Example: /j irssi +(This joins to the channel #irssi) + +Description + +See also: LEAVE, WINDOW CLOSE + diff --git a/apps/irssi/docs/help/in/kick.in b/apps/irssi/docs/help/in/kick.in new file mode 100644 index 00000000..304c4d31 --- /dev/null +++ b/apps/irssi/docs/help/in/kick.in @@ -0,0 +1,16 @@ + +@SYNTAX:kick@ + +This command "kicks" the specified user off of the specified +channel. It is typically used to remove troublemakers, flooders, +or people otherwise making a nuisanse of themselves on the channel. +The reason for the kick is recommended, but not required by the IRC +servers + +If the is omitted, removes the nick from the current +channel. + +The default alias for /KICK is /K. + +See also: KNOCKOUT + diff --git a/apps/irssi/docs/help/in/kickban.in b/apps/irssi/docs/help/in/kickban.in new file mode 100644 index 00000000..aff0cfb2 --- /dev/null +++ b/apps/irssi/docs/help/in/kickban.in @@ -0,0 +1,10 @@ + +@SYNTAX:kickban@ + +Kicks off and bans a nick from the current channel. +A reason for the kick can be supplied. + +Default alias for /KICKBAN is /KB. + +See also: KNOCKOUT, BANTYPE + diff --git a/apps/irssi/docs/help/in/kill.in b/apps/irssi/docs/help/in/kill.in new file mode 100644 index 00000000..338871d9 --- /dev/null +++ b/apps/irssi/docs/help/in/kill.in @@ -0,0 +1,18 @@ + +@SYNTAX:kill@ + +IRC operator command. + +KILL is used to forcibly remote a client from the irc network. +It works similarly to KICK, except that a reason must be +given (even if it is meaningless or flat-out wrong). + +In general, KILL is useful only as a warning tool for abusive +users. Modern irc clients (this one included) have automated +means for reconnecting to a server after a disconnection (whether +due to a KILL or something else), so KILL is by no means a +permanent solution. It is not intended as a means for personal +vendettas; this practice is generally frowned upon. + +See also: OPER + diff --git a/apps/irssi/docs/help/in/knockout.in b/apps/irssi/docs/help/in/knockout.in new file mode 100644 index 00000000..1d18b533 --- /dev/null +++ b/apps/irssi/docs/help/in/knockout.in @@ -0,0 +1,11 @@ + +@SYNTAX:knockout@ + +Kicks user off the channel and bans him/her. Ban +lasts the given number of seconds or 5 minutes +by default. + +Default alias for /KNOCKOUT is /KN. + +See also: BAN, KICK + diff --git a/apps/irssi/docs/help/in/lastlog.in b/apps/irssi/docs/help/in/lastlog.in new file mode 100644 index 00000000..0c508c4f --- /dev/null +++ b/apps/irssi/docs/help/in/lastlog.in @@ -0,0 +1,22 @@ + +@SYNTAX:lastlog@ + + -clear: remove all lastlog lines from window + + -: don't print the "Lastlog:" and "End of Lastlog" messages. + -file: write lastlog to file instead of screen + -window: which window's lastlog to check (output is always to active) + -case: Case-sensitive matching + -force: Force displaying lastlog even if it's longer than 1000 lines + -new: show only lines since last /LASTLOG + -regexp: `text' is a regular expression + -word: `text' must match to full words + -level: what levels to check, like -public -msgs (default is all) + : text to search for, or all if empty + : maximum number of lines to show + : skip the last `start' lines + +Shows the given number of lines of log from the current window. + +See also: + diff --git a/apps/irssi/docs/help/in/layout.in b/apps/irssi/docs/help/in/layout.in new file mode 100644 index 00000000..d5e81438 --- /dev/null +++ b/apps/irssi/docs/help/in/layout.in @@ -0,0 +1,13 @@ + +@SYNTAX:layout@ + +Saves the current window layout to configuration (yes, you'll still +need to use /SAVE to save the configuration to file). Next time you run +irssi, all the channels and queries are exactly in the same windows +where they were when you called /LAYOUT SAVE. + +Channels aren't actually joined in those windows immediately, they're +just marked "next time you join to '#channel' in server that has tag +'ircnet' place it to this window". + +/LAYOUT RESET removes the saved layout. diff --git a/apps/irssi/docs/help/in/levels.in b/apps/irssi/docs/help/in/levels.in new file mode 100644 index 00000000..937ec1dd --- /dev/null +++ b/apps/irssi/docs/help/in/levels.in @@ -0,0 +1,35 @@ +Message levels (or in short, levels) are used almost everywhere. +They describe what kind of messages we're dealing with. Here's a +list of them all: + + CRAP - Can be almost anything + MSGS - Private messages + PUBLIC - Public messages in channel + NOTICES - Notices + SNOTES - Server notices + CTCPS - CTCP messages + ACTIONS - Actions (/me) - usually ORed with PUBLIC or MSGS + JOINS - Someone joins a channel + PARTS - Someone parts a channel + QUITS - Someone quits IRC + KICKS - Someone gets kicked from channel + MODES - Channel mode is changed + TOPICS - Channel topic is changed + WALLOPS - Wallop is received + INVITES - Invite is received + NICKS - Someone changes nick + DCC - DCC related messages + DCCMSGS - DCC chat messages + CLIENTNOTICES - Irssi's notices + CLIENTERRORS - Irssi's error messages + CLIENTCRAP - Some other messages from Irssi + +And a few special ones that could be included with the +levels above: + + HILIGHT - Text is highlighted + NOHILIGHT - Don't check highlighting for this message + NO_ACT - Don't trigger channel activity when printing + this message + NEVER - Never ignore or log this message + diff --git a/apps/irssi/docs/help/in/links.in b/apps/irssi/docs/help/in/links.in new file mode 100644 index 00000000..726fcb6b --- /dev/null +++ b/apps/irssi/docs/help/in/links.in @@ -0,0 +1,9 @@ + +@SYNTAX:links@ + +Shows the links between the IRC servers of the +current IRC network. If a wildcard parameter is +specified, shows only the matching entries. + +See also: + diff --git a/apps/irssi/docs/help/in/list.in b/apps/irssi/docs/help/in/list.in new file mode 100644 index 00000000..32a3cdbd --- /dev/null +++ b/apps/irssi/docs/help/in/list.in @@ -0,0 +1,9 @@ + +@SYNTAX:list@ + +Lists the channel names. Trying to list all the channel +names usually causes you to be disconnected from the +server with the reason "Excessive flood". + +See also: + diff --git a/apps/irssi/docs/help/in/load.in b/apps/irssi/docs/help/in/load.in new file mode 100644 index 00000000..134c7623 --- /dev/null +++ b/apps/irssi/docs/help/in/load.in @@ -0,0 +1,14 @@ + +@SYNTAX:load@ + +Load a plugin. If full path isn't given, irssi searches the plugin from +directories: + + ~/.irssi/modules/ + /lib/irssi/modules/ + +See plugins page of http://irssi.org/ to see if there's any plugins +you'd want to use. + +See also: UNLOAD + diff --git a/apps/irssi/docs/help/in/log.in b/apps/irssi/docs/help/in/log.in new file mode 100644 index 00000000..6a61ef9f --- /dev/null +++ b/apps/irssi/docs/help/in/log.in @@ -0,0 +1,40 @@ + +@SYNTAX:log@ + + -noopen: Create the entry to log list, but don't start logging + -autoopen: Automatically open this log file at startup + -: Targets are logged only in this server + -targets: Log only in specified channels/nicks + -window: Log the active window + : File name where to log, it is parsed with + strftime(), so %%d=day, etc. see "man strftime" for + more info. + : Defaults to ALL + : ID number of log. + +/SET log_create_mode - Specifies what file mode to use with + the created log files. Default is 0644. + +All of these are parsed with strftime(): +/SET log_timestamp - Specifies the time stamp format. + Default is "%%H:%%M ". +/SET log_open_string - Text written to log when it's opened +/SET log_close_string - Text written to log when it's closed +/SET log_day_changed - Text written to log when day changes + +NOTE: Log files are locked after opened, so two Irssis can't +accidentally try to write to the same log file. + +Examples: + +/LOG OPEN -targets cras ~/irclogs/cras.log MSGS + - Logs all messages from/to nick `cras' + +/LOG OPEN -targets #linux ~/irclogs/linux/linux-%%Y-%%m-%%d + - Logs all messages in channel #linux. Log is rotated daily, so + logs in 1. May 2000 goes to file "linux-2000-05-01", when the + day is changed, Irssi closes the log and starts logging to + "linux-2000-05-02" etc. + +See also: SET LOG, WINDOW LOG + diff --git a/apps/irssi/docs/help/in/lusers.in b/apps/irssi/docs/help/in/lusers.in new file mode 100644 index 00000000..abcd55c4 --- /dev/null +++ b/apps/irssi/docs/help/in/lusers.in @@ -0,0 +1,5 @@ + +@SYNTAX:lusers@ + +Shows user statistics of the current IRC network. + diff --git a/apps/irssi/docs/help/in/map.in b/apps/irssi/docs/help/in/map.in new file mode 100644 index 00000000..824dce37 --- /dev/null +++ b/apps/irssi/docs/help/in/map.in @@ -0,0 +1,5 @@ + +@SYNTAX:map@ + +Not available in IRC. + diff --git a/apps/irssi/docs/help/in/me.in b/apps/irssi/docs/help/in/me.in new file mode 100644 index 00000000..b918ec71 --- /dev/null +++ b/apps/irssi/docs/help/in/me.in @@ -0,0 +1,8 @@ + +@SYNTAX:me@ + +Sends a CTCP ACTION to the current channel or query. +For example: /me sits back. + +See also: ACTION, CTCP + diff --git a/apps/irssi/docs/help/in/mircdcc.in b/apps/irssi/docs/help/in/mircdcc.in new file mode 100644 index 00000000..d3402e0a --- /dev/null +++ b/apps/irssi/docs/help/in/mircdcc.in @@ -0,0 +1,11 @@ + +@SYNTAX:mircdcc@ + +Selects whether to send mIRC style CTCPs in DCC chat +session. + +If a mIRC user sends first a CTCP, mIRC style CTCPs is +automatically selected for that DCC Chat session. + +See also: SET MIRC + diff --git a/apps/irssi/docs/help/in/mode.in b/apps/irssi/docs/help/in/mode.in new file mode 100644 index 00000000..5bef27c4 --- /dev/null +++ b/apps/irssi/docs/help/in/mode.in @@ -0,0 +1,97 @@ + +@SYNTAX:mode@ + +Irssi knows these channel modes: + + i - Invite only - People can't join to channel without being + /INVITEd, or being in invite list (+I, see below). + m - Moderated - People who don't have voices (+v) can't send + messages to channel + p - Private - People who aren't joined to channel can't see it + for example with /WHOISing people who are in channel. + s - Secret - Like private, but the channel isn't displayed in + /LIST's output. + n - No external msgs - Without this mode, anyone can send messages + to channel without even being joined. + t - Topic can be changed only by channel operators. + + k - Channel password (aka. key) - The channel can't be joined + without specifying the channel key (see section 6.2). + + l - User limit - No more than people can join to + channel. This can be overridden with /INVITE with some + servers. + + This is usually used for protecting channel from join + flooding, like some bot allows max. 5 users to join in + one minute or so. + + a - Anonymous - No-one's nick name, host or anything else can be + seen. All messages, joins, parts, modes, etc. are seen as coming + from nick "anonymous", this could be pretty confusing but nice + feature if you want total anonymity. This mode can only be set, + never unset. This mode isn't supported by all servers. + + NOTE: there is/was one bug :) Channel operators can guess if some + nick might be in the channel and try to kick it. If nick was in + channel, everyone will see the nick that was kicked. + + r - Re-op - If channel becomes opless for longer than 45 (?) minutes, + op everyone in the channel. This works only in !channels. This + mode can only be set, not unset by channel creator. + + b - Set/remove ban. For example MODE #channel +b *!*@*.org bans + everyone from .org domain. + + If someone from .org domain was already in channel before the + ban was set, he/she couldn't be able to write any messages to + channel (doesn't work with all servers). + + Ban can also be overridden with /INVITE, although many stupid + IRC clients automatically kick the user out because they see + the ban and think that because of it the user shouldn't be in + the channel (doesn't work with all servers). + + e - Ban exceptions. You could for example ban everyone from + *!*@*.org but set ban exception to *!*@*.host.org - works only + in IRCnet/EFnet servers. + + I - Invite list. If channel is invite only (+i), people in this + list can join it without being /INVITEd - works only in + IRCnet/EFnet servers. + + This is excellent for in-country channels that don't want + foreigners (spammers!) to join the channel, for example setting + channel's mode to +i and +I *!*@*.fi allows only finnish people + to join the channel. In addition to this, there's usually a bot + in the channels and sending /MSG bot invite command to it + /INVITEs you to the channel. + + The ':' feature in channel modes is quite similiar, see section + 6.2. + + O - Channel owner, the nick who creates a !channel receives this + mode. It isn't displayed anywhere, you can't pass it to anyone + else and you can't regain it again. This is needed for setting + +r mode in channel when it's first created. + + o - Grant or revoke channel operator status from nick + v - Grant or revoke voice status from nick, only people with + +v (or +o) can talk to channel when it's moderated (+m). + +You can send multiple mode changes with one mode command: + +/MODE #channel +nto-o+v nick1 nick2 nick3 + +This would set channel's mode to +nt, give ops to nick1, take ops +from nick2 and give voices to nick3. + +You can set only limited number of modes that requires argument in +one command. In IRCnet it's 3, in EFnet it's 4 and in many others +it's 6. If it's not known, Irssi defaults to 3. Irssi will also +automatically split them, so you can use /MODE +oooooo n1,n2,.. +command to op 6 people and Irssi will split it to two commands in +IRCnet/EFnet. + +See also: OP, DEOP, VOICE, DEVOICE, BAN, UNBAN + diff --git a/apps/irssi/docs/help/in/motd.in b/apps/irssi/docs/help/in/motd.in new file mode 100644 index 00000000..ba354408 --- /dev/null +++ b/apps/irssi/docs/help/in/motd.in @@ -0,0 +1,7 @@ + +@SYNTAX:motd@ + +Shows the motd of the current server. This contains +usually some useful info on the server, administrator and +the rules. + diff --git a/apps/irssi/docs/help/in/msg.in b/apps/irssi/docs/help/in/msg.in new file mode 100644 index 00000000..f58ba41a --- /dev/null +++ b/apps/irssi/docs/help/in/msg.in @@ -0,0 +1,15 @@ + +@SYNTAX:msg@ + +Sends a message to a nick or a channel. Usually this +is used for sending private messages to other persons. + +Examples: + +/MSG friend Hi, what's up? + +/MSG #irssi Hello, is the new gtk-version out already? +(This format is rarely needed.) + +See also: CTCP + diff --git a/apps/irssi/docs/help/in/names.in b/apps/irssi/docs/help/in/names.in new file mode 100644 index 00000000..2d515c1c --- /dev/null +++ b/apps/irssi/docs/help/in/names.in @@ -0,0 +1,21 @@ + +@SYNTAX:names@ + + -ops: show channel operators in list + -halfops: show half operators in list + -voices: show voiced people in list + -normal: show rest of the people in list + +Shows the names (nicks) in the specified channels. /NAMES ** shows all +nicks in all channels, you probably don't want to do this. + +Examples: + +/NAMES + - shows nicks in the current channel. + +/NAMES -ops #c1,#c2 + - shows operators in channels #c1 and #c2 + +See also: WHO, CHANNEL + diff --git a/apps/irssi/docs/help/in/nctcp.in b/apps/irssi/docs/help/in/nctcp.in new file mode 100644 index 00000000..58b80545 --- /dev/null +++ b/apps/irssi/docs/help/in/nctcp.in @@ -0,0 +1,7 @@ + +@SYNTAX:nctcp@ + +Sends a CTCP reply notice to the nick/channel. + +See also: CTCP, ACTION, MSG, NOTICE + diff --git a/apps/irssi/docs/help/in/netsplit.in b/apps/irssi/docs/help/in/netsplit.in new file mode 100644 index 00000000..2e244ca1 --- /dev/null +++ b/apps/irssi/docs/help/in/netsplit.in @@ -0,0 +1,6 @@ + +@SYNTAX:netsplit@ + +Irssi keeps track of people who were lost in net splits. With this +command you can get a list of them. + diff --git a/apps/irssi/docs/help/in/nick.in b/apps/irssi/docs/help/in/nick.in new file mode 100644 index 00000000..57313b7f --- /dev/null +++ b/apps/irssi/docs/help/in/nick.in @@ -0,0 +1,6 @@ + +@SYNTAX:nick@ + +Changes your nick. This should be hardly rarely +used or needed. + diff --git a/apps/irssi/docs/help/in/note.in b/apps/irssi/docs/help/in/note.in new file mode 100644 index 00000000..d77bb230 --- /dev/null +++ b/apps/irssi/docs/help/in/note.in @@ -0,0 +1,28 @@ + +@SYNTAX:note@ + +NOTE is a sort of turbo-charged messaging system for irc. In short, +it achieves at the server level what the client attempts to do with MSG +and NOTIFY. The messaging system resembles modern voicemail systems +(except in text); messages can be sent, stored, or set for deferred +delivery. The client notification system works like NOTIFY, except with +greater accuracy and flexibility. + +The most common uses of NOTE are its SPY and SEND functions. SPY is similar +to NOTIFY, except it can accept a full address to spy on, not just a nickname. +SEND, as its name implies, sends a note to a user; if that user is not currently +online, it will be delivered if the user logs onto irc within a set time period. + +When referring to a particular user, NOTE can deal with the standard +nick!user@host notation. Wildcards are allowed, and any portion may be omitted, +so long as the identifier remains unambiguous. + +Examples: +To send a note to Joebob (whose account is jbriggs@drivein.com): +/NOTE SEND joebob!jbriggs@drivein.com Hey there! Great movie! + +To spy on anyone from blah.com for the next 30 days: +/NOTE SPY +30 *!*@*.blah.com A blah.com user is active + +This command is Not available in the IRCNet. + diff --git a/apps/irssi/docs/help/in/notice.in b/apps/irssi/docs/help/in/notice.in new file mode 100644 index 00000000..6b4da9b0 --- /dev/null +++ b/apps/irssi/docs/help/in/notice.in @@ -0,0 +1,10 @@ + +@SYNTAX:notice@ + +Sends a notice to the nick or the channel. Usually notices are +used in bots and scripts for different kinds of replies. The +IRC protocol states that notices may not generate replies to +avoid msg loops. + +See also: NCTCP, MSG + diff --git a/apps/irssi/docs/help/in/notify.in b/apps/irssi/docs/help/in/notify.in new file mode 100644 index 00000000..5f278e0c --- /dev/null +++ b/apps/irssi/docs/help/in/notify.in @@ -0,0 +1,15 @@ + +@SYNTAX:notify@ + + -away: Notifies about away-status changes + -idle: Notifies if idle time is first larger than + (default is hour) and then it drops down. + -list: Lists the notify list entries with all their settings + : Either a simple "nick" or "nick!*@*blah.org". + The nick can't contain wildcards, but the user/host can. + +/NOTIFY without any arguments displays if the people in notify +list are online or offline. + +See also: UNNOTIFY, SET NOTIFY + diff --git a/apps/irssi/docs/help/in/op.in b/apps/irssi/docs/help/in/op.in new file mode 100644 index 00000000..ce46cb63 --- /dev/null +++ b/apps/irssi/docs/help/in/op.in @@ -0,0 +1,8 @@ + +@SYNTAX:op@ + +Gives the channel operator privileges for the specified +nick(s). Wildcards in the nick are allowed. + +See also: DEOP, MODE, VOICE, DEVOICE, KICK + diff --git a/apps/irssi/docs/help/in/oper.in b/apps/irssi/docs/help/in/oper.in new file mode 100644 index 00000000..6cb6518c --- /dev/null +++ b/apps/irssi/docs/help/in/oper.in @@ -0,0 +1,10 @@ + +@SYNTAX:oper@ + +Gives you operator priviledges if the correct nickname and +password are given. If password is not given, you will be +prompted for one. If no nickname is given, your current +nickname will be used. + +See also: KILL, DIE + diff --git a/apps/irssi/docs/help/in/part.in b/apps/irssi/docs/help/in/part.in new file mode 100644 index 00000000..ff3b12fb --- /dev/null +++ b/apps/irssi/docs/help/in/part.in @@ -0,0 +1,8 @@ + +@SYNTAX:part@ + +Parts from the current or specified channel. Depending +on your settings, closes the corresponding window, too. + +See also: LEAVE, JOIN + diff --git a/apps/irssi/docs/help/in/perlflush.in b/apps/irssi/docs/help/in/perlflush.in new file mode 100644 index 00000000..27fc509f --- /dev/null +++ b/apps/irssi/docs/help/in/perlflush.in @@ -0,0 +1,8 @@ + +@SYNTAX:perlflush@ + +Stops and removes all Perl scripts which have been run. +Also undefines all the commands defined by Perl scripts. + +See also: RUN + diff --git a/apps/irssi/docs/help/in/ping.in b/apps/irssi/docs/help/in/ping.in new file mode 100644 index 00000000..3518b0c9 --- /dev/null +++ b/apps/irssi/docs/help/in/ping.in @@ -0,0 +1,10 @@ + +@SYNTAX:ping@ + +Sends CTCP PING to another IRC client. This is used +to find out the speed of IRC network. When the PONG +reply comes in, irssi shows the interval time between +sending the request and receiving the reply. + +See also: CTCP + diff --git a/apps/irssi/docs/help/in/query.in b/apps/irssi/docs/help/in/query.in new file mode 100644 index 00000000..818bbe64 --- /dev/null +++ b/apps/irssi/docs/help/in/query.in @@ -0,0 +1,11 @@ + +@SYNTAX:query@ + +Starts a private conversation with the nick. All text you +type that would normally be sent to your channel now goes to +the specified nick in the form of MSGs. + +Usually this command opens up a new window, too. + +See also: WINDOW, MSG, SET QUERY + diff --git a/apps/irssi/docs/help/in/quit.in b/apps/irssi/docs/help/in/quit.in new file mode 100644 index 00000000..f7536211 --- /dev/null +++ b/apps/irssi/docs/help/in/quit.in @@ -0,0 +1,10 @@ + +@SYNTAX:quit@ + +This ends your irc session. If a quit message is supplied, it +will be displayed to anyone else on any channel you were on +before quitting. If one isn't specified, the text "Leaving" is +used. + +/EXIT does the same. + diff --git a/apps/irssi/docs/help/in/quote.in b/apps/irssi/docs/help/in/quote.in new file mode 100644 index 00000000..eceb409c --- /dev/null +++ b/apps/irssi/docs/help/in/quote.in @@ -0,0 +1,8 @@ + +@SYNTAX:quote@ + +Sends server raw data without parsing. + +Example: + /QUOTE PRIVMSG cras :Hey, this works! + diff --git a/apps/irssi/docs/help/in/rawlog.in b/apps/irssi/docs/help/in/rawlog.in new file mode 100644 index 00000000..b863e234 --- /dev/null +++ b/apps/irssi/docs/help/in/rawlog.in @@ -0,0 +1,15 @@ + +@SYNTAX:rawlog@ + +All data that is received or sent to server is kept in a raw log +buffer for a while. Also event redirections are kept there. This is +very useful for debugging purposes. + +/RAWLOG SAVE - Save the current raw log buffer to file +/RAWLOG OPEN - Like /RAWLOG SAVE, but keep the log file + open and write all new log to it. +/RAWLOG CLOSE - Close the open raw log + +/SET rawlog_lines - Specify the number of raw log lines to + keep in memory. + diff --git a/apps/irssi/docs/help/in/reconnect.in b/apps/irssi/docs/help/in/reconnect.in new file mode 100644 index 00000000..81a7fc46 --- /dev/null +++ b/apps/irssi/docs/help/in/reconnect.in @@ -0,0 +1,10 @@ + +@SYNTAX:reconnect@ + +You can reconnect to server with /RECONNECT . + +/RECONNECT without any arguments will disconnect from the +active server and reconnect back immediately. + +See also: SERVER, DISCONNECT, RMRECONNS + diff --git a/apps/irssi/docs/help/in/rehash.in b/apps/irssi/docs/help/in/rehash.in new file mode 100644 index 00000000..2fb55791 --- /dev/null +++ b/apps/irssi/docs/help/in/rehash.in @@ -0,0 +1,11 @@ + +@SYNTAX:rehash@ + +IRC Operator command. + +This command is used to force the current server to reload it's +ircd.conf configuration file. This is useful for effecting +configuration changes without starting a new server. + +See also: OPER, RESTART + diff --git a/apps/irssi/docs/help/in/reload.in b/apps/irssi/docs/help/in/reload.in new file mode 100644 index 00000000..e7b2a5b6 --- /dev/null +++ b/apps/irssi/docs/help/in/reload.in @@ -0,0 +1,7 @@ + +@SYNTAX:reload@ + +Reloads the irssi's configuration file. + +See also: SAVE + diff --git a/apps/irssi/docs/help/in/restart.in b/apps/irssi/docs/help/in/restart.in new file mode 100644 index 00000000..14cd940f --- /dev/null +++ b/apps/irssi/docs/help/in/restart.in @@ -0,0 +1,12 @@ + +@SYNTAX:restart@ + +IRC Operator command. + +This command is used to completely restart the server. A side effect of +this is that the configuration file will be read again. However, it is +generally more useful for clearing out internal buffers and other +wasted memory. + +See also: OPER, DIE + diff --git a/apps/irssi/docs/help/in/rmreconns.in b/apps/irssi/docs/help/in/rmreconns.in new file mode 100644 index 00000000..e6e4e92c --- /dev/null +++ b/apps/irssi/docs/help/in/rmreconns.in @@ -0,0 +1,7 @@ + +@SYNTAX:rmreconns@ + +Removes the pending reconnections from the reconnect list. + +See also: CONNECT, RECONNECT, SERVER + diff --git a/apps/irssi/docs/help/in/rmrejoins.in b/apps/irssi/docs/help/in/rmrejoins.in new file mode 100644 index 00000000..a485e93a --- /dev/null +++ b/apps/irssi/docs/help/in/rmrejoins.in @@ -0,0 +1,9 @@ + +@SYNTAX:rmrejoins@ + +Removes the pending rejoins from the channel rejoin list in active +server. Channels are added to rejoin list when join failed because of +netsplits in server ("Channel is temporarily unavailable"). + +See also: JOIN + diff --git a/apps/irssi/docs/help/in/rping.in b/apps/irssi/docs/help/in/rping.in new file mode 100644 index 00000000..e0623173 --- /dev/null +++ b/apps/irssi/docs/help/in/rping.in @@ -0,0 +1,12 @@ + +@SYNTAX:rping@ + +IRC Operator command. + +This command works like the PING command (CTCP PING), except +it is used on a server instead of a client. As with PING, it +is used to test the relative distance another server is from +you across the irc network. + +See also: OPER + diff --git a/apps/irssi/docs/help/in/run.in b/apps/irssi/docs/help/in/run.in new file mode 100644 index 00000000..982808cb --- /dev/null +++ b/apps/irssi/docs/help/in/run.in @@ -0,0 +1,7 @@ + +@SYNTAX:run@ + +Runs a perl-script. For more information, see the +perl.txt in the docs-directory of irssi. + +See also: PERLFLUSH diff --git a/apps/irssi/docs/help/in/save.in b/apps/irssi/docs/help/in/save.in new file mode 100644 index 00000000..a6d5ea5b --- /dev/null +++ b/apps/irssi/docs/help/in/save.in @@ -0,0 +1,8 @@ + +@SYNTAX:save@ + +Saves the current Irssi configuration into the configuration +file. + +See also: RELOAD + diff --git a/apps/irssi/docs/help/in/sconnect.in b/apps/irssi/docs/help/in/sconnect.in new file mode 100644 index 00000000..0f19d349 --- /dev/null +++ b/apps/irssi/docs/help/in/sconnect.in @@ -0,0 +1,8 @@ + +@SYNTAX:sconnect@ + +IRC Operator command. Makes an IRC server to connect +to another server. + +See also: OPER, SQUIT, RESTART + diff --git a/apps/irssi/docs/help/in/scrollback.in b/apps/irssi/docs/help/in/scrollback.in new file mode 100644 index 00000000..7da9b820 --- /dev/null +++ b/apps/irssi/docs/help/in/scrollback.in @@ -0,0 +1,12 @@ + +@SYNTAX:scrollback@ + +/SCROLLBACK, or the default alias /SB: + +/SB CLEAR - Clear screen, free all memory used by texts in window. +/SB HOME - Jump to start of the buffer +/SB END - Jump to end of the buffer +/SB GOTO [[-|+]line#|time] - Jump to specified line or timestamp. + +See also: SET SCROLL + diff --git a/apps/irssi/docs/help/in/server.in b/apps/irssi/docs/help/in/server.in new file mode 100644 index 00000000..f75462cb --- /dev/null +++ b/apps/irssi/docs/help/in/server.in @@ -0,0 +1,30 @@ + +@SYNTAX:server@ + + -4, -6: specify explicitly whether to use IPv4 or IPv6 address + -auto: Automatically connect to server at startup (default) + -noauto: Don't connect to server at startup + -ircnet: Specify what IRC network this server belongs to + -host: Specify what host name to use, if you have multiple + -cmdspeed: Same as /SET cmd_queue_speed, see section 3.1 + -cmdmax: Same as /SET cmd_max_at_once, see section 3.1 + -port: This is pretty much like the port argument later, + except this can be used to modify existing server's + port. + +/SERVER disconnects the server in active window and connects +to the new one. It will take the same arguments as /CONNECT. +If you prefix the address with the + character, Irssi won't +disconnect the active server, and it will create a new window +where the server is connected (ie. /window new hide; +/connect address) + +/SERVER without any arguments displays the list of connected + servers. + +/SERVER REMOVE
[] + +/SERVER LIST + +See also: RMRECONNS, DISCONNECT, RECONNECT + diff --git a/apps/irssi/docs/help/in/servlist.in b/apps/irssi/docs/help/in/servlist.in new file mode 100644 index 00000000..cee9612d --- /dev/null +++ b/apps/irssi/docs/help/in/servlist.in @@ -0,0 +1,19 @@ + +@SYNTAX:servlist@ + +SERVLIST gives the list of services currently present on the +IRC network. It can take two arguments. + limits the output to the services which names matches + the mask. + limits the output to the services of the specified type. + +The fields returned are: + Service name. + Server who introduced the service. + Distribution mask. + Service type. + Hop count to the service. + A comment. + +See also: SQUERY + diff --git a/apps/irssi/docs/help/in/set.in b/apps/irssi/docs/help/in/set.in new file mode 100644 index 00000000..e355aaba --- /dev/null +++ b/apps/irssi/docs/help/in/set.in @@ -0,0 +1,23 @@ + +@SYNTAX:set@ + +You can view or change the settings with /SET command. + +/SET without any arguments displays all the settings. +/SET displays settings whose key (partly) matches +/SET sets to + +Boolean settings accept only values ON, OFF and TOGGLE. You can +also use /TOGGLE command to change them, so /TOGGLE behaves +like /SET TOGGLE. /TOGGLE also accepts arguments ON and OFF +when /TOGGLE behaves exactly like /SET. + +Remember that changes are not saved until you use /SAVE! + +Examples: + /SET autolog OFF - Sets value for setting 'autolog' + /SET close - Shows all settings whose variable name + contains 'close'. Very practical. + +See also: TOGGLE + diff --git a/apps/irssi/docs/help/in/silence.in b/apps/irssi/docs/help/in/silence.in new file mode 100644 index 00000000..4e37d7b7 --- /dev/null +++ b/apps/irssi/docs/help/in/silence.in @@ -0,0 +1,22 @@ + +@SYNTAX:silence@ + +Works only in the Undernet and Open Projects (ircu). + +SILENCE is similar in many respects to IGNORE, except that it is +server-based. What this means is the server will never even send +you messages from anyone you have SILENCEd, whereas it will with +IGNORE, where your client is responsible for filtering the messages +out. This has the advantage of not bogging your client down with +excessive data as it tries to filter out messages. + +The default behavior is to SILENCE a nick!user@host pattern, and +if such a pattern is not passed as the argument, it must be prepended +with a plus ('+') to be added to your silence list. If a pattern is +prepended with a minus ('-'), it will be removed from your silence list. +If you only specify a nickname, you can list the patterns in the +silence list owned by that nickname. If no arguments are given, your +own silence list is displayed. + +See also: IGNORE + diff --git a/apps/irssi/docs/help/in/squery.in b/apps/irssi/docs/help/in/squery.in new file mode 100644 index 00000000..a0bb4f11 --- /dev/null +++ b/apps/irssi/docs/help/in/squery.in @@ -0,0 +1,10 @@ + +@SYNTAX:squery@ + + - Service name + - Commands to pass to the service. + +/SQUERY sends a query to the specified service. + +See also: SERVLIST + diff --git a/apps/irssi/docs/help/in/squit.in b/apps/irssi/docs/help/in/squit.in new file mode 100644 index 00000000..64b8616c --- /dev/null +++ b/apps/irssi/docs/help/in/squit.in @@ -0,0 +1,7 @@ + +@SYNTAX:squit@ + +IRC Operator command. Makes server to quit IRC network. + +See also: OPER, DIE, RESTART + diff --git a/apps/irssi/docs/help/in/stats.in b/apps/irssi/docs/help/in/stats.in new file mode 100644 index 00000000..3afc6ebd --- /dev/null +++ b/apps/irssi/docs/help/in/stats.in @@ -0,0 +1,20 @@ + +@SYNTAX:stats@ + +Shows some irc server usage statistics. + c - Shows C and N lines for a given server. These are + the names of the servers that are allowed to connect. + h - Shows H and L lines for a given server (Hubs and Leaves). + k - Show K lines for a server. This shows who is not + allowed to connect and possibly at what time they are + not allowed to connect. + i - Shows I lines. This is who CAN connect to a server. + l - Shows information about amount of information passed + to servers and users. + m - Shows a count for the number of times the various + commands have been used since the server was booted. + o - Shows the list of authorized operators on the server. + u - Shows the uptime for a server + y - Shows Y lines, which lists the various connection + classes for a given server. + diff --git a/apps/irssi/docs/help/in/time.in b/apps/irssi/docs/help/in/time.in new file mode 100644 index 00000000..7877fe75 --- /dev/null +++ b/apps/irssi/docs/help/in/time.in @@ -0,0 +1,11 @@ + +@SYNTAX:time@ + +This displays the time of day, local to the server queried (thus, +the time returned may not be the same as the client's local time). + +If the server name is omitted, the client's current server is used. +If a nickname is given, that client's server is queried. + +Same as /DATE. + diff --git a/apps/irssi/docs/help/in/toggle.in b/apps/irssi/docs/help/in/toggle.in new file mode 100644 index 00000000..19913061 --- /dev/null +++ b/apps/irssi/docs/help/in/toggle.in @@ -0,0 +1,11 @@ + +@SYNTAX:toggle@ + +/TOGGLE behaves like /SET TOGGLE. /TOGGLE also +accepts arguments ON and OFF when /TOGGLE behaves exactly +like /SET. + +Remember that changes are not saved until you use /SAVE! + +See also: SET + diff --git a/apps/irssi/docs/help/in/topic.in b/apps/irssi/docs/help/in/topic.in new file mode 100644 index 00000000..ae2564f8 --- /dev/null +++ b/apps/irssi/docs/help/in/topic.in @@ -0,0 +1,8 @@ + +@SYNTAX:topic@ + + -delete - Deletes the topic. + +Shows or/and changes the topic of the current or specified +channel. + diff --git a/apps/irssi/docs/help/in/trace.in b/apps/irssi/docs/help/in/trace.in new file mode 100644 index 00000000..27df384c --- /dev/null +++ b/apps/irssi/docs/help/in/trace.in @@ -0,0 +1,8 @@ + +@SYNTAX:trace@ + +Without a specified server it shows the current connections on +the local server. If you specify a remote server it will show +all servers between your current server and that remote server +as well as the connections on that remote server. + diff --git a/apps/irssi/docs/help/in/ts.in b/apps/irssi/docs/help/in/ts.in new file mode 100644 index 00000000..0f51f96d --- /dev/null +++ b/apps/irssi/docs/help/in/ts.in @@ -0,0 +1,7 @@ + +@SYNTAX:ts@ + +Shows topics of all channels you're on. + +See also: CHANNEL, TOPIC + diff --git a/apps/irssi/docs/help/in/unalias.in b/apps/irssi/docs/help/in/unalias.in new file mode 100644 index 00000000..d3b2e744 --- /dev/null +++ b/apps/irssi/docs/help/in/unalias.in @@ -0,0 +1,7 @@ + +@SYNTAX:unalias@ + +Removes an alias. + +See also: ALIAS + diff --git a/apps/irssi/docs/help/in/unban.in b/apps/irssi/docs/help/in/unban.in new file mode 100644 index 00000000..a22963a1 --- /dev/null +++ b/apps/irssi/docs/help/in/unban.in @@ -0,0 +1,11 @@ + +@SYNTAX:unban@ + +Removes the specified ban(s) from the channel. + +Examples: + /UNBAN *!*@*.fi + /UNBAN larry!*@* *!me@*.mydomain.net + +See also: BAN, KNOCKOUT + diff --git a/apps/irssi/docs/help/in/unignore.in b/apps/irssi/docs/help/in/unignore.in new file mode 100644 index 00000000..6b5c5559 --- /dev/null +++ b/apps/irssi/docs/help/in/unignore.in @@ -0,0 +1,7 @@ + +@SYNTAX:unignore@ + +Unignores the specified userhost mask. + +See also: IGNORE + diff --git a/apps/irssi/docs/help/in/unload.in b/apps/irssi/docs/help/in/unload.in new file mode 100644 index 00000000..4bfdeb95 --- /dev/null +++ b/apps/irssi/docs/help/in/unload.in @@ -0,0 +1,8 @@ + +@SYNTAX:unload@ + +Unload a running plugin. List of running plugins can be shown with +/LOAD. + +See also: LOAD + diff --git a/apps/irssi/docs/help/in/unnotify.in b/apps/irssi/docs/help/in/unnotify.in new file mode 100644 index 00000000..5d74ce80 --- /dev/null +++ b/apps/irssi/docs/help/in/unnotify.in @@ -0,0 +1,7 @@ + +@SYNTAX:unnotify@ + +Removes an entry from the notify list. + +See also: NOTIFY + diff --git a/apps/irssi/docs/help/in/unquery.in b/apps/irssi/docs/help/in/unquery.in new file mode 100644 index 00000000..0d488c64 --- /dev/null +++ b/apps/irssi/docs/help/in/unquery.in @@ -0,0 +1,7 @@ + +@SYNTAX:unquery@ + +Removes a query window of specified nick. + +See also: QUERY, SET QUERY + diff --git a/apps/irssi/docs/help/in/unsilence.in b/apps/irssi/docs/help/in/unsilence.in new file mode 100644 index 00000000..110ff557 --- /dev/null +++ b/apps/irssi/docs/help/in/unsilence.in @@ -0,0 +1,9 @@ + +@SYNTAX:unsilence@ + +Works only in the Undernet and Open Projects (ircu). + +Removes a pattern from your silence list. + +See also: SILENCE + diff --git a/apps/irssi/docs/help/in/uping.in b/apps/irssi/docs/help/in/uping.in new file mode 100644 index 00000000..0fc003b8 --- /dev/null +++ b/apps/irssi/docs/help/in/uping.in @@ -0,0 +1,12 @@ + +@SYNTAX:uping@ + +IRC Operator command. Works only in the Undernet and Open Projects (ircu). + +This command works like the PING command (CTCP PING), except +it is used on a server instead of a client. As with PING, it +is used to test the relative distance another server is from +you across the irc network. + +See also: RPING, OPER + diff --git a/apps/irssi/docs/help/in/userhost.in b/apps/irssi/docs/help/in/userhost.in new file mode 100644 index 00000000..a1039f9d --- /dev/null +++ b/apps/irssi/docs/help/in/userhost.in @@ -0,0 +1,7 @@ + +@SYNTAX:userhost@ + +Shows the userhost info of the specified nick. + +See also: WHOIS + diff --git a/apps/irssi/docs/help/in/ver.in b/apps/irssi/docs/help/in/ver.in new file mode 100644 index 00000000..975fbf7a --- /dev/null +++ b/apps/irssi/docs/help/in/ver.in @@ -0,0 +1,9 @@ + +@SYNTAX:ver@ + +Sends a CTCP VERSION request to the nick. This is used +to find out which client and/or script the nick +is using. + +See also: CTCP + diff --git a/apps/irssi/docs/help/in/version.in b/apps/irssi/docs/help/in/version.in new file mode 100644 index 00000000..a7dd5c6f --- /dev/null +++ b/apps/irssi/docs/help/in/version.in @@ -0,0 +1,8 @@ + +@SYNTAX:version@ + +Shows the version info of the current or specified +IRC server. + +See also: ADMIN, STATS + diff --git a/apps/irssi/docs/help/in/voice.in b/apps/irssi/docs/help/in/voice.in new file mode 100644 index 00000000..ee6f243e --- /dev/null +++ b/apps/irssi/docs/help/in/voice.in @@ -0,0 +1,8 @@ + +@SYNTAX:voice@ + +Gives the voice (+v mode) to the nick(s) on the current channel. +Wildcards in the nick are allowed. + +See also: DEVOICE, OP, DEOP + diff --git a/apps/irssi/docs/help/in/wait.in b/apps/irssi/docs/help/in/wait.in new file mode 100644 index 00000000..4f5dc2f8 --- /dev/null +++ b/apps/irssi/docs/help/in/wait.in @@ -0,0 +1,10 @@ + +@SYNTAX:wait@ + +Wait for before sending the next command to server. + +This could be useful for example when identifying to NickServ; after +sending the identify message you'd wait 3 seconds before joining to +channels so NickServ has time to identify you to ChanServ which then +auto-ops you when joining. + diff --git a/apps/irssi/docs/help/in/wall.in b/apps/irssi/docs/help/in/wall.in new file mode 100644 index 00000000..212c3a8c --- /dev/null +++ b/apps/irssi/docs/help/in/wall.in @@ -0,0 +1,11 @@ + +@SYNTAX:wall@ + +This command sends a message to all operators in a channel. This is internal +irssi command which sends a message separately to each opearator, so this +may not be very good idea to use in channel with lots of operators. + +Some IRC servers support also /MSG @#channel or /WALLCHOPS which you should +use if possible. + +See also: WALLCHOPS diff --git a/apps/irssi/docs/help/in/wallchops.in b/apps/irssi/docs/help/in/wallchops.in new file mode 100644 index 00000000..034b1f68 --- /dev/null +++ b/apps/irssi/docs/help/in/wallchops.in @@ -0,0 +1,9 @@ + +@SYNTAX:wallchops@ + +Works only in the Undernet and Open Projects (ircu). + +Sends an message to all other channel operators of the current channel. + +See also: WALL + diff --git a/apps/irssi/docs/help/in/wallops.in b/apps/irssi/docs/help/in/wallops.in new file mode 100644 index 00000000..1690db2c --- /dev/null +++ b/apps/irssi/docs/help/in/wallops.in @@ -0,0 +1,9 @@ + +@SYNTAX:wallops@ + +This command sends the given message to everyone on +the network who has user mode +w turned on. If you +are not an operator, you will probably receive +an error message when using this command + +See also: OPER, WALLOPS diff --git a/apps/irssi/docs/help/in/who.in b/apps/irssi/docs/help/in/who.in new file mode 100644 index 00000000..014231ee --- /dev/null +++ b/apps/irssi/docs/help/in/who.in @@ -0,0 +1,23 @@ + +@SYNTAX:who@ + +Without parameters, shows all users and their user infos +on the current channel. If you specify a channel, shows +the all users of the given channel. + +If you specify an string with wildcards, you will be +shown all users whose nick, userhost or realname matches +the wildcard expression. + +If a channel is secret or private and you're not on it, +you will be shown only those channel members, who do not +have the invisible (+i) mode set. + +Examples: + /WHO - Shows users on current channel + /WHO #irssi - Shows users on channel #irssi + /WHO timo* - Shows users whose nick, userhost, + or realname begins with string 'timo' + +See also: WHOIS, CHANNEL + diff --git a/apps/irssi/docs/help/in/whois.in b/apps/irssi/docs/help/in/whois.in new file mode 100644 index 00000000..55f3e330 --- /dev/null +++ b/apps/irssi/docs/help/in/whois.in @@ -0,0 +1,15 @@ + +@SYNTAX:whois@ + +Shows whois information of the specified nick. +By default, this is aliased to /WI. + +/WHOIS nick1 nick1 also queries the idle time of +the user. This is aliased to /WII by default. + +If given nick is not in the IRC, irssi automatically +sends a WHOWAS query. Read carefully the reply to see +if it is a WHOIS or WHOWAS reply. :) + +See also: WHO, CHANNEL + diff --git a/apps/irssi/docs/help/in/whowas.in b/apps/irssi/docs/help/in/whowas.in new file mode 100644 index 00000000..2c7c68bc --- /dev/null +++ b/apps/irssi/docs/help/in/whowas.in @@ -0,0 +1,19 @@ + +@SYNTAX:whowas@ + +This command is similar to WHOIS, except it returns information +about nicknames that were recently in use. Like WHOIS, it shows +the nickname, address, real name, and server. It may also return +multiple entries if the nickname has been used recently by several +people. These multiples may be limited by specifying a count to show. + +WHOWAS will work regardless of whether the queried nick is in use. +If no arguments are given, the client's current nickname is used. + +Example: To show the last 5 users of the nickname JoeBob: + +/whowas joebob 5 + + +See also: WHOIS + diff --git a/apps/irssi/docs/help/in/window.in b/apps/irssi/docs/help/in/window.in new file mode 100644 index 00000000..7af71dc4 --- /dev/null +++ b/apps/irssi/docs/help/in/window.in @@ -0,0 +1,40 @@ + +@SYNTAX:window@ + +This command includes various subcommands for handling +irssi windows. + +/WINDOW CLOSE + - Closes the current window. +/WINDOW MOVE #NUMBER|PREV|NEXT + - Moves current window to another position in the + window list. +/WINDOW GOTO #NUMBER + - Moves into the specified window. +/WINDOW GOTO ACTIVE + - finds the first window with the higest + activity (msgs to you -> msgs -> rest). Alt-A is + the default shortcut key for this +/WINDOW GOTO #CHANNEL|NICK|=NICK + - moves you to the window with the channel, query or + dcc chat. +/WINDOW LEVEL [+/-]PUB/MSGS/... + - /window level msgs - creates messages window + - /window level all -msgs - creates status window +/WINDOW NEW [HIDDEN|SPLIT] + - creates new hidden/splitted window, + +Window logging + +/WINDOW LOG ON|OFF|TOGGLE [] + - Start/stop logging the active window. This works exactly like + /LOG OPEN -window. +/WINDOW LOGFILE + - Sets the default log file to use in the window, it can be + overridden by specifying the file name in /WINDOW LOG. + If no file name isn't given, Irssi defaults to + ~/irc.log. or ~/irc.log.Window if window + doesn't have a name. + +See also: SET CREATE, JOIN, QUERY, LOG + diff --git a/apps/irssi/docs/help/in/wjoin.in b/apps/irssi/docs/help/in/wjoin.in new file mode 100644 index 00000000..8386e178 --- /dev/null +++ b/apps/irssi/docs/help/in/wjoin.in @@ -0,0 +1,9 @@ + +@SYNTAX:wjoin@ + +With this you can join multiple channels in same +window. This command does the same as /JOIN but +it doesn't create a new window for the channel joined. + +See also: JOIN, WINDOW + diff --git a/apps/irssi/docs/help/in/wquery.in b/apps/irssi/docs/help/in/wquery.in new file mode 100644 index 00000000..ae4799af --- /dev/null +++ b/apps/irssi/docs/help/in/wquery.in @@ -0,0 +1,8 @@ + +@SYNTAX:wquery@ + +Starts a query in the current window without +opening a new window. + +See also: QUERY, WINDOW, SET AUTOCREATE + diff --git a/apps/irssi/docs/manual.txt b/apps/irssi/docs/manual.txt new file mode 100644 index 00000000..7c25c7bc --- /dev/null +++ b/apps/irssi/docs/manual.txt @@ -0,0 +1,1030 @@ + + Irssi 0.8 documentation - http://irssi.org/ + + Copyright(c) 2000 Timo Sirainen + + + Index + + 0. Generic babbling + 1. Installation + 2. Message levels + 3. Flood protection + 4. Configuration + 5. Servers + 6. Channels + 7. IRC commands and features + 8. Notify list + 9. Text highlighting + 10. Ignoring + 11. Logging + + ( not written yet: ) + 12. Aliases + 13. Themes + 14. Last log (currently text version only) + 15. Nick and word completion + 16. Translation tables + 17. Windowing system (text version) + 18. Keyboard (text version) + 19. Perl scripting + + + + 0. Generic babbling + + 0.1 History + + Hello. I'm Timo Sirainen aka. cras, and I'm an IRC addict. :) + + I'm actually quite new in IRC, I got my first internet connection + sometimes around fall 1997 and I started actively IRCing around + christmas. I used EPIC and BitchX mostly at the start, but soon + found some nice KDE IRC client which name I can't remember anymore. + It's author however stopped developing it after I had been using it + a few months. And since it had bugs and all, I wanted another nice + GUI IRC client. I didn't find any. + + Since I've always been a coder and do-it-yourself guy (my own + offline reader and BBS software in the BBS ages), I started my own + IRC client at spring 1998. I called it yagIRC standing for "Yet + another GTK IRC client". GTK was in around version 1.0 back then, + and it had a lot of features/bugs which I found all the time as I + tried to do some "different" things than other people. These + sometimes prevented me of doing something some feature I wanted. + + So, in summer 1998 I went to army and I passed development of yagIRC + to two guys, they did a few new features and released a version or + two, but finally (in summer 1999?) they put a message to web page + which told that they finally had stopped developing it entirely, + also saying that my code was a total mess :) (yes, it was a mess) + + I got out of the army 1.1.1999. I promised to myself that I wouldn't + do another IRC client, but after trying to use BitchX a while, I + started dreaming about an IRC client which would have an excellent + look and feel. After trying various things, I only came up with the + GNOME panel applet which people still tell me that it's a great + feature. I was more like thinking some pretty little icons in + some corner telling me about new messages and other stuff.. + + I thought that I would keep Irssi a small project, just doing a few + little features that *I* wanted, nothing for others. But after few + versions and few interested people, I started coding it more and + more generic.. + + Finally, after releasing version 0.6.0 (february, 1999) I realized + that things were getting into a big mess again. I started a rewrite, + I organized the code into irc-base, irc-extra, user interface and + GUI directories, created the signalling system for letting them + communicate themselves easily and released 0.7.0. This was the base + for the rest of the 0.7.x releases, and it did work pretty well. + The signalling system was excellent, for example creating text mode + version was really easy and you didn't need tens of (empty) gui_xxx() + functions like in the yagIRC days. Maintaining the text and GTK + versions separately was really easy too. + + About a year later after releasing Irssi 0.7.0, I started having + dreams about an IRC client that would be extremely modular, like you + could upgrade the client to newer version ON THE FLY without needing + to even disconnect from the servers. I started a project codenamed + i2k, I took the code from Irssi, split it into more directories and + changed quite a lot of the code to work a bit differently. + + I developed i2k quite a long, until I finally gave up with it since + it could do only some basic things, and Irssi 0.7 really needed + maintaining. After a while I got an idea, maybe I could merge the + code from the i2k to Irssi more easily than rewriting the whole + client. This was more easier than I thought. It's now been two + months since I started it, and Irssi 0.8 is looking absolutely + excellent. + + 0.2 Irssi 0.8 + + Irssi 0.8 is my fourth try to create the perfect IRC client. + This time I'm concentrating to the code. I try to avoid kludges, I + try to make as simple code as I can, and I try to provide enough + easy to use functions so that extending Irssi is as simple as + possible. I also try to keep the "bloat" features in scripts or + modules instead of build-in. + + I think I'm succeeded with these goals pretty well, there's some + small problems but everything in the big picture looks great. + + 0.3 Future + + What about Irssi 1.0, what will it look like? + + I was thinking about the Linux kernel versioning and keeping + Irssi 0.8 a stable version all the time while developing new + features only to Irssi 0.9. After 0.9 is finished, it will be + called 0.10 or 1.0 depending if I think it's ready to be called 1.0. + + 1.0's goal is that it has all the possible features anyone will + ever need. If not build-in, then in scripts or loadable modules. + Not very small goal :) + + 0.4 This documentation + + Strange, I just created the index list and started writing this. + I've never been too good at documentation and I usually don't like + writing it, but after coding so much recently and seeing that the + NEWS file was getting *SO* large, I thought that I had to put all + these features down somewhere so people (and me!) would find them. + + + 1. Installation + + 1.1 Configuration + + configure script accepts these parameters: + + --enable-ipv6 Enable IPv6 support + --disable-curses-windows Don't use curses windows. Use this if + screen drawing seems too slow for some reason. + This option may not work properly with all + curseses (solaris8). + --enable-memdebug Enable memory debugging, great for finding + memory leaks + + --enable-perl=static Build Perl support statically to irssi binary + (default is to build a module) + --enable-perl-path=dir Specify installation dir for Perl libraries + --disable-perl Disable Perl support + + --with-socks Build with socks library + --with-bot Build irssi-bot + --with-proxy Build irssi-proxy module + --without-textui Build without text frontend + --with-servertest Build test irc server which you can use to try + crash irc clients + + In short: + + ./configure + make + make install + + + 1.2 Command line parameters + + --connect -c Connect to server at startup + --port -p - specify port + --noconnect -! Don't autoconnect to any servers at startup + --nick -n Specify what nick to use + --hostname -h Specify what host name to use + + + 2. Message levels + + + Message levels (or in short, levels) are used almost everywhere. + They describe what kind of messages we're dealing with. Here's a + list of them all: + + CRAP - Can be almost anything + MSGS - Private messages + PUBLIC - Public messages in channel + NOTICES - Notices + SNOTES - Server notices + CTCPS - CTCP messages + ACTIONS - Actions (/me) - usually ORed with PUBLIC or MSGS + JOINS - Someone joins a channel + PARTS - Someone parts a channel + QUITS - Someone quits IRC + KICKS - Someone gets kicked from channel + MODES - Channel mode is changed + TOPICS - Channel topic is changed + WALLOPS - Wallop is received + INVITES - Invite is received + NICKS - Someone changes nick + DCC - DCC related messages + DCCMSGS - DCC chat messages + CLIENTNOTICES - Irssi's notices + CLIENTERRORS - Irssi's error messages + CLIENTCRAP - Some other messages from Irssi + + And a few special ones that could be included with the + levels above: + + HILIGHT - Text is highlighted + NOHILIGHT - Don't check highlighting for this message + NO_ACT - Don't trigger channel activity when printing + this message + NEVER - Never ignore or log this message + + + 3. Flood protection + + 3.1 Command flood protection + + Most (all?) IRC servers' flood protection works like this + (from RFC 1459): + + -------- + * check to see if client's `message timer' is less than + current time (set to be equal if it is); + + * read any data present from the client; + + * while the timer is less than ten seconds ahead of the current + time, parse any present messages and penalize the client by + 2 seconds for each message; + + which in essence means that the client may send 1 message every 2 + seconds without being adversely affected. + -------- + + Irssi's flood protection works the same way, except it penalizes + 2.2 seconds by default for each message (helps with some servers). + You can change it with /SET cmd_queue_speed . You can + also change the number of commands before flood protection activates + (ie. the burst count) with /SET cmd_max_at_once . + + IRC servers also have an input buffer where the client's commands + are saved before processed. It's size is server specific (can be as + low as 1k!) If it gets full, the server kicks you out (the + "Excess flood" quit message). Irssi's flood protecion protects this + pretty well with small commands, but if you send many big commands + (like >400 char long messages) fast, you could get easily kicked out. + Normally this isn't problem, but if you have scripts sending long + messages, you should remember this. I'm not sure how much you should + wait between the long messages, but 2 seconds isn't enough. + + This protection is used with all commands sent to server, so you + don't need to worry about it with your scripts. + + 3.2 CTCP flood protection + + Other people can pretty easily flood you with CTCP requests, and + even if you wouldn't get kicked out from the server, they could + easily grow your command queue. So, Irssi's CTCP flood protection + works like this: + + First it checks how big the CTCP reply queue is, if it's longer + than `max_ctcp_queue', the CTCP is ignored. You can change it with + /SET max_ctcp_queue (default is 5). + + After this the CTCP reply is placed to server's "idle queue", so + the reply is sent "when there's extra time", this means that if + you are busy sending other commands, it might take a while before + the reply is sent. + + 3.3 Detecting floods + + Irssi is all the time automatically checking different flooding, + when flood is noticed, it sends "flood" signal. This can be easily + used for example to create a script for kicking channel flooders. + Autoignore uses this also, see section 10.2. + + Flood is detected when more than `flood_max_msgs' same kind of + messages arrives in `flood_timecheck' seconds to same target + (channel or private msg) so it isn't flooding if same user sends a + message to 10 different channels you are on, but it is flooding if + 10 messages are sent to same channel by the same user. + + Currently only messages, notices and ctcps are checked for + flooding. + + /SET flood_max_msgs = , default is 4 + /SET flood_timecheck = , default is 5 seconds + If either of these is 0, the flood checking is disabled. + + + 4. Configuration + + 4.1 Configuration files + + The configuration is saved to ~/.irssi/config file. You can edit + it with text editor if you want, you can also add comments to it + and they stay there even if /SAVE is used. Comments are the lines + starting with # character. Any errors in config file are displayed + at startup. + + Irssi uses it's own config library for handling the config file. + The format is pretty much the same as in libPropList and should be + easily understandable. + + You can reload the config file on the fly with /RELOAD command, you + can also read a different config file with /RELOAD . + + If you change any settings, they aren't saved to file until you use + /SAVE. You can save the config file to different place with + /SAVE . + + 4.2 Settings + + You can view or change the settings with /SET command. + + /SET without any arguments displays all the settings. + /SET displays settings which key (partly) matches + /SET sets to + + Boolean settings accepts only values ON, OFF and TOGGLE. You can + also use /TOGGLE command to change them, so /TOGGLE behaves + like /SET TOGGLE. /TOGGLE also accepts arguments ON and OFF + when /TOGGLE behaves exactly like /SET. + + Remember that changes are not saved until you use /SAVE! + + + 5. Servers + + 5.1 Generic + + Irssi is multi-server friendly. You can be connected to multiple + different servers, or the same server multiple times. Most of the + settings that let you specify the channel, let you also specify IRC + network. + + Servers are referenced by a "server tag". If the server is known + to belong to some IRC network, the tag is the IRC network's name, + like "ircnet". If the IRC network is unknown, the tag is created + from the server's name, like irc.funet.fi -> funet. If the tag + already exists, a number is added to the end of it and raised until + unused tag is found. + + Quit messages have a small problem if there's already a few + commands in server's input command queue. If the server's socket is + disconnected immediately after QUIT message is sent, it is possible + that the server didn't yet process the quit command and your quit + message will be "broken pipe" or something similiar. The right thing + to do is to let the server disconnect you, but what if the + connection to server is broken and the server never disconnects you? + I solved the problem by waiting a few seconds to see if the server + disconnects us. If it didn't, force the disconnect. This explains + the (a bit annoying) "waiting for servers to close connections" + message when quiting Irssi. Most IRC clients just ignore this whole + problem, but I hate it if my quit message isn't displayed right. + + 5.2 IRC networks + + Different IRC networks behave a bit differently, and to be as + efficient as possible, Irssi needs to know a few things about them + or the safe defaults will be used. The default configuration file + contains the settings for the biggest IRC networks. + + /IRCNET ADD [-kicks ] [-msgs ] [-modes ] + [-whois ] [-cmdspeed ] [-cmdmax ] + [-nick ] [-user ] [-realname ] + [-host ] [-autosendcmd ] + + -kicks: Maximum number of nicks in one /KICK command + -msgs: Maximum number of nicks in one /MSG command + -modes: Maximum number of mode changes in one /MODE command + -whois: Maximum number of nicks in one /WHOIS command + -cmdspeed: Same as /SET cmd_queue_speed, see section 3.1 + -cmdmax: Same as /SET cmd_max_at_once, see section 3.1 + -nick, -user, -realname: Specify what nick/user/name to use + -host: Specify what host name to use, if you have multiple + -autosendcmd: Command to send after connecting to a server + + With -autosendcmd argument you can automatically run any commands + after connecting to ircnet. This is useful for automatically + identifying yourself to NickServ, for example + + /IRCNET ADD -autosendcmd "/msg NickServ identify secret" OPN + + /IRCNET REMOVE + + 5.3 Manually connecting and disconnecting + + To connect to a new server, use: + /CONNECT [-ircnet ] [-host ]
| + [ [ []]] + + If there's no password, set it to -. You can directly connect to + IRC server in specified address, or you can connect to some IRC + network and Irssi will pick the server for you. + + You don't need to specify the IRC network, password, nick, etc. if + you setup the server using /SERVER ADD (see next section). If the + settings can't be found there either, Irssi will use the defaults: + + /SET default_nick = , defaults to user_name + /SET alternate_nick = , defaults to _ + /SET user_name = , defaults to your login name + /SET real_name = , taken from /etc/passwd by default + /SET hostname = , what host name to use when connecting + /SET skip_motd ON|OFF|TOGGLE - Don't show server's MOTD + + NOTE: /CONNECT is also a command for IRC operators to connect IRC + servers to other IRC servers. If you want to use it, use /SCONNECT + instead. + + You can disconnect from the server with: + /DISCONNECT *| [message] + + If message isn't given, Irssi will use the default quit message. You + can set it with /SET quit_message , default is "leaving". + + /SERVER disconnects the server in active window and connects to new + one. It will take the same arguments as /CONNECT. If you prefix the + address with + character, Irssi won't disconnect the active server, + and it will create a new window where the server is connected + (ie. /window new hide;/connect address) + + /SERVER without any arguments displays list of connected servers. + + 5.4 Server settings + + /SERVER ADD [-auto | -noauto] [-ircnet ] [-host ] + [-cmdspeed ] [-cmdmax ] [-port ] +
[ []] + + -auto: Automatically connect to server at startup + -noauto: Don't connect to server at startup (default) + -ircnet: Specify what IRC network this server belongs to + -host: Specify what host name to use, if you have multiple + -cmdspeed: Same as /SET cmd_queue_speed, see section 3.1 + -cmdmax: Same as /SET cmd_max_at_once, see section 3.1 + -port: This is pretty much like the port argument later, except + this can be used to modify existing server's port. + + /SERVER REMOVE
[] + + /SERVER LIST + + Servers are identified by their name and port. You can have multiple + entries for the same server name but in different ports. This is + useful for IRC proxies, in one port you could have IRCNet proxy, + another port would have EFNet, etc. + + If you wish to change existing server's port to something else, use + -port command. For example if you had irc.server.org in port 6667 + and you wanted to change it to port 6668, use command: + + /SERVER ADD -port 6668 irc.server.org 6667 + + If you want to remove some settings from existing server, for + example hostname, just give -host "" parameters to it. + + After connected to server, Irssi can automatically change your user + mode. You can set it with /SET usermode , default is +i. + + 5.5 Automatic reconnecting + + If you get disconnected from server, Irssi will try to reconnect + back to some of the servers in the same IRC network. To prevent + flooding the server that doesn't let you in (and avoiding K-lines), + Irssi won't try to reconnect to the same server more often than + once in `server_reconnect_time' seconds. You can change it with + /SET server_reconnect_time , default is 5 minutes. + + After reconnected to server, Irssi will re-set your user mode, away + message and will join you back to the same channels where you were + before the connection was lost. + + You can see list of the reconnections with /SERVER. The servers + that have tag as RECON-n are the reconnections. You can remove them + with /DISCONNECT , and you can reconnect to them immediately + with /RECONNECT . /RECONNECT without any arguments will + disconnect from the active server and reconnect back immediately. + + 5.6 Command redirections + + FIXME + + 5.7 Server idle command queue + + There's some situations when you want to ask something from the + server which isn't really important. For example when connected + to server and you didn't get your nick, Irssi asks with /WHOIS + who has your nick and displays it. But if you already have a lot of + commands in buffer, like you just autojoined to many channels, + you'd rather first let the JOIN commands to be sent to server + + This is where server idle queue gets into picture. Commands in + idle queue are sent to server when there's nothing else in the + normal command queue. + + Idle queue works with server redirections, so you can ask something + from server when it has time and your function is called when the + reply is received. + + 5.8 Net splits + + Irssi keeps track of people who were lost in net splits. You can + get a list of them with /NETSPLIT command. + + Another use for this is with bots. Channel master can op anyone in + the channel and the bot happily accepts it. But if the opped user + is lost behind a net split and in netjoin the server gives ops for + the user, the bot should decide if the user (who isn't in bot's user + database) is a malicious attacker who should be deopped, or if + he/she/it is just some user that already had ops before the net + split. + + /SET hide_netsplit_quits - If ON, hide all netsplit quit messages + and display only "Netsplit host1 host2: nicks". + + /SET netsplit_max_nicks - If non-zero, limit the number of nicks + printed in netsplit message and add "(+ more, use /NETSPLIT + to show all of them)" text. + + 5.9 Lag checking + + Irssi will constantly check how big the lag to the server is. It's + done by sending IRSSILAG CTCP replies to ourself. Using PING command + for this would seem more reasonable, but there was too many problems + with it - some servers didn't even know the whole PING command! + + If the lag is too big, Irssi will reconnect to different IRC server. + This is sometimes useful if the connection has been stuck for 30 + minutes but it still hasn't been closed. + + /SET lag_check_time - Specifies how often to check the + lag. If it is set to 0, the lag detection is disabled. Default + is 30 seconds. + /SET lag_max_before_disconnect - Specifies how big the lag + can be before reconnecting to another server. Default is 5 + minutes. + /SET lag_min_show <100th seconds> - Specifies the minimum lag to + display in status bar. Default is 1 second. + + 5.10 Raw log + + All data that is received or sent to server is kept in a raw log + buffer for a while. Also event redirections are kept there. This is + very useful for debugging purposes. + + /RAWLOG SAVE - Save the current raw log buffer to file + /RAWLOG OPEN - Like /RAWLOG SAVE, but keep the log file + open and write all new log to it. + /RAWLOG CLOSE - Close the open raw log + + /SET rawlog_lines - Specify the number of raw log lines to + keep in memory. + + + 6. Channels + + 6.1 Generic + + There's several types of channels you can join, here's a list of + the ones that Irssi supports: + + #channel - Normal channels, most commonly used + +channel - Modeless channels, channel has no modes, no channel + operators and no topic. This way no-one is above others + and there's no operator-wars etc. But on the other hand, + you can't kick any troublemakers.. + &channel - Local channels, these channels aren't distributed outside + the IRC server. IRCNet has replaced server notices with + several different &channels (&ERRORS, &NOTICES, etc.) + !channel - New channels, currently supported only by IRCNet. These + channels are designed so that they can't be taken over + with net splits. /JOIN !channel joins to existing + !channel, /JOIN !!channel creates a new channel. + + Most of the commands that take channel name as parameter, can also + accept * as the channel name, which means the active channel. + + + 6.2 Joining, parting + + Channels can be joined with /JOIN command. You can join to multiple + channels with one /JOIN by giving it a comma-separated list of + channels, like /JOIN #channel1,#channel2. If you don't give the + channel mode character (#+&!) before the channel name, Irssi + automatically uses # channels. + + Channel name may contain any characters except SPACE, BELL, NUL, + CR, LF or comma (','). You can also restrict the channel to only + certain users by adding the hostmask to the end of the channel + name separated with a ':' character, like #channel:*!*@*.fi lets + only people from .fi domain join the channel. This doesn't work with + all IRC servers and it's pretty difficult to use, since everyone + will have to always join the #channel:*!*@*.fi channel, #channel or + #channel:*!*@*.se channels are different channels. Ban exceptions + (+e) and especially invite lists (+I) replace this functionality + pretty well, see section 6.5. + + If channel has a password (aka. key), you can join it with + /JOIN #channel pass, or multiple channels with passwords with + + /JOIN #secret1,#public,#secret2 pass1,x,pass2 + + #public didn't have any password, so we used "x" as it's password. + It doesn't really matter what password you send with channels that + don't have passwords. + + If you want to join to channel in different server than active one + in window, you can do it with /JOIN - #channel, like + /JOIN -efnet #irssi. + + You can leave channels with /PART [] []. + For example "/PART byebye all" leaves the active channel with + "byebye all" message, or /PART #chan1,#chan2 leaves those channels. + + NOTE: Sending JOIN 0 directly to server (/quote join 0) leaves all + the channels you are joined. There's been some jokes about joining + for example to #2000,0 where the server actually leaves you from all + channels. With Irssi this isn't really a problem, since irssi would + happily join to channels #2000 and #0. + + 6.3 Automatic joining + + Irssi can automatically join to specified channels in specified + IRC networks. It can also automatically send the password when + manually joining to channel without specifying the password. + + /CHANNEL ADD [-auto | -noauto] [-bots ] [-botcmd ] + [] + + With -bots and -botcmd arguments you can automatically send + commands to someone in channel. This is useful for automatically + getting ops for channels, for example + + /CHANNEL ADD -auto -bots "*!bot@bothost.org bot*!*@host2.org" + -botcmd "msg $0 op mypass" #channel ircnet + + You can also use the -botcmd without -bots argument. The command is + then sent whenever you join the channel. + + If you want to remove some settings from existing channel record, + for example bots, just give the -bots "" parameters to it. Password + can be removed by setting it to - (or actually, "" works too). + + You can remove the channels with + /CHANNEL REMOVE + + /CHANNEL LIST displays list of channels with settings. + /CHANNEL without any arguments displays list of channels you have + joined. You can also use /CHANNEL to join to channels just as with + /JOIN, like /CHANNEL #a. + + 6.4 After-join automation + + When joined to channel, Irssi asks some information about it. + After it has got all of it, it prints the "Channel synchronized" + text. The following information is asked: + + - Channel mode + - WHO list to get nicks' hosts - useful for /BAN for example + - Ban list - useful for allowing /UNBAN to use wildcards + - Exception list, Invite list - these are asked only from servers + that support +I and +e modes, mostly just IRCNet and some EFNet + servers. These aren't really needed for anything currenty, except + /INVITELIST and /BANS uses them to display the lists. + + If you have joined many channels at once, Irssi tries to optimize + the commands it sends to server. Instead of sending two commands + to ask two channels' mode, it just sends MODE #a,#b. Same thing with + WHO list and ban/except/invite lists. Some servers do not support + this and they reply with different kinds of error messages, Irssi + tries to deal with them all right and resend the commands again + separately. However, some strange servers sometimes use some weird + error replies that Irssi doesn't know about, and the channel never + gets synchronized. If this happens with some server you know, please + let the Irssi's author know about it. + + 6.5 Channel modes + + Irssi knows these channel modes: + + i - Invite only - People can't join to channel without being + /INVITEd, or being in invite list (+I, see below). + m - Moderated - People who don't have voices (+v) can't send + messages to channel + p - Private - People who aren't joined to channel can't see it + for example with /WHOISing people who are in channel. + s - Secret - Like private, but the channel isn't displayed in + /LIST's output. + n - No external msgs - Without this mode, anyone can send messages + to channel without even being joined. + t - Topic can be changed only by channel operators. + + k - Channel password (aka. key) - The channel can't be joined + without specifying the channel key (see section 6.2). + + l - User limit - No more than people can join to + channel. This can be overridden with /INVITE with some + servers. + + This is usually used for protecting channel from join + flooding, like some bot allows max. 5 users to join in + one minute or so. + + a - Anonymous - No-one's nick name, host or anything else can be + seen. All messages, joins, parts, modes, etc. are seen as coming + from nick "anonymous", this could be pretty confusing but nice + feature if you want total anonymity. This mode can only be set, + never unset. This mode isn't supported by all servers. + + NOTE: there is/was one bug :) Channel operators can guess if some + nick might be in the channel and try to kick it. If nick was in + channel, everyone will see the nick that was kicked. + + r - Re-op - If channel becomes opless for longer than 45 (?) minutes, + op everyone in the channel. This works only in !channels. This + mode can only be set, not unset by channel creator. + + b - Set/remove ban. For example MODE #channel +b *!*@*.org bans + everyone from .org domain. + + If someone from .org domain was already in channel before the + ban was set, he/she couldn't be able to write any messages to + channel (doesn't work with all servers). + + Ban can also be overridden with /INVITE, although many stupid + IRC clients automatically kick the user out because they see + the ban and think that because of it the user shouldn't be in + the channel (doesn't work with all servers). + + e - Ban exceptions. You could for example ban everyone from + *!*@*.org but set ban exception to *!*@*.host.org - works only + in IRCnet/EFnet servers. + + I - Invite list. If channel is invite only (+i), people in this + list can join it without being /INVITEd - works only in + IRCnet/EFnet servers. + + This is excellent for in-country channels that don't want + foreigners (spammers!) to join the channel, for example setting + channel's mode to +i and +I *!*@*.fi allows only finnish people + to join the channel. In addition to this, there's usually a bot + in the channels and sending /MSG bot invite command to it + /INVITEs you to the channel. + + The ':' feature in channel modes is quite similiar, see section + 6.2. + + O - Channel owner, the nick who creates a !channel receives this + mode. It isn't displayed anywhere, you can't pass it to anyone + else and you can't regain it again. This is needed for setting + +r mode in channel when it's first created. + + o - Grant or revoke channel operator status from nick + v - Grant or revoke voice status from nick, only people with + +v (or +o) can talk to channel when it's moderated (+m). + + You can send multiple mode changes with one mode command: + + /MODE #channel +nto-o+v nick1,nick2,nick3 + + This would set channel's mode to +nt, give ops to nick1, take ops + from nick2 and give voices to nick3. + + You can set only limited number of modes that requires argument in + one command. In IRCnet it's 3, in EFnet it's 4 and in many others + it's 6. If it's not known, Irssi defaults to 3. Irssi will also + automatically split them, so you can use /MODE +oooooo n1,n2,.. + command to op 6 people and Irssi will split it to two commands in + IRCnet/EFnet. + + Instead of manually setting o, v and b modes you probably want to + use /OP, /DEOP, /VOICE, /DEVOICE, /BAN and /UNBAN commands. + + /OP, /DEOP, /VOICE and /DEVOICE commands allows wildcards as their + argument. So /OP ni* will op all non-opped people whose nick start + with "ni". /DEOP * will deop everyone else except you. /VOICE and + /DEVOICE work the same way. + + 6.6 Bans + + You can give /BAN a list of nicks or whole ban masks. /UNBAN + accepts wildcards, so if you have ban nick!user@reallylonghost.org, + you can simply unban it with /UNBAN *really* + + Using /BAN , Irssi will automatically create the mask. You + can change the way it's created with /BANTYPE command: + + /BANTYPE normal|host|domain|custom + + Normal - *!user@*.domain.net + Host - *!*@host.domain.net + Domain - *!*@*.domain.net + Custom [nick] [user] [host] [domain] + eg. /bantype custom nick domain - nick!*@*.domain.net + eg. /bantype custom user host - *!user@host.domain.net + + Irssi has also a couple of commands to help banning people: + + /KICKBAN [] - ban and kick the nick + /KNOCKOUT [] - kickban the nick, unban + after waiting , default is 5 minutes. + + 6.7 Massjoins + + Automatic opping the nick right after joined to channel is a pretty + commonly used. What mostly irritates me with this is that the nick + may be opped multiple times by different people, or after netsplits + when the people join back, the server will op them, but still the + bots op the people again, even if it was just done by the server. + + Irssi has this feature that it sends a "massjoin" signal a while + after the real join. If someone has already opped the nick, you can + easily check it in the massjoin signal handler. + + The default is to report maximum of 5 joins in one massjoin signal. + If the 5 joins don't come in 5 seconds, the signal is sent anyway. + You can change these with /SET massjoin_max_wait and + /SET massjoin_max_joins . + + + 7. IRC commands and features (FIXME) + + 7.x Basic commands + + 7.x IRC operator commands + + 7.x Away features + + 8. Notify list + + Notify list is generally used for knowing when someone you know + comes to IRC or leaves from IRC. Traditionally notify list can + handle only a list of nicks, no nick masks etc. I lost interest to + traditional notify lists long time ago, since the people I know + are in IRC all the time. So I made a bit more featureful notify + list: + + /NOTIFY [-list] [-away] [-idle [minutes]] [ircnet [ircnet...]] + + -away: Notifies about away-status changes + -idle: Notifies if idle time is first larger than + (default is hour) and then it drops down. + -list: Lists the notify list entries with all their settings + : Either a simple "nick" or "nick!*@*blah.org". The nick + can't contain wildcards, but the user/host can. + + /UNNOTIFY + + /NOTIFY without any arguments displays if the people in notify + list are online or offline. + + + 9. Text highlighting + + Irssi supports highlighting lines that match the specified pattern. + You can also change the color of the nicks that match specified nick + mask, so you could for example show your friends' nicks with + different color. + + /HILIGHT [-mask | -regexp | -word] [-nick] [-color ] + [-level ] [-channels ] + + -mask: Match only for nick, is a nick mask + -regexp: is a regular expression + -word: must match to full words + -nick: Hilight only the nick, not the whole line + -color: Print the reply with - see below + -level: Match only for messages, default is + publics,msgs,notices,actions + -channels: Match only in + + /DEHILIGHT | + + /HILIGHT without any arguments displays list of the hilights. + + By default the highlighted line will be printed with white color. + You can change this with the -color argument. If is a + number, Irssi will treat it as a MIRC color code. You can also use + bolds (^B), underlines (^_) etc. as if you like. + + + 10. Ignoring + + 10.1 Manual ignoring + + Irssi's ignoring options should be enough for everyone :) + + /IGNORE [-regexp | -word] [-pattern ] [-replies] [-except] + [-channels ] <^levels> + + -regexp: is a regular expression + -word: must match to full words + -pattern: must match to the message's text + -replies: Ignore replies to nick in channels. For example + "/IGNORE -replies *!*@*.fi PUBLIC" ignores everyone + from Finland, but also anyone sending message + "tofinnishnick: blahblah". + -except: *DON'T* ignore + -channels: Ignore only in channels + : Either a nick mask or list of channels + : List of levels to ignore + <^levels>: List of levels to NOT ignore + (/ignore -except nick notices = /ignore nick ^notices) + + /UNIGNORE | + + /IGNORE without any arguments displays list of ignores. + + The best match always wins, so you can have: + + /IGNORE * CTCPS + /IGNORE -except *!*@host.org CTCPS + + 10.2 Automatic ignoring + + Irssi can automatically set ignores for people who flood you. + Currently you can autoignore MSGS, NOTICES, CTCPS and PUBLIC. + Actions are placed to either MSGS or PUBLIC. See section 3.3 for + definition of the flood. + + /SET autoignore_time specifies how long to ignore the + user. + + /SET autoignore_levels specifies what levels to ignore + automatically, default is to ignore only CTCPS. + + + 11. Logging + + 11.1 Basic logging + + /LOG OPEN [-noopen] [-autoopen] [-targets ] + [-window] [] + + -noopen: Create the entry to log list, but don't start logging + -autoopen: Automatically open this log file at startup + -targets: Log only in specified channels/nicks + -window: Log the active window + : File name where to log, it is parsed with + strftime(), so %d=day, etc. see "man strftime" for + more info. Irssi will automatically check every hour + if log should be rotated. + : Defaults to ALL + + /LOG CLOSE | - Close log and remove from log list + /LOG START | - Start logging to file + /LOG STOP | - Stop logging to file + /LOG without any arguments displays the log list + + /SET log_create_mode - Specifies what file mode to use with + the created log files. Default is 0644. + + All of these are parsed with strftime(): + /SET log_timestamp - Specifies the time stamp format. + Default is "%H:%M ". + /SET log_open_string - Text written to log when it's opened + /SET log_close_string - Text written to log when it's closed + /SET log_day_changed - Text written to log when day changes + + NOTE: Log files are locked after opened, so two Irssis can't + accidentally try to write to the same log file. + + Examples: + + /LOG OPEN -targets cras ~/irclogs/cras.log MSGS + - Logs all messages from/to nick `cras' + + /LOG OPEN -targets #linux ~/irclogs/linux/linux-%Y-%m-%d + - Logs all messages in channel #linux. Log is rotated daily, so + logs in 1. May 2000 goes to file "linux-2000-05-01", when the + day is changed, Irssi closes the log and starts logging to + "linux-2000-05-02" etc. + + 11.2 Window logging + + /WINDOW LOG ON|OFF|TOGGLE [] + + Start/stop logging the active window. This works exactly like + /LOG OPEN -window. + + /WINDOW LOGFILE + + Sets the default log file to use in the window, it can be + overridden with specifying the file name in /WINDOW LOG. If no file + name isn't given, Irssi defaults to ~/irc.log. or + ~/irc.log.Window if window doesn't have name. + + Creates the entry to log list, same as /LOG OPEN -window -noopen. + Also, if /WINDOW LOG ON is used it starts logging to this file. + + 11.3 Automatic logging + + This is the logging method that I had been asked to implement for + ages, and it is really simple to use too. It logs only messages + that have "targets", ie. private messages and channel specific + messages (msgs, modes, topics, etc). WHOIS replies and such aren't + logged. If you with to log them too, use the /LOG command. + + So, when for example a private messages comes to you from "guy" + nick, Irssi creates a log file ~/irclogs/guy.log for it. After few + minutes of inactivity, the log file is closed. + + /SET AUTOLOG ON|OFF|TOGGLE - Enable/disable autolog. + + /SET AUTOLOG_LEVEL - Specifies what levels to log, default + is ALL. + + /SET AUTOLOG_PATH - expandos (see special_vars.txt) can be + used, $0 is the target. If you are using multiple servers, it makes + sense to use the server tag as part of the file name, for example + ~/irclogs/$tag/$0.log (this is the default). The directories are + created automatically. + + 11.4 Awaylog + + Irssi logs specified messages when you're away. After you set + yourself unaway, Irssi will display all the messages in the awaylog. + + /SET awaylog_level - Default is MSGS HILIGHT + /SET awaylog_file - Default is ~/.irssi/away.log + + You can disable this feature by setting awaylog_level to NONE. + + +.. no, the docs end here, I got bored of writing these after a few days and +haven't touched these since then. diff --git a/apps/irssi/docs/startup-HOWTO.html b/apps/irssi/docs/startup-HOWTO.html new file mode 100644 index 00000000..e0af3492 --- /dev/null +++ b/apps/irssi/docs/startup-HOWTO.html @@ -0,0 +1,647 @@ +

Startup HOWTO

+ +

To new Irssi users (not to new IRC users ..)

+ +

Copyright (c) 2000-2001 by Timo Sirainen

+ + +

Index with some FAQ questions that are answered in the chapter:

+ +
    +
  1. For all the lazy people
  2. +
  3. Basic user interface usage
  4. +
  5. Server and channel automation +
      +
    • how do I automatically connect to servers at startup?
    • +
    • how do I automatically join to channels at startup?
    • +
  6. +
  7. Setting up windows and automatically restoring them + at startup
  8. +
  9. Status and msgs windows & message levels +
      +
    • I want /WHOIS to print reply to current window
    • +
    • I want all messages to go to one window, not create new windows
    • +
  10. +
  11. How support for multiple servers works in irssi +
      +
    • I connected to some server that doesn't respond and now irssi + keeps trying to reconnect to it again and again, how can I stop + it??
    • +
    • I want to have own status and/or msgs window for each servers
    • +
  12. +
  13. /LASTLOG and jumping around in scrollback +
      +
    • How can I save all texts in a window to file?
    • +
  14. +
  15. Logging
  16. +
  17. Irssi's settings
  18. +
+ +

1. For all the lazy people

+ +

These settings should give you pretty good defaults (the ones I use):

+ +

I don't like automatic query windows, I don't like status window, I do +like msgs window where all messages go:

+ +
+     /SET autocreate_own_query OFF
+     /SET autocreate_query_level DCCMSGS
+     /SET use_status_window OFF
+     /SET use_msgs_window ON
+
+ +

Disable automatic window closing when /PARTing channel or /UNQUERYing +query:

+ +
+     /SET autoclose_windows OFF
+     /SET reuse_unused_windows ON
+
+ +

And example how to add servers:

+ +

(openprojects network, identify with nickserv and wait for 2 seconds before +joining channels)

+ +
+     /IRCNET ADD -autosendcmd "/^msg nickserv ident pass;wait -opn 2000" opn
+
+ +

Then add some servers to different networks (ircnet is already set up +for them), irc.kpnqwest.fi is used by default for IRCNet but if it fails, +irc.funet.fi is tried next:

+ +
+     /SERVER ADD -auto -ircnet ircnet irc.kpnqwest.fi 6667
+     /SERVER ADD -ircnet ircnet irc.funet.fi 6667
+     /SERVER ADD -auto -ircnet efnet efnet.cs.hut.fi 6667
+
+ +

Automatically join to channels after connected to server, send op request +to bot after joined to efnet/#irssi:

+ +
+     /CHANNEL ADD -auto #irssi ircnet
+     /CHANNEL ADD -auto -bots *!*@bot@host.org -botcmd "/^msg $0 op pass"
+		  #irssi efnet
+
+ +

2. Basic user interface usage

+ +

By default, irssi uses "hidden windows" for everything. Hidden +window is created every time you /JOIN a channel or /QUERY someone. +There's several ways you can change between these windows:

+ +
+     Meta-1, Meta-2, .. Meta-0 - Jump directly between windows 1-10
+     Meta-q .. Meta-p          - Jump directly between windows 11-20
+     /WINDOW <number>          - Jump to any window with specified number
+     Ctrl-P, Ctrl-N            - Jump to previous / next window
+
+ +

Clearly the easiest way is to use Meta-number keys. And what is the Meta +key? For some terminals, it's the same as ALT. If you have Windows keyboard, +it's probably the left Windows key. If they don't work directly, you'll need +to set a few X resources (NOTE: these work with both xterm and rxvt):

+ +
+     XTerm*eightBitInput:   false
+     XTerm*metaSendsEscape: true
+
+ +

With rxvt, you can also specify which key acts as Meta key. So if you +want to use ALT instead of Windows key for it, use:

+ +
+     rxvt*modifier: alt
+
+ +

And how exactly do you set these X resources? For Debian, there's +/etc/X11/Xresources/xterm file where you can put them and it's read +automatically when X starts. ~/.Xresources and ~/.Xdefaults files might also +work. If you can't get anything else to work, just copy&paste those lines to +~/.Xresources and directly call "xrdb -merge ~/.Xresources" in some xterm. +The resources affect only the new xterms you start, not existing ones.

+ +

Many windows SSH clients also don't allow usage of ALT. One excellent +client that does allow is putty, you can download it from + +http://www.chiark.greenend.org.uk/~sgtatham/putty/.

+ +

Irssi also supports split windows, they've had some problems in past +but I think they should work pretty well now :) Here's some commands +related to them:

+ +
+     /WINDOW NEW                    - Create new split window
+     /WINDOW NEW HIDE               - Create new hidden window
+     /WINDOW CLOSE                  - Close split or hidden window
+
+     /WINDOW HIDE [<number>|<name>] - Make the split window hidden window
+     /WINDOW SHOW <number>|<name>   - Make the hidden window a split window
+
+     /WINDOW SHRINK [<lines>]       - Shrink the split window
+     /WINDOW GROW [<lines>]         - Grow the split window
+     /WINDOW BALANCE                - Balance the sizes of all split windows
+
+ +

By default, irssi uses "sticky windowing" for split windows. This means +that windows created inside one split window cannot be moved to another +split window without some effort. For example you could have following +window layout:

+ +
+     Split window 1: win#1 - Status window, win#2 - Messages window
+     Split window 2: win#3 - ircnet/#channel1, win#4 - ircnet/#channel2
+     Split window 3: win#5 - efnet/#channel1, win#6 - efnet/#channel2
+
+ +

When you are in win#1 and press ALT-6, irssi jumps to split window +#3 and moves the efnet/#channel2 the active window.

+ +

With non-sticky windowing the windows don't have any relationship with +split windows, pressing ALT-6 in win#1 moves win#6 to split window 1 +and sets it active, except if win#6 was already visible in some other +split window irssi just changes to that split window. This it the way +windows work with ircii, if you prefer it you can set it with

+ +
+     /SET autostick_split_windows OFF
+
+ +

Each window can have multiple channels, queries and other "window +items" inside them. If you don't like windows at all, you disable +automatic creating of them with

+ +
+     /SET autocreate_windows OFF
+
+ +

If you want to group only some channels or queries in one window, +use

+ +
+     /JOIN -window #channel
+     /QUERY -window nick
+
+ +

3. Server and channel automation

+ +

Irssi's multiple IRC network support is IMHO very good - at least +compared to other clients :) Even if you're only in one IRC network you +should group all your servers to be in the same IRC network as this +helps with reconnecting if your primary server breaks and is probably +useful in some other ways too :) For information how to actually use +irssi correctly with multiple servers see the chapter 6.

+ +

First you need to have your IRC network set, use /IRCNET command to +see if it's already there. If it isn't, use /IRCNET ADD yourircnet. To +make Irssi work properly with different IRC networks, you might need to +give some special settings to /IRCNET ADD, see manual.txt for more +information about them. Irssi defaults to IRCNet's behaviour.

+ +

After that you need to add your servers. For example:

+ +
+     /SERVER ADD -auto -ircnet ircnet irc.kpnqwest.fi 6667
+     /SERVER ADD -auto -ircnet worknet irc.mycompany.com 6667 password
+
+ +

The -auto option specifies that this server is automatically connected +at startup. You don't need to make more than one server with -auto +option to one IRC network, other servers are automatically connected in +same network if the -auto server fails.

+ +

And finally channels:

+ +
+     /CHANNEL ADD -auto -bots *!*@bot@host.org -botcmd "/^msg $0 op pass"
+		  #irssi efnet
+     /CHANNEL ADD -auto #secret ircnet password
+
+ +

-bots and -botcmd should be the only ones needing a bit of +explaining. They're used to send commands automatically to bot when +channel is joined, usually to get ops automatically. You can specify +multiple bot masks with -bots option separated with spaces (and +remember to quote the string then). The $0 in -botcmd specifies the +first found bot in the list. If you don't need the bot masks (ie. the +bot is always with the same nick, like chanserv) you can give only the +-botcmd option and the command is always sent.

+ + +

4. Setting up windows and automatically restoring them at startup

+ +

First connect to all the servers, join the channels and create the +queries you want. If you want to move the windows or channels around +use commands:

+ +
+     /WINDOW MOVE LEFT/RIGHT/number    - move window elsewhere
+     /WINDOW ITEM MOVE <number>|<name> - move channel/query to another window
+
+ +

When everything looks the way you like, use /LAYOUT SAVE command +(and /SAVE, if you don't have autosaving enabled) and when you start +irssi next time, irssi remembers the positions of the channels, queries +and everything. This "remembering" doesn't mean that simply using +/LAYOUT SAVE would automatically make irssi reconnect to all servers +and join all channels, you'll need the /SERVER ADD -auto and /CHANNEL +ADD -auto commands to do that.

+ +

If you want to change the layout, you just rearrange the layout like +you want it and use /LAYOUT SAVE again. If you want to remove the +layout for some reason, use /LAYOUT RESET.

+ + +

5. Status and msgs windows & message levels

+ +

By default, all the "extra messages" go to status window. This means +pretty much all messages that don't clearly belong to some channel or +query. Some people like it, some don't. If you want to remove it, use

+ +
+     /SET use_status_window OFF
+
+ +

This doesn't have any effect until you restart irssi. If you want to +remove it immediately, just /WINDOW CLOSE it.

+ +

Another common window is "messages window", where all private +messages go. By default it's disabled and query windows are created +instead. To make all private messages go to msgs window, say:

+ +
+     /SET use_msgs_window ON
+     /SET autocreate_query_level DCCMSGS  (or if you don't want queries to
+					   dcc chats either, say NONE)
+
+ +

use_msgs_window either doesn't have any effect until restarting +irssi. To create it immediately say:

+ +
+     /WINDOW NEW HIDE     - create the window
+     /WINDOW NAME (msgs)  - name it to "(msgs)"
+     /WINDOW LEVEL MSGS   - make all private messages go to this window
+     /WINDOW MOVE 1       - move it to first window
+
+ +

Note that neither use_msgs_window nor use_status_window have any +effect at all if /LAYOUT SAVE has been used.

+ +

This brings us to message levels.. What are they? All messages that +irssi prints have one or more "message levels". Most common are PUBLIC +for public messages in channels, MSGS for private messages and CRAP for +all sorts of messages with no real classification. You can get a whole +list of levels with

+ +
+     /HELP levels
+
+ +

Status window has message level "ALL -MSGS", meaning that all messages, +except private messages, without more specific place go to status +window. The -MSGS is there so it doesn't conflict with messages +window.

+ + +

6. How support for multiple servers works in irssi

+ +

ircii and several other clients support multiple servers by placing +the connection into some window. IRSSI DOES NOT. There is no required +relationship between window and server. You can connect to 10 servers +and manage them all in just one window, or join channel in each one of +them to one sigle window if you really want to. That being said, here's +how you do connect to new server without closing the old connection:

+ +
+     /CONNECT irc.server.org
+
+ +

Instead of the /SERVER which disconnects the existing connection. To +see list of all active connections, use /SERVER without any parameters. +You should see a list of something like:

+ +
+     -!- IRCNet: irc.telia.fi:6667 (IRCNet)
+     -!- OPN: tolkien.openprojects.net:6667 (OPN)
+     -!- RECON-1: 192.168.0.1:6667 () (02:59 left before reconnecting)
+
+ +

Here you see that we're connected to IRCNet and OPN networks. The +the IRCNet at the beginning is called the "server tag" while the +(IRCnet) at the end shows the IRC network. Server tag specifies unique +tag to refer to the server, usually it's the same as the IRC network. +When the IRC network isn't known it's some part of the server name. +When there's multiple connections to same IRC network or server, irssi +adds a number after the tag so there could be ircnet, ircnet2, ircnet3 +etc.

+ +

Server tags beginning with RECON- mean server reconnections. Above we +see that connection to server at 192.168.0.1 wasn't successful and +irssi will try to connect it again in 3 minutes.

+ +

To disconnect one of the servers, or to stop irssi from +reconnecting, use

+ +
+     /DISCONNECT ircnet   - disconnect server with tag "ircnet"
+     /DISCONNECT recon-1  - stop trying to reconnect to RECON-1 server
+     /RMRECONNS           - stop all server reconnections
+
+     /RECONNECT recon-1   - immediately try reconnecting back to RECON-1
+     /RECONNECT ALL       - immediately try reconnecting back to all
+			    servers in reconnection queue
+
+ +

Now that you're connected to all your servers, you'll have to know how +to specify which one of them you want to use. One way is to have an +empty window, like status or msgs window. In it, you can specify which +server to set active with

+ +
+     /WINDOW SERVER tag    - set server "tag" active
+     Ctrl-X                - set the next server in list active
+
+ +

When the server is active, you can use it normally. When there's +multiple connected servers, irssi adds [servertag] prefix to all +messages in non-channel/query messages so you'll know where it came +from.

+ +

Several commands also accept -servertag option to specify which server +it should use:

+ +
+     /MSG -tag nick message
+     /JOIN -tag #channel
+     /QUERY -tag nick
+
+ +

/MSG tab completion also automatically adds the -tag option when +nick isn't in active server.

+ +

Window's server can be made sticky. When sticky, it will never +automatically change to anything else, and if server gets disconnected, the +window won't have any active server. When the server gets connected again, +it is automatically set active in the window. To set the window's server +sticky use

+ +
+     /WINDOW SERVER -sticky tag
+
+ +

This is useful if you wish to have multiple status or msgs windows, one +for each server. Here's how to do them (repeat for each server)

+ +
+     /WINDOW NEW HIDE
+     /WINDOW NAME (status)
+     /WINDOW LEVEL ALL -MSGS
+     /WINDOW SERVER -sticky ircnet
+
+     /WINDOW NEW HIDE
+     /WINDOW NAME (msgs)
+     /WINDOW LEVEL MSGS
+     /WINDOW SERVER -sticky ircnet
+
+ +

7. /LASTLOG and jumping around in scrollback

+ +

/LASTLOG command can be used for searching texts in scrollback +buffer. Simplest usages are

+ +
+     /LASTLOG word     - print all lines with "word" in them
+     /LASTLOG word 10  - print last 10 occurances of "word"
+     /LASTLOG -topics  - print all topic changes
+
+ +

If there's more lines to be printed than 1000, irssi doesn't thinks +that you probably made some mistake and won't print them without -force +option. If you want to save the full lastlog to file, use

+ +
+     /LASTLOG -file ~/irc.log
+
+ +

With -file option you don't need -force even if there's more than 1000 +lines. /LASTLOG has a lot of other options too, see /HELP lastlog for +details.

+ +

Once you've found the lines you were interested in, you might want +to check the discussion around them. Irssi has /SCROLLBACK (or alias +/SB) command for jumping around in scrollback buffer. Since /LASTLOG +prints the timestamp when the message was originally printed, you can +use /SB GOTO hh:mm to jump directly there. To get back to the bottom of +scrollback, use /SB END command.

+ + +

8. Logging

+ +

Irssi can automatically log important messages when you're set away +(/AWAY reason). When you set yourself unaway (/AWAY), the new messages +in away log are printed to screen. You can configure it with:

+ +
+     /SET awaylog_level MSGS HILIGHT     - Specifies what messages to log
+     /SET awaylog_file ~/.irssi/away.log - Specifies the file to use
+
+ +

Easiest way to start logging with Irssi is to use autologging. With it +Irssi logs all channels and private messages to specified directory. +You can turn it on with

+ +
+     /SET autolog ON
+
+ +

By default it logs pretty much everything execept CTCPS or CRAP +(/WHOIS requests, etc). You can specify the logging level yourself with

+ +
+     /SET autolog_level ALL -CRAP -CLIENTCRAP -CTCPS (this is the default)
+
+ +

By default irssi logs to ~/irclogs/<servertag>/<target>.log. +You can change this with

+ +
+     /SET autolog_path ~/irclogs/$tag/$0.log (this is the default)
+
+ +

The path is automatically created if it doesn't exist. $0 specifies +the target (channel/nick). You can make irssi automatically rotate the +logs by adding date/time formats to the file name. The formats are in +"man strftime" format. For example

+ +
+     /SET autolog_path = ~/irclogs/%Y/$tag/$0.%m-%d.log
+
+ +

For logging only some specific channels or nicks, see /HELP log

+ + +

9. Irssi's settings

+ +

You probably don't like Irssi's default settings. I don't like them. +But I'm still convinced that they're pretty good defaults. Here's some +of them you might want to change (the default value is shown):

+ +

Queries

+ +
+
/SET autocreate_own_query ON
+
Should new query window be created when you send message to someone + (with /msg).
+ +
/SET autocreate_query_level MSGS
+
New query window should be created when receiving messages with + this level. MSGS, DCCMSGS and NOTICES levels work currently. You can + disable this with /SET -clear autocrate_query_level.
+ +
/SET autoclose_query 0
+
Query windows can be automatically closed after certain time of + inactivity. Queries with unread messages aren't closed and active + window is neither never closed. The value is given in seconds.
+
+ +

Windows

+ +
+
/SET use_msgs_window OFF
+
Create messages window at startup. All private messages go to this + window. This only makes sense if you've disabled automatic query + windows. Message window can also be created manually with /WINDOW LEVEL + MSGS, /WINDOW NAME (msgs).
+ +
/SET use_status_window ON
+
Create status window at startup. All messages that don't really + have better place go here, like all /WHOIS replies etc. Status window + can also be created manually with /WINDOW LEVEL ALL -MSGS, /WINDOW NAME + (status).
+ +
/SET autocreate_windows ON
+
Should we create new windows for new window items or just place + everything in one window
+ +
/SET autoclose_windows ON
+
Should window be automatically closed when the last item in them is + removed (ie. /PART, /UNQUERY).
+ +
/SET reuse_unused_windows OFF
+
When finding where to place new window item (channel, query) Irssi + first tries to use already existing empty windows. If this is set ON, + new window will always be created for all window items. This setting is + ignored if autoclose_windows is set ON.
+ +
/SET window_auto_change OFF
+
Should Irssi automatically change to automatically created windows + - usually queries when someone sends you a message. To prevent + accidentally sending text meant to some other channel/nick, Irssi + clears the input buffer when changing the window. The text is still in + scrollback buffer, you can get it back with pressing arrow up key.
+ +
/SET print_active_channel OFF
+
When you keep more than one channel in same window, Irssi prints + the messages coming to active channel as "<nick> text" + and other channels as "<nick:channel> text". If this + setting is set ON, the messages to active channels are also printed in + the latter way.
+ +
/SET window_history OFF
+
Should command history be kept separate for each window.
+
+ + +

User information

+ +
+
/SET nick
+
Your nick name
+ +
/SET alternate_nick
+
Your alternate nick.
+ +
/SET user_name
+
Your username, if you have ident enabled this doesn't affect + anything
+ +
/SET real_name
+
Your real name.
+
+ + +

Server information

+ +
+
/SET skip_motd OFF
+
Should we hide server's MOTD (Message Of The Day).
+ +
/SET server_reconnect_time 300
+
Seconds to wait before connecting to same server again. Don't set + this too low since it usually doesn't help at all - if the host is + down, the few extra minutes of waiting won't hurt much.
+ +
/SET lag_max_before_disconnect 300
+
Maximum server lag in seconds before disconnecting and trying to + reconnect. This happens mostly only when network breaks between you and + IRC server.
+
+ + +

Appearance

+ +
+
/SET timestamps ON
+
Show timestamps before each message.
+ +
/SET hide_text_style OFF
+
Hide all bolds, underlines, MIRC colors, etc.
+ +
/SET show_nickmode ON
+
Show the nick's mode before nick in channels, ie. ops have + <@nick>, voices <+nick> and others < nick>
+ +
/SET show_quit_once OFF
+
Show quit message only once in some of the channel windows the + nick was in instead of in all windows.
+ +
/SET topicbar ON
+
Show the channel's topic in top of screen.
+ +
/SET lag_min_show 100
+
Show the server lag in status bar if it's bigger than this, the + unit is 1/100 of seconds (ie. the default value of 100 = 1 second).
+ +
/SET indent 10
+
When lines are longer than screen width they have to be split to + multiple lines. This specifies how much space to put at the beginning + of the line before the text begins. This can be overridden in text + formats with %| format.
+ +
/SET activity_hide_targets
+
If you don't want to see window activity in some certain channels + or queries, list them here. For example "#boringchannel =bot1 =bot2". + If any highlighted text or message for you appears in that window, this + setting is ignored and the activity is shown.
+ +
/SET mail_counter ON
+
Show the number of mails in your mbox in status + bar. The mbox file is taken from $MAIL environment setting. Only mbox + format works for now.
+ + +

Nick completion

+ +
+
/SET completion_auto OFF
+
Automatically complete the nick if line begins with start of nick + and the completion character. Learn to use the tab-completion instead, + it's a lot better ;)
+ +
/SET completion_char :
+
Completion character to use.
+
diff --git a/apps/irssi/file2header.sh b/apps/irssi/file2header.sh new file mode 100755 index 00000000..f25ae13b --- /dev/null +++ b/apps/irssi/file2header.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +echo "const char *$2 =" +cat $1|sed 's/\\/\\\\/g'|sed 's/"/\\"/g'|sed 's/^/\"/'|sed 's/$/\\n\"/' +echo ";" diff --git a/apps/irssi/findsyntax.pl b/apps/irssi/findsyntax.pl new file mode 100755 index 00000000..e24d50f0 --- /dev/null +++ b/apps/irssi/findsyntax.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl -w + +while(<>) { + if(m!/\*.SYNTAX\:! || $tt) { + s/^\s+/ /; + if (/^ [A-Z]+/) { + print "\n"; + s/^ //; + } + if (m!\*/!) { + $tt=0; + } else { + $tt=1; + chomp; + } + print; + } +} diff --git a/apps/irssi/irssi-config.in b/apps/irssi/irssi-config.in new file mode 100644 index 00000000..0a410425 --- /dev/null +++ b/apps/irssi/irssi-config.in @@ -0,0 +1,6 @@ +PROG_LIBS="@PROG_LIBS@" +PERL_LDFLAGS="@PERL_LDFLAGS@" +COMMON_LIBS="@COMMON_LIBS@" + +CHAT_MODULES="@CHAT_MODULES@" +silc_MODULES="@silc_MODULES@" diff --git a/apps/irssi/irssi-version.h.in b/apps/irssi/irssi-version.h.in new file mode 100644 index 00000000..668993fc --- /dev/null +++ b/apps/irssi/irssi-version.h.in @@ -0,0 +1,3 @@ +/* automatically created by autogen.sh */ +#define IRSSI_VERSION "@VERSION@" +#define IRSSI_VERSION_DATE "20010524" diff --git a/apps/irssi/irssi.spec.in b/apps/irssi/irssi.spec.in new file mode 100644 index 00000000..a3e9b9c0 --- /dev/null +++ b/apps/irssi/irssi.spec.in @@ -0,0 +1,216 @@ +# $Revision$, $Date$ +Name: irssi +Version: @VERSION@ +Release: 1 +Vendor: Timo Sirainen +Summary: Irssi is a IRC client +Summary(pl): Irssi - klient IRC +Copyright: GPL +Group: Applications/Communications +Group(pl): Aplikacje/Komunikacja +URL: http://irssi.org/ +Source0: http://irssi.org/irssi/files/%{name}-%{version}.tar.gz +BuildRequires: glib-devel +BuildRequires: ncurses-devel +BuildRequires: imlib-devel +BuildRequires: gtk+-devel +BuildRequires: gnome-libs-devel +BuildRequires: XFree86-devel +BuildRoot: /tmp/%{name}-%{version}-root + +%define _sysconfdir /etc +%define configure { CFLAGS="${CFLAGS:-%optflags}" ; export CFLAGS ; CXXFLAGS="${CXXFLAGS:-%optflags}" ; export CXXFLAGS ; FFLAGS="${FFLAGS:-%optflags}" ; export FFLAGS ; ./configure %{_target_platform} --prefix=%{_prefix} --exec-prefix=%{_exec_prefix} --bindir=%{_bindir} --sbindir=%{_sbindir} --sysconfdir=%{_sysconfdir} --datadir=%{_datadir} --includedir=%{_includedir} --libdir=%{_libdir} --libexecdir=%{_libexecdir} --localstatedir=%{_localstatedir} --sharedstatedir=%{_sharedstatedir} --mandir=%{_mandir} --infodir=%{_infodir} } + +%description +Irssi is a textUI IRC client with IPv6 support +by Timo Sirainen . +More information can be found at http://irssi.org/. + +%description -l pl +Irssi jest tekstowym klientem IRC ze wsparciem dla IPv6. +Napisany zosta³ przez Timo Strainen +Wiêcej informacji mo¿na znale¶æ pod adresem +http://irssi.org/ + +%package GNOME +Summary: GNOME version of irssi IRC client +Summary(pl): Wersja dla ¶rodowiska GNOME klienta IRC irssi +Group: X11/Applications/Communications +Group(pl): X11/Aplikacje/Komunikacja +Requires: %{name} = %{version} + +%description GNOME +Irssi is a GTK based (with GNOME) GUI IRC client with IPv6 support +by Timo Sirainen . +More information can be found at http://irssi.org/. + +%description GNOME -l pl +Irssi jest graficznym klientem IRC ze wsparciem dla IPv6 pracuj±cym +w ¶rodowisku GNOME. Napisany zosta³ przez Timo Sirainen . +Wiêcej informacji mo¿na znale¶æ pod adresem +http://irssi.org/ + +%prep +%setup -q + +%build +CPPFLAGS="-I/usr/X11R6/include"; export CPPFLAGS +LDFLAGS="-s -L/usr/X11R6/lib"; export LDFLAGS +%configure \ + --with-gnome \ + --with-gnome-panel \ + --with-imlib \ + --enable-ipv6 \ + --with-textui=ncurses \ + --without-socks \ + --with-plugins +make + +%install +rm -rf $RPM_BUILD_ROOT +make DESTDIR=$RPM_BUILD_ROOT install +strip --strip-unneeded $RPM_BUILD_ROOT%{_libdir}/irssi/plugins/lib*.so.*.* + +gzip -9fn AUTHORS ChangeLog README TODO NEWS + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr (644,root,root,755) +%doc {AUTHORS,ChangeLog,README,TODO,NEWS}.gz + +%attr(755,root,root) %{_bindir}/irssi +%attr(755,root,root) %{_bindir}/irssi-text +%attr(755,root,root) %{_bindir}/irssi-bot + +%dir %{_sysconfdir}/irssi +%config(noreplace) %verify(not size mtime md5) %{_sysconfdir}/irssi/* + +%dir %{_libdir}/irssi +%dir %{_libdir}/irssi/plugins +%attr(755,root,root) %{_libdir}/irssi/plugins/lib*.so.*.* +%attr(755,root,root) %{_libdir}/irssi/plugins/lib*.so +#%attr(755,root,root) %{_libdir}/irssi/plugins/lib*.la +#%attr(755,root,root) %{_libdir}/irssi/plugins/lib*.a + +%files GNOME +%defattr (644,root,root,755) +%attr(755,root,root) %{_bindir}/irssi + +%{_sysconfdir}/CORBA/servers/irssi.gnorba +%{_datadir}/gnome/apps/Network/irssi.desktop +%{_datadir}/gnome/help/irssi +%{_datadir}/pixmaps/* + +%define date %(echo `LC_ALL="C" date +"%a %b %d %Y"`) +%changelog +* %{date} PLD Team +All below listed persons can be reached on @pld.org.pl + +$Log$ +Revision 1.1.1.1 2001/05/24 12:09:28 priikone + imported irssi. + +Revision 1.11 2001/03/29 14:38:28 cras +http://irssi.org -> http://irssi.org/ + +Revision 1.10 2000/06/02 01:55:01 cras +Changed irssi's url to http://irssi.org/ + +Revision 1.9 2000/04/17 09:37:05 kloczek +- added pixmaps to %files (irssi have now own icon). + +Revision 1.8 2000/04/14 11:27:02 cras +Sorry for a big update - I still don't have internet connection at home +and this is what I've been doing a few weeks now.. :) You really shouldn't +upgrade to this version without keeping a backup of the working one, since +this will break everything and at least notify list is broken - probably +something else too. + +* On the way to 0.8.0 .. Major rewriting/rearranging code. There's + some changes in behaviour because I'm trying to make Irssi a bit + more compatible with EPIC. + +* libPropList isn't needed anymore - I'm using my own configuration + library. This is mostly because different proplists worked a bit + differently everywhere and several people had problems with it. + It's also yet another extra library that you needed to compile + Irssi. New configuration library has several advantages: + + You can add comments to configuration file and they also stay + there when it's saved. + + It's not nearly as vulnerable as proplist. If some error occurs, + instead of just not reading anything it will try to continue if + possible. Also the error messages are written to irssi's text + window instead of stdout. + + It can be managed more easily than proplist - setting/getting the + configuration is a lot more easier. + +* Coding style changes - I'm not using gint, gchar etc. anymore, + they're just extra pain when moving code to non-glib projects and + syntax hilighting doesn't work by default with most editors ;) + + Indentation style was also changed to K&R because of some political + reasons ;) And I'm already starting to like it.. :) It forces me + to split code to different functions more often and the result is + that the code gets more readable. + + And finally I'm also using nst' all over the place. + ++ /EVAL - Expand all the special variables from string and + run it. Commands can be split with ; character. See + docs/SPECIAL_VARS for more info. ++ Aliases are parsed just like /EVAL - arguments are in $0..$9. ++ Text formats are also parsed like /EVAL, arguments used to be in + $1..$9, now they're in $0..$8 so it messes up existing themes.. ++ /SET [key [value]] - no more the '=' character. Boolean values + also need to be changed with ON/OFF/TOGGLE values (not yes/no). + Settings aren't saved to disk until you use /SAVE. ++ /TOGGLE [ON/OFF] - same as /SET TOGGLE + +Revision 1.7 2000/02/25 17:03:15 cras +Irssi 0.7.27 released. + +Revision 1.6 2000/01/27 19:03:28 cras +fixes by vkoivula@saunalahti.fi + +Revision 1.6 2000/01/25 00:00:00 vkoivula@saunalahti.fi +- requires libProbList-devel changed to libPropList (problist.h is actually in + this package) +- fixed filelist + +Revision 1.5 2000/01/13 02:13:20 kloczek +- irssi.desktop now this is not applet but application description file - + place them in $(datadir)/gnome/apps/Network. + +Revision 1.4 1999/10/16 13:05:25 wiget +- polish translation + +Revision 1.3 1999/09/13 16:50:25 wiget +- fixed %%configure macro + +Revision 1.2 1999/09/04 11:42:33 wiget +- new way to update Version: field in spec +- new target for make 'make rpm' + +Revision 1.4 1999/09/03 09:36:24 wiget +- updated to 0.7.16alpha-1 + +Revision 1.3 1999/09/02 17:27:36 wiget +- added BuildRequires rules + +Revision 1.2 1999/09/02 17:22:51 wiget +- rewrite to PLD style coding: +-- correct Group and Group(pl) +-- %%changelog moved to end +-- splited to irssi and irssi-GNOME +-- added patch to allow 'make install DESTDIR=/some/dir' +-- added ./configure parameters +-- striped unneeded symbol from plugins +-- gziped docs +-- corrected %%files section + +- based at spec from tarball (by JT Traub ) diff --git a/apps/irssi/src/Makefile.am b/apps/irssi/src/Makefile.am new file mode 100644 index 00000000..d8fc1899 --- /dev/null +++ b/apps/irssi/src/Makefile.am @@ -0,0 +1,8 @@ +if BUILD_TEXTUI +TEXTUI=fe-text +endif + +noinst_HEADERS = \ + common.h + +SUBDIRS = lib-popt lib-config core silc fe-common $(TEXTUI) diff --git a/apps/irssi/src/common.h b/apps/irssi/src/common.h new file mode 100644 index 00000000..c79c5d9b --- /dev/null +++ b/apps/irssi/src/common.h @@ -0,0 +1,89 @@ +#ifndef __COMMON_H +#define __COMMON_H + +#define IRSSI_AUTHOR "Timo Sirainen " +#define IRSSI_WEBSITE "http://irssi.org/" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +# ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDLIB_H +# include +#endif +#include +#include + +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif +#include + +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_DIRENT_H +# include +#endif +#include +#ifdef WIN32 +# include +#endif + +#include +#ifdef HAVE_GMODULE +# include +#endif + +#include "core/memdebug.h" + +#define g_free_not_null(a) \ + G_STMT_START { \ + if (a) g_free(a); \ + } G_STMT_END + +#define g_free_and_null(a) \ + G_STMT_START { \ + if (a) { g_free(a); (a) = NULL; } \ + } G_STMT_END + +#define G_INPUT_READ (1 << 0) +#define G_INPUT_WRITE (1 << 1) + +typedef void (*GInputFunction) (void *data, GIOChannel *source, int condition); + +int g_input_add(GIOChannel *source, int condition, + GInputFunction function, void *data); +int g_input_add_full(GIOChannel *source, int priority, int condition, + GInputFunction function, void *data); + +#define MAX_INT_STRLEN ((sizeof(int) * CHAR_BIT + 2) / 3 + 1) + +typedef struct _IPADDR IPADDR; +typedef struct _CONFIG_REC CONFIG_REC; +typedef struct _CONFIG_NODE CONFIG_NODE; + +typedef struct _LINEBUF_REC LINEBUF_REC; +typedef struct _NET_SENDBUF_REC NET_SENDBUF_REC; +typedef struct _RAWLOG_REC RAWLOG_REC; + +typedef struct _CHATNET_REC CHATNET_REC; +typedef struct _SERVER_REC SERVER_REC; +typedef struct _WI_ITEM_REC WI_ITEM_REC; +typedef struct _CHANNEL_REC CHANNEL_REC; +typedef struct _QUERY_REC QUERY_REC; +typedef struct _NICK_REC NICK_REC; + +typedef struct _SERVER_CONNECT_REC SERVER_CONNECT_REC; +typedef struct _SERVER_SETUP_REC SERVER_SETUP_REC; +typedef struct _CHANNEL_SETUP_REC CHANNEL_SETUP_REC; + +#endif diff --git a/apps/irssi/src/fe-common/Makefile.am b/apps/irssi/src/fe-common/Makefile.am new file mode 100644 index 00000000..de959118 --- /dev/null +++ b/apps/irssi/src/fe-common/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = core silc diff --git a/apps/irssi/src/fe-common/silc/Makefile.am b/apps/irssi/src/fe-common/silc/Makefile.am new file mode 100644 index 00000000..74efdc41 --- /dev/null +++ b/apps/irssi/src/fe-common/silc/Makefile.am @@ -0,0 +1,33 @@ +INCLUDES = $(GLIB_CFLAGS) -I$(IRSSI_INCLUDE) -I$(IRSSI_INCLUDE)/src + +SILC_INCLUDE=../../../.. +IRSSI_INCLUDE=../../.. + +INCLUDES = \ + $(GLIB_CFLAGS) \ + -DSYSCONFDIR=\""$(sysconfdir)"\" \ + -I$(IRSSI_INCLUDE) -I$(IRSSI_INCLUDE)/src \ + -I$(IRSSI_INCLUDE)/src/core \ + -I$(IRSSI_INCLUDE)/src/silc \ + -I$(IRSSI_INCLUDE)/src/silc/core \ + -I$(SILC_INCLUDE)/includes \ + -I$(SILC_INCLUDE)/lib/silccore \ + -I$(SILC_INCLUDE)/lib/silccrypt \ + -I$(SILC_INCLUDE)/lib/silcmath \ + -I$(SILC_INCLUDE)/lib/silcske \ + -I$(SILC_INCLUDE)/lib/silcsim \ + -I$(SILC_INCLUDE)/lib/silcutil \ + -I$(SILC_INCLUDE)/lib/silcclient \ + -I$(SILC_INCLUDE)/lib/silcmath/gmp \ + -I$(SILC_INCLUDE)/lib/trq + +noinst_LIBRARIES = libfe_common_silc.a + +libfe_common_silc_a_SOURCES = \ + fe-channels.c \ + fe-common-silc.c \ + silc-modules.c + +noinst_HEADERS = \ + fe-common-silc.h \ + module.h diff --git a/apps/irssi/src/fe-common/silc/fe-channels.c b/apps/irssi/src/fe-common/silc/fe-channels.c new file mode 100644 index 00000000..47696cfc --- /dev/null +++ b/apps/irssi/src/fe-common/silc/fe-channels.c @@ -0,0 +1,29 @@ +/* + fe-channels.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" + +void fe_silc_channels_init(void) +{ +} + +void fe_silc_channels_deinit(void) +{ +} diff --git a/apps/irssi/src/fe-common/silc/fe-common-silc.c b/apps/irssi/src/fe-common/silc/fe-common-silc.c new file mode 100644 index 00000000..c779caee --- /dev/null +++ b/apps/irssi/src/fe-common/silc/fe-common-silc.c @@ -0,0 +1,45 @@ +/* + fe-common-silc.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" + +void fe_silc_channels_init(void); +void fe_silc_channels_deinit(void); + +void fe_silc_modules_init(void); +void fe_silc_modules_deinit(void); + +void fe_common_silc_init(void) +{ + fe_silc_channels_init(); + fe_silc_modules_init(); +} + +void fe_common_silc_deinit(void) +{ + fe_silc_modules_deinit(); + + fe_silc_channels_deinit(); +} + +void fe_common_silc_finish_init(void) +{ +} diff --git a/apps/irssi/src/fe-common/silc/fe-common-silc.h b/apps/irssi/src/fe-common/silc/fe-common-silc.h new file mode 100644 index 00000000..4a9a6483 --- /dev/null +++ b/apps/irssi/src/fe-common/silc/fe-common-silc.h @@ -0,0 +1,8 @@ +#ifndef __FE_COMMON_SILC_H +#define __FE_COMMON_SILC_H + +void fe_common_silc_init(void); +void fe_common_silc_deinit(void); +void fe_common_silc_finish_init(void); + +#endif diff --git a/apps/irssi/src/fe-common/silc/module-formats.c b/apps/irssi/src/fe-common/silc/module-formats.c new file mode 100644 index 00000000..2b30bffe --- /dev/null +++ b/apps/irssi/src/fe-common/silc/module-formats.c @@ -0,0 +1,211 @@ +/* + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "printtext.h" + +FORMAT_REC fecommon_irc_formats[] = { + { MODULE_NAME, "IRC", 0 }, + + /* ---- */ + { NULL, "Server", 0 }, + + { "lag_disconnected", "No PONG reply from server %_$0%_ in $1 seconds, disconnecting", 2, { 0, 1 } }, + { "disconnected", "Disconnected from %_$0%_ %K[%n$1%K]", 2, { 0, 0 } }, + { "server_list", "%_$0%_: $1:$2 ($3)", 5, { 0, 0, 1, 0, 0 } }, + { "server_lookup_list", "%_$0%_: $1:$2 ($3) (connecting...)", 5, { 0, 0, 1, 0, 0 } }, + { "server_reconnect_list", "%_$0%_: $1:$2 ($3) ($5 left before reconnecting)", 6, { 0, 0, 1, 0, 0, 0 } }, + { "server_reconnect_removed", "Removed reconnection to server %_$0%_ port %_$1%_", 3, { 0, 1, 0 } }, + { "server_reconnect_not_found", "Reconnection tag %_$0%_ not found", 1, { 0 } }, + { "query_server_changed", "Query with %_$2%_ changed to server %_$1%_", 3, { 0, 0, 0 } }, + { "setupserver_added", "Server $0 saved", 2, { 0, 1 } }, + { "setupserver_removed", "Server $0 removed", 2, { 0, 1 } }, + { "setupserver_not_found", "Server $0 not found", 2, { 0, 1 } }, + { "setupserver_header", "Server Port IRC Net Settings", 0 }, + { "setupserver_line", "%|$[!20]0 $[5]1 $[10]2 $3", 4, { 0, 1, 0, 0 } }, + { "setupserver_footer", "", 0 }, + { "netsplit", "%RNetsplit%n %_$0%_ %_$1%_ quits: $2", 3, { 0, 0, 0 } }, + { "netsplit_more", "%RNetsplit%n %_$0%_ %_$1%_ quits: $2 (+$3 more, use /NETSPLIT to show all of them)", 4, { 0, 0, 0, 1 } }, + { "netsplit_join", "%CNetsplit%n over, joins: $0", 1, { 0 } }, + { "netsplit_join_more", "%CNetsplit%n over, joins: $0 (+$1 more)", 2, { 0, 1 } }, + { "no_netsplits", "There are no net splits", 0 }, + { "netsplits_header", "Nick Channel Server Splitted server", 0 }, + { "netsplits_line", "$[9]0 $[10]1 $[20]2 $3", 4, { 0, 0, 0, 0 } }, + { "netsplits_footer", "", 0 }, + { "ircnet_added", "Ircnet $0 saved", 1, { 0 } }, + { "ircnet_removed", "Ircnet $0 removed", 1, { 0 } }, + { "ircnet_not_found", "Ircnet $0 not found", 1, { 0 } }, + { "ircnet_header", "Ircnets:", 0 }, + { "ircnet_line", "$0: $1", 2, { 0, 0 } }, + { "ircnet_footer", "", 0 }, + + /* ---- */ + { NULL, "Channels", 0 }, + + { "join", "%c%_$0%_ %K[%c$1%K]%n has joined %_$2", 3, { 0, 0, 0 } }, + { "part", "%c$0 %K[%n$1%K]%n has left %_$2%_ %K[%n$3%K]", 4, { 0, 0, 0, 0 } }, + { "joinerror_toomany", "Cannot join to channel %_$0%_ %K(%nYou have joined to too many channels%K)", 1, { 0 } }, + { "joinerror_full", "Cannot join to channel %_$0%_ %K(%nChannel is full%K)", 1, { 0 } }, + { "joinerror_invite", "Cannot join to channel %_$0%_ %K(%nYou must be invited%K)", 1, { 0 } }, + { "joinerror_banned", "Cannot join to channel %_$0%_ %K(%nYou are banned%K)", 1, { 0 } }, + { "joinerror_bad_key", "Cannot join to channel %_$0%_ %K(%nBad channel key%K)", 1, { 0 } }, + { "joinerror_bad_mask", "Cannot join to channel %_$0%_ %K(%nBad channel mask%K)", 1, { 0 } }, + { "joinerror_unavail", "Cannot join to channel %_$0%_ %K(%nChannel is temporarily unavailable%K)", 1, { 0 } }, + { "kick", "%c$0%n was kicked from %_$1%_ by %_$2%_ %K[%n$3%K]", 4, { 0, 0, 0, 0 } }, + { "quit", "%c$0 %K[%n$1%K]%n has quit IRC %K[%n$2%K]", 3, { 0, 0, 0 } }, + { "quit_once", "%_$3%_ %c$0 %K[%n$1%K]%n has quit IRC %K[%n$2%K]", 4, { 0, 0, 0, 0 } }, + { "invite", "%_$0%_ invites you to %_$1", 2, { 0, 0 } }, + { "inviting", "Inviting $0 to %_$1", 2, { 0, 0 } }, + { "not_invited", "You have not been invited to a channel!", 0 }, + { "names", "%K[%g%_Users%_%K(%g$0%K)]%n $1", 2, { 0, 0 } }, + { "names_nick", "%K[%n%_$0%_$1%K] ", 2, { 0, 0 } }, + { "endofnames", "%g%_$0%_%K:%n Total of %_$1%_ nicks %K[%n%_$2%_ ops, %_$3%_ voices, %_$4%_ normal%K]", 5, { 0, 1, 1, 1, 1 } }, + { "channel_created", "Channel %_$0%_ created $1", 2, { 0, 0 } }, + { "topic", "Topic for %c$0%K:%n $1", 2, { 0, 0 } }, + { "no_topic", "No topic set for %c$0", 1, { 0 } }, + { "new_topic", "%_$0%_ changed the topic of %c$1%n to%K:%n $2", 3, { 0, 0, 0 } }, + { "topic_unset", "Topic unset by %_$0%_ on %c$1", 2, { 0, 0 } }, + { "topic_info", "Topic set by %_$0%_ %K[%n$1%K]", 2, { 0, 0 } }, + { "chanmode_change", "mode/%c$0 %K[%n$1%K]%n by %_$2", 3, { 0, 0, 0 } }, + { "server_chanmode_change", "%RServerMode/%c$0 %K[%n$1%K]%n by %_$2", 3, { 0, 0, 0 } }, + { "channel_mode", "mode/%c$0 %K[%n$1%K]", 2, { 0, 0 } }, + { "bantype", "Ban type changed to %_$0", 1, { 0 } }, + { "no_bans", "No bans in channel %_$0%_", 1, { 0 } }, + { "banlist", "%_$0%_: ban %c$1", 2, { 0, 0 } }, + { "banlist_long", "%_$0%_: ban %c$1 %K[%nby %_$2%_, $3 secs ago%K]", 4, { 0, 0, 0, 1 } }, + { "ebanlist", "%_$0%_: ban exception %c$1", 2, { 0, 0 } }, + { "ebanlist_long", "%_$0%_: ban exception %c$1 %K[%nby %_$2%_, $3 secs ago%K]", 4, { 0, 0, 0, 1 } }, + { "invitelist", "%_$0%_: invite %c$1", 2, { 0, 0 } }, + { "no_such_channel", "$0: No such channel", 1, { 0 } }, + { "channel_synced", "Join to %_$0%_ was synced in %_$1%_ secs", 2, { 0, 2 } }, + { "not_in_channels", "You are not on any channels", 0 }, + { "current_channel", "Current channel $0", 1, { 0 } }, + { "chanlist_header", "You are on the following channels:", 0 }, + { "chanlist_line", "$[-10]0 %|+$1 ($2): $3", 4, { 0, 0, 0, 0 } }, + { "chansetup_not_found", "Channel $0 not found", 2, { 0, 0 } }, + { "chansetup_added", "Channel $0 saved", 2, { 0, 0 } }, + { "chansetup_removed", "Channel $0 removed", 2, { 0, 0 } }, + { "chansetup_header", "Channel IRC net Password Settings", 0 }, + { "chansetup_line", "$[15]0 %|$[10]1 $[10]2 $3", 4, { 0, 0, 0, 0 } }, + { "chansetup_footer", "", 0 }, + + /* ---- */ + { NULL, "Nick", 0 }, + + { "usermode_change", "Mode change %K[%n%_$0%_%K]%n for user %c$1", 2, { 0, 0 } }, + { "user_mode", "Your user mode is %K[%n%_$0%_%K]", 1, { 0 } }, + { "away", "You have been marked as being away", 0 }, + { "unaway", "You are no longer marked as being away", 0 }, + { "nick_away", "$0 is away: $1", 2, { 0, 0 } }, + { "no_such_nick", "$0: No such nick/channel", 1, { 0 } }, + { "your_nick", "Your nickname is $0", 1, { 0 } }, + { "your_nick_changed", "You're now known as %c$0", 1, { 0 } }, + { "nick_changed", "%_$0%_ is now known as %c$1", 2, { 0, 0 } }, + { "nick_in_use", "Nick %_$0%_ is already in use", 1, { 0 } }, + { "nick_unavailable", "Nick %_$0%_ is temporarily unavailable", 1, { 0 } }, + { "your_nick_owned", "Your nick is owned by %_$3%_ %K[%n$1@$2%K]", 4, { 0, 0, 0, 0 } }, + + /* ---- */ + { NULL, "Who queries", 0 }, + + { "whois", "%_$0%_ %K[%n$1@$2%K]%n%: ircname : $3", 4, { 0, 0, 0, 0 } }, + { "whowas", "%_$0%_ %K[%n$1@$2%K]%n%: ircname : $3", 4, { 0, 0, 0, 0 } }, + { "whois_idle", " idle : $1 days $2 hours $3 mins $4 secs", 5, { 0, 1, 1, 1, 1 } }, + { "whois_idle_signon", " idle : $1 days $2 hours $3 mins $4 secs %K[%nsignon: $5%K]", 6, { 0, 1, 1, 1, 1, 0 } }, + { "whois_server", " server : $1 %K[%n$2%K]", 3, { 0, 0, 0 } }, + { "whois_oper", " : %_IRC operator%_", 1, { 0 } }, + { "whois_registered", " : has registered this nick", 1, { 0 } }, + { "whois_channels", " channels : $1", 2, { 0, 0 } }, + { "whois_away", " away : $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", "$[-10]0 %|%_$[!9]1%_ $[!3]2 $[!2]3 $4@$5 %K(%W$6%K)", 7, { 0, 0, 0, 0, 0, 0, 0 } }, + { "end_of_who", "End of /WHO list", 1, { 0 } }, + + /* ---- */ + { NULL, "Your messages", 0 }, + + { "own_msg", "%K<%n$2%W$0%K>%n %|$1", 3, { 0, 0, 0 } }, + { "own_msg_channel", "%K<%n$3%W$0%K:%c$1%K>%n %|$2", 4, { 0, 0, 0, 0 } }, + { "own_msg_private", "%K[%rmsg%K(%R$0%K)]%n $1", 2, { 0, 0 } }, + { "own_msg_private_query", "%K<%W$2%K>%n %|$1", 3, { 0, 0, 0 } }, + { "own_notice", "%K[%rnotice%K(%R$0%K)]%n $1", 2, { 0, 0 } }, + { "own_me", "%W * $0%n $1", 2, { 0, 0 } }, + { "own_ctcp", "%K[%rctcp%K(%R$0%K)]%n $1 $2", 3, { 0, 0, 0 } }, + { "own_wall", "%K[%WWall%K/%c$0%K]%n $1", 2, { 0, 0 } }, + + /* ---- */ + { NULL, "Received messages", 0 }, + + { "pubmsg_me", "%K<%n$2%Y$0%K>%n %|$1", 3, { 0, 0, 0 } }, + { "pubmsg_me_channel", "%K<%n$3%Y$0%K:%c$1%K>%n %|$2", 4, { 0, 0, 0, 0 } }, + { "pubmsg_hilight", "%K<%n$3$0$1%K>%n %|$2", 4, { 0, 0, 0, 0 } }, + { "pubmsg_hilight_channel", "%K<%n$4$0$1%K:%c$2%K>%n %|$3", 5, { 0, 0, 0, 0, 0 } }, + { "pubmsg", "%K<%n$2$0%K>%n %|$1", 3, { 0, 0, 0 } }, + { "pubmsg_channel", "%K<%n$3$0%K:%c$1%K>%n %|$2", 4, { 0, 0, 0, 0 } }, + { "msg_private", "%K[%R$0%K(%r$1%K)]%n $2", 3, { 0, 0, 0 } }, + { "msg_private_query", "%K<%R$0%K>%n %|$2", 3, { 0, 0, 0 } }, + { "notice_server", "%g!$0%n $1", 2, { 0, 0 } }, + { "notice_public", "%K-%M$0%K:%m$1%K-%n $2", 3, { 0, 0, 0 } }, + { "notice_public_ops", "%K-%M$0%K:%m@$1%K-%n $2", 3, { 0, 0, 0 } }, + { "notice_private", "%K-%M$0%K(%m$1%K)-%n $2", 3, { 0, 0, 0 } }, + { "action_private", "%W (*) $0%n $2", 3, { 0, 0, 0 } }, + { "action_private_query", "%W * $0%n $2", 3, { 0, 0, 0 } }, + { "action_public", "%W * $0%n $1", 2, { 0, 0 } }, + { "action_public_channel", "%W * $0%K:%c$1%n $2", 3, { 0, 0, 0 } }, + + /* ---- */ + { NULL, "CTCPs", 0 }, + + { "ctcp_reply", "CTCP %_$0%_ reply from %_$1%_%K:%n $2", 3, { 0, 0, 0 } }, + { "ctcp_reply_channel", "CTCP %_$0%_ reply from %_$1%_ in channel %_$3%_%K:%n $2", 4, { 0, 0, 0, 0 } }, + { "ctcp_ping_reply", "CTCP %_PING%_ reply from %_$0%_: $1.$2 seconds", 3, { 0, 2, 2 } }, + { "ctcp_requested", "%g>>> %_$0%_ %K[%g$1%K] %grequested %_$2%_ from %_$3", 4, { 0, 0, 0, 0 } }, + + /* ---- */ + { NULL, "Other server events", 0 }, + + { "online", "Users online: %_$0", 1, { 0 } }, + { "pong", "PONG received from $0: $1", 2, { 0, 0 } }, + { "wallops", "%WWALLOP%n $0: $1", 2, { 0, 0 } }, + { "action_wallops", "%WWALLOP * $0%n $1", 2, { 0, 0 } }, + { "error", "%_ERROR%_ $0", 1, { 0 } }, + { "unknown_mode", "Unknown mode character $0", 1, { 0 } }, + { "not_chanop", "You're not channel operator in $0", 1, { 0 } }, + + /* ---- */ + { NULL, "Misc", 0 }, + + { "ignored", "Ignoring %_$1%_ from %_$0%_", 2, { 0, 0 } }, + { "unignored", "Unignored %_$0%_", 1, { 0 } }, + { "ignore_not_found", "%_$0%_ is not being ignored", 1, { 0 } }, + { "ignore_no_ignores", "There are no ignores", 0 }, + { "ignore_header", "Ignorance List:", 0 }, + { "ignore_line", "$[-4]0 $1: $2 $3 $4", 4, { 1, 0, 0, 0 } }, + { "ignore_footer", "", 0 }, + { "talking_in", "You are now talking in %_$0%_", 1, { 0 } }, + { "query_start", "Starting query with %_$0%_", 1, { 0 } }, + { "no_query", "No query with %_$0%_", 1, { 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, NULL, 0 } +}; diff --git a/apps/irssi/src/fe-common/silc/module-formats.h b/apps/irssi/src/fe-common/silc/module-formats.h new file mode 100644 index 00000000..aa4c87c3 --- /dev/null +++ b/apps/irssi/src/fe-common/silc/module-formats.h @@ -0,0 +1,181 @@ +#include "printtext.h" + +enum { + IRCTXT_MODULE_NAME, + + IRCTXT_FILL_1, + + IRCTXT_LAG_DISCONNECTED, + IRCTXT_DISCONNECTED, + IRCTXT_SERVER_LIST, + IRCTXT_SERVER_LOOKUP_LIST, + IRCTXT_SERVER_RECONNECT_LIST, + IRCTXT_RECONNECT_REMOVED, + IRCTXT_RECONNECT_NOT_FOUND, + IRCTXT_QUERY_SERVER_CHANGED, + IRCTXT_SETUPSERVER_ADDED, + IRCTXT_SETUPSERVER_REMOVED, + IRCTXT_SETUPSERVER_NOT_FOUND, + IRCTXT_SETUPSERVER_HEADER, + IRCTXT_SETUPSERVER_LINE, + IRCTXT_SETUPSERVER_FOOTER, + 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_IRCNET_ADDED, + IRCTXT_IRCNET_REMOVED, + IRCTXT_IRCNET_NOT_FOUND, + IRCTXT_IRCNET_HEADER, + IRCTXT_IRCNET_LINE, + IRCTXT_IRCNET_FOOTER, + + IRCTXT_FILL_2, + + IRCTXT_JOIN, + IRCTXT_PART, + IRCTXT_JOINERROR_TOOMANY, + IRCTXT_JOINERROR_FULL, + IRCTXT_JOINERROR_INVITE, + IRCTXT_JOINERROR_BANNED, + IRCTXT_JOINERROR_BAD_KEY, + IRCTXT_JOINERROR_BAD_MASK, + IRCTXT_JOINERROR_UNAVAIL, + IRCTXT_KICK, + IRCTXT_QUIT, + IRCTXT_QUIT_ONCE, + IRCTXT_INVITE, + IRCTXT_INVITING, + IRCTXT_NOT_INVITED, + IRCTXT_NAMES, + IRCTXT_NAMES_NICK, + IRCTXT_ENDOFNAMES, + IRCTXT_CHANNEL_CREATED, + IRCTXT_TOPIC, + IRCTXT_NO_TOPIC, + IRCTXT_NEW_TOPIC, + IRCTXT_TOPIC_UNSET, + 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_INVITELIST, + IRCTXT_NO_SUCH_CHANNEL, + IRCTXT_CHANNEL_SYNCED, + IRCTXT_NOT_IN_CHANNELS, + IRCTXT_CURRENT_CHANNEL, + IRCTXT_CHANLIST_HEADER, + IRCTXT_CHANLIST_LINE, + IRCTXT_CHANSETUP_NOT_FOUND, + IRCTXT_CHANSETUP_ADDED, + IRCTXT_CHANSETUP_REMOVED, + IRCTXT_CHANSETUP_HEADER, + IRCTXT_CHANSETUP_LINE, + IRCTXT_CHANSETUP_FOOTER, + + IRCTXT_FILL_4, + + IRCTXT_USERMODE_CHANGE, + IRCTXT_USER_MODE, + IRCTXT_AWAY, + IRCTXT_UNAWAY, + IRCTXT_NICK_AWAY, + IRCTXT_NO_SUCH_NICK, + IRCTXT_YOUR_NICK, + IRCTXT_YOUR_NICK_CHANGED, + IRCTXT_NICK_CHANGED, + 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_REGISTERED, + IRCTXT_WHOIS_CHANNELS, + IRCTXT_WHOIS_AWAY, + IRCTXT_END_OF_WHOIS, + IRCTXT_END_OF_WHOWAS, + IRCTXT_WHOIS_NOT_FOUND, + IRCTXT_WHO, + IRCTXT_END_OF_WHO, + + IRCTXT_FILL_6, + + IRCTXT_OWN_MSG, + IRCTXT_OWN_MSG_CHANNEL, + IRCTXT_OWN_MSG_PRIVATE, + IRCTXT_OWN_MSG_PRIVATE_QUERY, + IRCTXT_OWN_NOTICE, + IRCTXT_OWN_ME, + IRCTXT_OWN_CTCP, + IRCTXT_OWN_WALL, + + IRCTXT_FILL_7, + + IRCTXT_PUBMSG_ME, + IRCTXT_PUBMSG_ME_CHANNEL, + IRCTXT_PUBMSG_HILIGHT, + IRCTXT_PUBMSG_HILIGHT_CHANNEL, + IRCTXT_PUBMSG, + IRCTXT_PUBMSG_CHANNEL, + IRCTXT_MSG_PRIVATE, + IRCTXT_MSG_PRIVATE_QUERY, + IRCTXT_NOTICE_SERVER, + IRCTXT_NOTICE_PUBLIC, + IRCTXT_NOTICE_PUBLIC_OPS, + 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_FILL_10, + + IRCTXT_ONLINE, + IRCTXT_PONG, + IRCTXT_WALLOPS, + IRCTXT_ACTION_WALLOPS, + IRCTXT_ERROR, + IRCTXT_UNKNOWN_MODE, + IRCTXT_NOT_CHANOP, + + IRCTXT_FILL_11, + + IRCTXT_IGNORED, + IRCTXT_UNIGNORED, + IRCTXT_IGNORE_NOT_FOUND, + IRCTXT_IGNORE_NO_IGNORES, + IRCTXT_IGNORE_HEADER, + IRCTXT_IGNORE_LINE, + IRCTXT_IGNORE_FOOTER, + IRCTXT_TALKING_IN, + IRCTXT_QUERY_STARTED, + IRCTXT_NO_QUERY, + IRCTXT_NO_MSGS_GOT, + IRCTXT_NO_MSGS_SENT +}; + +extern FORMAT_REC fecommon_irc_formats[]; diff --git a/apps/irssi/src/fe-common/silc/module.h b/apps/irssi/src/fe-common/silc/module.h new file mode 100644 index 00000000..f6f6791a --- /dev/null +++ b/apps/irssi/src/fe-common/silc/module.h @@ -0,0 +1,9 @@ +#include "common.h" + +#define MODULE_NAME "fe-common/silc" + +#undef PACKAGE +#undef VERSION +#include "silcincludes.h" +#include "clientlibincludes.h" +#include "silc-core.h" diff --git a/apps/irssi/src/fe-common/silc/silc-modules.c b/apps/irssi/src/fe-common/silc/silc-modules.c new file mode 100644 index 00000000..16081e1a --- /dev/null +++ b/apps/irssi/src/fe-common/silc/silc-modules.c @@ -0,0 +1,3 @@ +/* this file is automatically generated by configure - don't change */ +void fe_silc_modules_init(void) { } +void fe_silc_modules_deinit(void) { } diff --git a/apps/irssi/src/fe-text/Makefile.am b/apps/irssi/src/fe-text/Makefile.am new file mode 100644 index 00000000..ef0f5616 --- /dev/null +++ b/apps/irssi/src/fe-text/Makefile.am @@ -0,0 +1,53 @@ +bin_PROGRAMS = silc + +INCLUDES = \ + $(GLIB_CFLAGS) \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/core/ \ + -I$(top_srcdir)/src/silc/core/ \ + -I$(top_srcdir)/src/fe-common/core/ \ + -I$(top_srcdir)/src/fe-common/silc/ \ + $(CURSES_INCLUDEDIR) \ + -DLOCALEDIR=\""$(datadir)/locale"\" + +silc_DEPENDENCIES = @COMMON_LIBS@ + +silc_LDADD = \ + @COMMON_LIBS@ \ + @PERL_LINK_LIBS@ \ + @PERL_FE_LINK_LIBS@ \ + $(PROG_LIBS) \ + $(CURSES_LIBS) \ + -L../../../lib -lsilcclient -lsilc + +silc_SOURCES = \ + gui-entry.c \ + gui-expandos.c \ + gui-printtext.c \ + gui-readline.c \ + gui-windows.c \ + lastlog.c \ + mainwindows.c \ + mainwindow-activity.c \ + mainwindows-save.c \ + screen.c \ + statusbar.c \ + statusbar-items.c \ + textbuffer.c \ + textbuffer-commands.c \ + textbuffer-view.c \ + silc.c \ + module-formats.c + +noinst_HEADERS = \ + gui-entry.h \ + gui-printtext.h \ + gui-readline.h \ + gui-windows.h \ + mainwindows.h \ + statusbar.h \ + screen.h \ + textbuffer.h \ + textbuffer-view.h \ + module.h \ + module-formats.h diff --git a/apps/irssi/src/fe-text/gui-entry.c b/apps/irssi/src/fe-text/gui-entry.c new file mode 100644 index 00000000..7544539a --- /dev/null +++ b/apps/irssi/src/fe-text/gui-entry.c @@ -0,0 +1,298 @@ +/* + gui-entry.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "formats.h" + +#include "gui-printtext.h" +#include "screen.h" + +static GString *entry; +static int promptlen, permanent_prompt, pos, scrstart, scrpos; +static int prompt_hidden; +static char *prompt; + +static void entry_screenpos(void) +{ + if (pos-scrstart < COLS-2-promptlen && pos-scrstart > 0) { + scrpos = pos-scrstart; + return; + } + + if (pos < COLS-1-promptlen) { + scrstart = 0; + scrpos = pos; + } else { + scrpos = (COLS-promptlen)*2/3; + scrstart = pos-scrpos; + } +} + +static void entry_update(void) +{ + char *p; + int n, len; + + len = entry->len-scrstart > COLS-1-promptlen ? + COLS-1-promptlen : entry->len-scrstart; + + set_color(stdscr, 0); + move(LINES-1, promptlen); + + for (p = entry->str+scrstart, n = 0; n < len; n++, p++) { + if (prompt_hidden) + addch(' '); + else if ((unsigned char) *p >= 32) + addch((unsigned char) *p); + else { + set_color(stdscr, ATTR_REVERSE); + addch(*p+'A'-1); + set_color(stdscr, 0); + } + } + clrtoeol(); + + move_cursor(LINES-1, scrpos+promptlen); + screen_refresh(NULL); +} + +void gui_entry_set_prompt(const char *str) +{ + if (str != NULL) { + if (permanent_prompt) return; + + g_free_not_null(prompt); + prompt = g_strdup(str); + promptlen = format_get_length(prompt); + } + + if (prompt != NULL) + gui_printtext(0, LINES-1, prompt); + + entry_screenpos(); + entry_update(); +} + +void gui_entry_set_perm_prompt(const char *str) +{ + g_return_if_fail(str != NULL); + + g_free_not_null(prompt); + prompt = g_strdup(str); + promptlen = format_get_length(prompt); + + permanent_prompt = TRUE; + gui_entry_set_prompt(NULL); +} + +void gui_entry_set_hidden(int hidden) +{ + prompt_hidden = hidden; +} + +void gui_entry_remove_perm_prompt(void) +{ + permanent_prompt = FALSE; +} + +void gui_entry_set_text(const char *str) +{ + g_return_if_fail(str != NULL); + + g_string_assign(entry, str); + pos = entry->len; + + entry_screenpos(); + entry_update(); +} + +char *gui_entry_get_text(void) +{ + return entry->str; +} + +void gui_entry_insert_text(const char *str) +{ + g_return_if_fail(str != NULL); + + g_string_insert(entry, pos, str); + pos += strlen(str); + + entry_screenpos(); + entry_update(); +} + +void gui_entry_insert_char(char chr) +{ + g_string_insert_c(entry, pos, chr); + pos++; + + entry_screenpos(); + entry_update(); +} + +void gui_entry_erase(int size) +{ + if (pos < size) return; + +#ifdef WANT_BIG5 + if (is_big5(entry->str[pos-2], entry->str[pos-1])) + size++; +#endif WANT_BIG5 + + pos -= size; + g_string_erase(entry, pos, size); + + entry_screenpos(); + entry_update(); +} + +void gui_entry_erase_word(void) +{ + int to; + + if (pos == 0) return; + + to = pos - 1; + + while (entry->str[to] == ' ' && to > 0) + to--; + + while (entry->str[to] != ' ' && to > 0) + to--; + + if (entry->str[to] == ' ' && to > 0) + to++; + + g_string_erase(entry, to, pos - to); + pos = to; + + entry_screenpos(); + entry_update(); +} + +void gui_entry_erase_next_word(void) +{ + int to = pos; + + if (pos == entry->len) return; + + while (entry->str[to] == ' ' && to < entry->len) + to++; + + while (entry->str[to] != ' ' && to < entry->len) + to++; + + g_string_erase(entry, pos, to - pos); + + entry_screenpos(); + entry_update(); +} + +int gui_entry_get_pos(void) +{ + return pos; +} + +void gui_entry_set_pos(int p) +{ + if (p >= 0 && p <= entry->len) + pos = p; + + entry_screenpos(); + entry_update(); +} + +void gui_entry_move_pos(int p) +{ +#ifdef WANT_BIG5 + if (p > 0 && is_big5 (entry->str[pos], entry->str[pos+1])) + p++; + else if (p < 0 && is_big5 (entry->str[pos-1], entry->str[pos])) + p--; +#endif WANT_BIG5 + + if (pos+p >= 0 && pos+p <= entry->len) + pos += p; + + entry_screenpos(); + entry_update(); +} + +static void gui_entry_move_words_left(int count) +{ + if (pos == 0) return; + + while (count > 0 && pos > 0) { + while (pos > 0 && entry->str[pos-1] == ' ') + pos--; + while (pos > 0 && entry->str[pos-1] != ' ') + pos--; + count--; + } +} + +static void gui_entry_move_words_right(int count) +{ + if (pos == entry->len) return; + + while (count > 0 && pos < entry->len) { + while (pos < entry->len && entry->str[pos] != ' ') + pos++; + while (pos < entry->len && entry->str[pos] == ' ') + pos++; + count--; + } +} + +void gui_entry_move_words(int count) +{ + if (count < 0) + gui_entry_move_words_left(-count); + else if (count > 0) + gui_entry_move_words_right(count); + + entry_screenpos(); + entry_update(); +} + +void gui_entry_redraw(void) +{ + gui_entry_set_prompt(NULL); + + entry_screenpos(); + entry_update(); +} + +void gui_entry_init(void) +{ + entry = g_string_new(NULL); + + pos = scrpos = 0; + prompt = NULL; promptlen = 0; + permanent_prompt = FALSE; + prompt_hidden = FALSE; +} + +void gui_entry_deinit(void) +{ + if (prompt != NULL) g_free(prompt); + g_string_free(entry, TRUE); +} diff --git a/apps/irssi/src/fe-text/gui-entry.h b/apps/irssi/src/fe-text/gui-entry.h new file mode 100644 index 00000000..ff8f3ef5 --- /dev/null +++ b/apps/irssi/src/fe-text/gui-entry.h @@ -0,0 +1,31 @@ +#ifndef __GUI_ENTRY_H +#define __GUI_ENTRY_H + +void gui_entry_set_prompt(const char *str); + +/* permanent prompt can't be overwritten with gui_entry_set_prompt() */ +void gui_entry_set_perm_prompt(const char *str); +void gui_entry_remove_perm_prompt(void); +void gui_entry_set_hidden(int hidden); + +void gui_entry_set_text(const char *str); +char *gui_entry_get_text(void); + +void gui_entry_insert_text(const char *str); +void gui_entry_insert_char(char chr); + +void gui_entry_erase(int size); +void gui_entry_erase_word(void); +void gui_entry_erase_next_word(void); + +int gui_entry_get_pos(void); +void gui_entry_set_pos(int pos); +void gui_entry_move_pos(int pos); +void gui_entry_move_words(int count); + +void gui_entry_redraw(void); + +void gui_entry_init(void); +void gui_entry_deinit(void); + +#endif diff --git a/apps/irssi/src/fe-text/gui-expandos.c b/apps/irssi/src/fe-text/gui-expandos.c new file mode 100644 index 00000000..c95ac621 --- /dev/null +++ b/apps/irssi/src/fe-text/gui-expandos.c @@ -0,0 +1,61 @@ +/* + gui-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "expandos.h" + +#include "gui-entry.h" +#include "gui-readline.h" + +/* idle time */ +static char *expando_idletime(SERVER_REC *server, void *item, int *free_ret) +{ + int diff; + + *free_ret = TRUE; + diff = (int) (time(NULL) - get_idle_time()); + return g_strdup_printf("%d", diff); +} + +/* current contents of the input line */ +static char *expando_inputline(SERVER_REC *server, void *item, int *free_ret) +{ + return gui_entry_get_text(); +} + +/* value of cutbuffer */ +static char *expando_cutbuffer(SERVER_REC *server, void *item, int *free_ret) +{ + return cutbuffer; +} + +void gui_expandos_init(void) +{ + expando_create("E", expando_idletime, NULL); + expando_create("L", expando_inputline, NULL); + expando_create("U", expando_cutbuffer, NULL); +} + +void gui_expandos_deinit(void) +{ + expando_destroy("E", expando_idletime); + expando_destroy("L", expando_inputline); + expando_destroy("U", expando_cutbuffer); +} diff --git a/apps/irssi/src/fe-text/gui-printtext.c b/apps/irssi/src/fe-text/gui-printtext.c new file mode 100644 index 00000000..5faae0fa --- /dev/null +++ b/apps/irssi/src/fe-text/gui-printtext.c @@ -0,0 +1,275 @@ +/* + gui-printtext.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "settings.h" + +#include "formats.h" +#include "printtext.h" + +#include "screen.h" +#include "gui-windows.h" + +int mirc_colors[] = { 15, 0, 1, 2, 12, 6, 5, 4, 14, 10, 3, 11, 9, 13, 8, 7 }; +static int scrollback_lines, scrollback_hours, scrollback_burst_remove; + +static int scrollback_save_formats; +static GString *format; + +static int last_color, last_flags; +static int next_xpos, next_ypos; + +void gui_printtext(int xpos, int ypos, const char *str) +{ + next_xpos = xpos; + next_ypos = ypos; + + printtext_gui(str); + + next_xpos = next_ypos = -1; +} + +static void remove_old_lines(TEXT_BUFFER_VIEW_REC *view) +{ + LINE_REC *line; + time_t old_time; + + old_time = time(NULL)-(scrollback_hours*3600)+1; + if (view->buffer->lines_count >= + scrollback_lines+scrollback_burst_remove) { + /* remove lines by line count */ + while (view->buffer->lines_count > scrollback_lines) { + line = view->buffer->lines->data; + if (line->info.time >= old_time) { + /* too new line, don't remove yet */ + break; + } + textbuffer_view_remove_line(view, line); + } + } +} + +static void get_colors(int flags, int *fg, int *bg) +{ + if (flags & PRINTFLAG_MIRC_COLOR) { + /* mirc colors - real range is 0..15, but after 16 + colors wrap to 0, 1, ... */ + *bg = *bg < 0 ? 0 : mirc_colors[*bg % 16]; + if (*fg > 0) *fg = mirc_colors[*fg % 16]; + } else { + /* default colors */ + *bg = *bg < 0 || *bg > 15 ? 0 : *bg; + if (*fg > 8) *fg &= ~8; + } + + if (*fg < 0 || *fg > 15) { + *fg = *bg == 0 ? current_theme->default_color : + current_theme->default_real_color; + } + + if (flags & PRINTFLAG_REVERSE) { + int tmp; + + tmp = *fg; *fg = *bg; *bg = tmp; + } + + if (*fg == 8) *fg |= ATTR_COLOR8; + if (flags & PRINTFLAG_BOLD) { + if (*fg == 0) *fg = current_theme->default_real_color; + *fg |= 8; + } + if (flags & PRINTFLAG_UNDERLINE) *fg |= ATTR_UNDERLINE; + if (flags & PRINTFLAG_BLINK) *bg |= 0x08; +} + +static void line_add_colors(TEXT_BUFFER_REC *buffer, LINE_REC **line, + int fg, int bg, int flags) +{ + unsigned char data[12]; + int color, pos; + + /* color should never have last bit on or it would be treated as a + command! */ + color = (fg & 0x0f) | ((bg & 0x07) << 4); + pos = 0; + + if (((fg & ATTR_COLOR8) == 0 && (fg|(bg << 4)) != last_color) || + ((fg & ATTR_COLOR8) && (fg & 0xf0) != (last_color & 0xf0))) { + data[pos++] = 0; + data[pos++] = color == 0 ? LINE_CMD_COLOR0 : color; + } + + if ((flags & PRINTFLAG_UNDERLINE) != (last_flags & PRINTFLAG_UNDERLINE)) { + data[pos++] = 0; + data[pos++] = LINE_CMD_UNDERLINE; + } + if (fg & ATTR_COLOR8) { + data[pos++] = 0; + data[pos++] = LINE_CMD_COLOR8; + } + if (bg & 0x08) { + data[pos++] = 0; + data[pos++] = LINE_CMD_BLINK; + } + if (flags & PRINTFLAG_INDENT) { + data[pos++] = 0; + data[pos++] = LINE_CMD_INDENT; + } + + *line = textbuffer_insert(buffer, *line, data, pos, NULL); + + last_flags = flags; + last_color = fg | (bg << 4); +} + +static void view_add_eol(TEXT_BUFFER_VIEW_REC *view, LINE_REC **line) +{ + static const unsigned char eol[] = { 0, LINE_CMD_EOL }; + + *line = textbuffer_insert(view->buffer, *line, eol, 2, NULL); + textbuffer_view_insert_line(view, *line); +} + +static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor, + void *bgcolor, void *pflags, + char *str, void *level) +{ + TEXT_BUFFER_VIEW_REC *view; + LINE_REC *insert_after; + LINE_INFO_REC lineinfo; + int fg, bg, flags; + + flags = GPOINTER_TO_INT(pflags); + fg = GPOINTER_TO_INT(fgcolor); + bg = GPOINTER_TO_INT(bgcolor); + get_colors(flags, &fg, &bg); + + if (window == NULL) { + g_return_if_fail(next_xpos != -1); + + wmove(stdscr, next_ypos, next_xpos); + set_color(stdscr, fg | (bg << 4)); + addstr(str); + next_xpos += strlen(str); + return; + } + + lineinfo.level = GPOINTER_TO_INT(level); + lineinfo.time = time(NULL); + + view = WINDOW_GUI(window)->view; + insert_after = WINDOW_GUI(window)->use_insert_after ? + WINDOW_GUI(window)->insert_after : view->buffer->cur_line; + + if (flags & PRINTFLAG_NEWLINE) + view_add_eol(view, &insert_after); + line_add_colors(view->buffer, &insert_after, fg, bg, flags); + textbuffer_insert(view->buffer, insert_after, + str, strlen(str), &lineinfo); +} + +static void sig_printtext_finished(WINDOW_REC *window) +{ + TEXT_BUFFER_VIEW_REC *view; + LINE_REC *insert_after; + + last_color = 0; + last_flags = 0; + + view = WINDOW_GUI(window)->view; + insert_after = WINDOW_GUI(window)->use_insert_after ? + WINDOW_GUI(window)->insert_after : view->buffer->cur_line; + + view_add_eol(view, &insert_after); + remove_old_lines(view); +} + +static void sig_print_format(THEME_REC *theme, const char *module, + TEXT_DEST_REC *dest, void *formatnump, + char **args) +{ + FORMAT_REC *formats; + int formatnum, n; + + if (!scrollback_save_formats) + return; + + formatnum = GPOINTER_TO_INT(formatnump); + formats = g_hash_table_lookup(default_formats, module); + + /* */ + g_string_truncate(format, 0); + + g_string_append_c(format, '\0'); + g_string_append_c(format, (char)LINE_CMD_FORMAT); + + g_string_append(format, module); + + g_string_append_c(format, '\0'); + g_string_append_c(format, (char)LINE_CMD_FORMAT); + + g_string_append(format, formats[formatnum].tag); + + for (n = 0; n < formats[formatnum].params; n++) { + g_string_append_c(format, '\0'); + g_string_append_c(format, (char)LINE_CMD_FORMAT); + + g_string_append(format, args[n]); + } +} + +static void read_settings(void) +{ + scrollback_lines = settings_get_int("scrollback_lines"); + scrollback_hours = settings_get_int("scrollback_hours"); + scrollback_burst_remove = settings_get_int("scrollback_burst_remove"); + scrollback_save_formats = settings_get_bool("scrollback_save_formats"); +} + +void gui_printtext_init(void) +{ + next_xpos = next_ypos = -1; + format = g_string_new(NULL); + + settings_add_int("history", "scrollback_lines", 500); + settings_add_int("history", "scrollback_hours", 24); + settings_add_int("history", "scrollback_burst_remove", 10); + settings_add_bool("history", "scrollback_save_formats", FALSE); + + signal_add("gui print text", (SIGNAL_FUNC) sig_gui_print_text); + signal_add("print text finished", (SIGNAL_FUNC) sig_printtext_finished); + signal_add("print format", (SIGNAL_FUNC) sig_print_format); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); + signal_add("beep", (SIGNAL_FUNC) beep); + + read_settings(); +} + +void gui_printtext_deinit(void) +{ + g_string_free(format, TRUE); + + signal_remove("gui print text", (SIGNAL_FUNC) sig_gui_print_text); + signal_remove("print text finished", (SIGNAL_FUNC) sig_printtext_finished); + signal_remove("print format", (SIGNAL_FUNC) sig_print_format); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); + signal_remove("beep", (SIGNAL_FUNC) beep); +} diff --git a/apps/irssi/src/fe-text/gui-printtext.h b/apps/irssi/src/fe-text/gui-printtext.h new file mode 100644 index 00000000..3b2098b7 --- /dev/null +++ b/apps/irssi/src/fe-text/gui-printtext.h @@ -0,0 +1,13 @@ +#ifndef __GUI_PRINTTEXT_H +#define __GUI_PRINTTEXT_H + +#include "gui-windows.h" + +extern int mirc_colors[]; + +void gui_printtext_init(void); +void gui_printtext_deinit(void); + +void gui_printtext(int xpos, int ypos, const char *str); + +#endif diff --git a/apps/irssi/src/fe-text/gui-readline.c b/apps/irssi/src/fe-text/gui-readline.c new file mode 100644 index 00000000..bc3429d8 --- /dev/null +++ b/apps/irssi/src/fe-text/gui-readline.c @@ -0,0 +1,666 @@ +/* + gui-readline.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "misc.h" +#include "settings.h" +#include "special-vars.h" + +#include "completion.h" +#include "command-history.h" +#include "keyboard.h" +#include "translation.h" + +#include "screen.h" +#include "gui-entry.h" +#include "gui-windows.h" + +#include + +typedef void (*ENTRY_REDIRECT_KEY_FUNC) (int key, void *data, SERVER_REC *server, WI_ITEM_REC *item); +typedef void (*ENTRY_REDIRECT_ENTRY_FUNC) (const char *line, void *data, SERVER_REC *server, WI_ITEM_REC *item); + +typedef struct { + SIGNAL_FUNC func; + int flags; + void *data; +} ENTRY_REDIRECT_REC; + +static KEYBOARD_REC *keyboard; +static ENTRY_REDIRECT_REC *redir; + +char *cutbuffer; +static int readtag; +static time_t idle_time; + +static void handle_key_redirect(int key) +{ + ENTRY_REDIRECT_KEY_FUNC func; + void *data; + + func = (ENTRY_REDIRECT_KEY_FUNC) redir->func; + data = redir->data; + g_free_and_null(redir); + + if (func != NULL) + func(key, data, active_win->active_server, active_win->active); + + gui_entry_remove_perm_prompt(); + window_update_prompt(); +} + +static void handle_entry_redirect(const char *line) +{ + ENTRY_REDIRECT_ENTRY_FUNC func; + void *data; + + gui_entry_set_hidden(FALSE); + + func = (ENTRY_REDIRECT_ENTRY_FUNC) redir->func; + data = redir->data; + g_free_and_null(redir); + + if (func != NULL) { + func(line, data, active_win->active_server, + active_win->active); + } + + gui_entry_remove_perm_prompt(); + window_update_prompt(); +} + +static int get_scroll_count(void) +{ + const char *str; + double count; + + str = settings_get_str("scroll_page_count"); + count = atof(str + (*str == '/')); + if (count <= 0) + count = 1; + else if (count < 1) + count = 1.0/count; + + if (*str == '/') + count = WINDOW_GUI(active_win)->parent->height/count; + return (int)count; +} + +static void window_prev_page(void) +{ + gui_window_scroll(active_win, -get_scroll_count()); +} + +static void window_next_page(void) +{ + gui_window_scroll(active_win, get_scroll_count()); +} + +void handle_key(int key) +{ + char str[3]; + + idle_time = time(NULL); + + if (redir != NULL && redir->flags & ENTRY_REDIRECT_FLAG_HOTKEY) { + handle_key_redirect(key); + return; + } + + if (key >= 0 && key < 32) { + /* control key */ + str[0] = '^'; + str[1] = key+'@'; + str[2] = '\0'; + } else if (key == 127) { + str[0] = '^'; + str[1] = '?'; + str[2] = '\0'; + } else { + str[0] = key; + str[1] = '\0'; + } + + if (!key_pressed(keyboard, str)) { + /* key wasn't used for anything, print it */ + gui_entry_insert_char((char) key); + } +} + +static void key_send_line(void) +{ + int add_history; + char *str; + + str = gui_entry_get_text(); + if (*str == '\0') return; + + translate_output(str); + + add_history = TRUE; + if (redir == NULL) { + signal_emit("send command", 3, str, + active_win->active_server, + active_win->active); + } else { + if (redir->flags & ENTRY_REDIRECT_FLAG_HIDDEN) + add_history = FALSE; + handle_entry_redirect(str); + } + + if (add_history) { + command_history_add(active_win, gui_entry_get_text(), + FALSE); + } + gui_entry_set_text(""); + command_history_clear_pos(active_win); +} + +static void key_combo(void) +{ +} + +static void key_backward_history(void) +{ + const char *text; + + text = command_history_prev(active_win, gui_entry_get_text()); + gui_entry_set_text(text); +} + +static void key_forward_history(void) +{ + const char *text; + + text = command_history_next(active_win, gui_entry_get_text()); + gui_entry_set_text(text); +} + +static void key_beginning_of_line(void) +{ + gui_entry_set_pos(0); +} + +static void key_end_of_line(void) +{ + gui_entry_set_pos(strlen(gui_entry_get_text())); +} + +static void key_backward_character(void) +{ + gui_entry_move_pos(-1); +} + +static void key_forward_character(void) +{ + gui_entry_move_pos(1); +} + +static void key_backward_word(void) +{ + gui_entry_move_words(-1); +} + +static void key_forward_word(void) +{ + gui_entry_move_words(1); +} + +static void key_erase_line(void) +{ + g_free_not_null(cutbuffer); + cutbuffer = g_strdup(gui_entry_get_text()); + + gui_entry_set_text(""); +} + +static void key_erase_to_beg_of_line(void) +{ + int pos; + + pos = gui_entry_get_pos(); + g_free_not_null(cutbuffer); + cutbuffer = g_strndup(gui_entry_get_text(), pos); + + gui_entry_erase(pos); +} + +static void key_erase_to_end_of_line(void) +{ + int pos; + + pos = gui_entry_get_pos(); + g_free_not_null(cutbuffer); + cutbuffer = g_strdup(gui_entry_get_text()+pos); + + gui_entry_set_pos(strlen(gui_entry_get_text())); + gui_entry_erase(strlen(gui_entry_get_text()) - pos); +} + +static void key_yank_from_cutbuffer(void) +{ + if (cutbuffer != NULL) + gui_entry_insert_text(cutbuffer); +} + +static void key_transpose_characters(void) +{ + char *line, c; + int pos; + + pos = gui_entry_get_pos(); + line = gui_entry_get_text(); + if (pos == 0 || strlen(line) < 2) + return; + + if (line[pos] != '\0') + gui_entry_move_pos(1); + c = line[gui_entry_get_pos()-1]; + gui_entry_erase(1); + gui_entry_move_pos(-1); + gui_entry_insert_char(c); + gui_entry_set_pos(pos); +} + +static void key_delete_character(void) +{ + if (gui_entry_get_pos() < (int)strlen(gui_entry_get_text())) { + gui_entry_move_pos(1); + gui_entry_erase(1); + } +} + +static void key_backspace(void) +{ + gui_entry_erase(1); +} + +static void key_delete_previous_word(void) +{ + gui_entry_erase_word(); +} + +static void key_delete_next_word(void) +{ + gui_entry_erase_next_word(); +} + +static void key_delete_to_previous_space(void) +{ + gui_entry_erase_word(); +} + +void readline(void) +{ + int key; + + for (;;) { + key = getch(); + if (key == ERR +#ifdef KEY_RESIZE + || key == KEY_RESIZE +#endif + ) break; + + handle_key(key); + } +} + +time_t get_idle_time(void) +{ + return idle_time; +} + +static void key_scroll_backward(void) +{ + window_prev_page(); +} + +static void key_scroll_forward(void) +{ + window_next_page(); +} + +static void key_scroll_start(void) +{ + signal_emit("command scrollback home", 3, NULL, active_win->active_server, active_win->active); +} + +static void key_scroll_end(void) +{ + signal_emit("command scrollback end", 3, NULL, active_win->active_server, active_win->active); +} + +static void key_change_window(const char *data) +{ + signal_emit("command window goto", 3, data, active_win->active_server, active_win->active); +} + +static void key_word_completion(void) +{ + char *line; + int pos; + + pos = gui_entry_get_pos(); + + line = word_complete(active_win, gui_entry_get_text(), &pos); + if (line != NULL) { + gui_entry_set_text(line); + gui_entry_set_pos(pos); + g_free(line); + } +} + +static void key_check_replaces(void) +{ + char *line; + int pos; + + pos = gui_entry_get_pos(); + + line = auto_word_complete(gui_entry_get_text(), &pos); + if (line != NULL) { + gui_entry_set_text(line); + gui_entry_set_pos(pos); + g_free(line); + } +} + +static void key_previous_window(void) +{ + signal_emit("command window previous", 3, "", active_win->active_server, active_win->active); +} + +static void key_next_window(void) +{ + signal_emit("command window next", 3, "", active_win->active_server, active_win->active); +} + +static void key_left_window(void) +{ + signal_emit("command window left", 3, "", active_win->active_server, active_win->active); +} + +static void key_right_window(void) +{ + signal_emit("command window right", 3, "", active_win->active_server, active_win->active); +} + +static void key_upper_window(void) +{ + signal_emit("command window up", 3, "", active_win->active_server, active_win->active); +} + +static void key_lower_window(void) +{ + signal_emit("command window down", 3, "", active_win->active_server, active_win->active); +} + +static void key_active_window(void) +{ + signal_emit("command window goto", 3, "active", active_win->active_server, active_win->active); +} + +static void key_previous_window_item(void) +{ + SERVER_REC *server; + GSList *pos; + + if (active_win->items != NULL) + signal_emit("command window item prev", 3, "", active_win->active_server, active_win->active); + else if (servers != NULL) { + /* change server */ + if (active_win->active_server == NULL) + server = servers->data; + else { + pos = g_slist_find(servers, active_win->active_server); + server = pos->next != NULL ? pos->next->data : servers->data; + } + signal_emit("command window server", 3, server->tag, active_win->active_server, active_win->active); + } +} + +static void key_next_window_item(void) +{ + SERVER_REC *server; + int index; + + if (active_win->items != NULL) { + signal_emit("command window item next", 3, "", + active_win->active_server, active_win->active); + } + else if (servers != NULL) { + /* change server */ + if (active_win->active_server == NULL) + server = servers->data; + else { + index = g_slist_index(servers, active_win->active_server); + server = index > 0 ? g_slist_nth(servers, index-1)->data : + g_slist_last(servers)->data; + } + signal_emit("command window server", 3, server->tag, + active_win->active_server, active_win->active); + } +} + +static void key_insert_text(const char *data) +{ + char *str; + + str = parse_special_string(data, active_win->active_server, + active_win->active, "", NULL, 0); + gui_entry_insert_text(str); + g_free(str); +} + +static void sig_window_auto_changed(void) +{ + command_history_next(active_win, gui_entry_get_text()); + gui_entry_set_text(""); +} + +static void sig_gui_entry_redirect(SIGNAL_FUNC func, const char *entry, + void *flags, void *data) +{ + redir = g_new0(ENTRY_REDIRECT_REC, 1); + redir->func = func; + redir->flags = GPOINTER_TO_INT(flags); + redir->data = data; + + if (redir->flags & ENTRY_REDIRECT_FLAG_HIDDEN) + gui_entry_set_hidden(TRUE); + gui_entry_set_perm_prompt(entry); +} + +void gui_readline_init(void) +{ + static char changekeys[] = "1234567890qwertyuio"; + char *key, data[MAX_INT_STRLEN]; + int n; + + cutbuffer = NULL; + redir = NULL; + idle_time = time(NULL); + readtag = g_input_add_full(g_io_channel_unix_new(0), + G_PRIORITY_HIGH, G_INPUT_READ, + (GInputFunction) readline, NULL); + + settings_add_str("history", "scroll_page_count", "/2"); + + keyboard = keyboard_create(NULL); + key_configure_freeze(); + + key_bind("key", NULL, "^M", "return", (SIGNAL_FUNC) key_combo); + key_bind("key", NULL, "^J", "return", (SIGNAL_FUNC) key_combo); + + /* meta */ + key_bind("key", NULL, "^[", "meta", (SIGNAL_FUNC) key_combo); + key_bind("key", NULL, "meta-[", "meta2", (SIGNAL_FUNC) key_combo); + key_bind("key", NULL, "meta-O", "meta2", (SIGNAL_FUNC) key_combo); + key_bind("key", NULL, "meta-[O", "meta2", (SIGNAL_FUNC) key_combo); + + /* arrow keys */ + key_bind("key", NULL, "meta2-A", "up", (SIGNAL_FUNC) key_combo); + key_bind("key", NULL, "meta2-B", "down", (SIGNAL_FUNC) key_combo); + key_bind("key", NULL, "meta2-C", "right", (SIGNAL_FUNC) key_combo); + key_bind("key", NULL, "meta2-D", "left", (SIGNAL_FUNC) key_combo); + + key_bind("key", NULL, "meta2-1~", "home", (SIGNAL_FUNC) key_combo); + key_bind("key", NULL, "meta2-7~", "home", (SIGNAL_FUNC) key_combo); + key_bind("key", NULL, "meta2-H", "home", (SIGNAL_FUNC) key_combo); + + key_bind("key", NULL, "meta2-4~", "end", (SIGNAL_FUNC) key_combo); + key_bind("key", NULL, "meta2-8~", "end", (SIGNAL_FUNC) key_combo); + key_bind("key", NULL, "meta2-F", "end", (SIGNAL_FUNC) key_combo); + + key_bind("key", NULL, "meta2-5~", "prior", (SIGNAL_FUNC) key_combo); + key_bind("key", NULL, "meta2-I", "prior", (SIGNAL_FUNC) key_combo); + key_bind("key", NULL, "meta2-6~", "next", (SIGNAL_FUNC) key_combo); + key_bind("key", NULL, "meta2-G", "next", (SIGNAL_FUNC) key_combo); + + key_bind("key", NULL, "meta2-2~", "insert", (SIGNAL_FUNC) key_combo); + key_bind("key", NULL, "meta2-3~", "delete", (SIGNAL_FUNC) key_combo); + + /* cursor movement */ + key_bind("backward_character", "", "left", NULL, (SIGNAL_FUNC) key_backward_character); + key_bind("forward_character", "", "right", NULL, (SIGNAL_FUNC) key_forward_character); + key_bind("backward_word", "", "meta2-d", NULL, (SIGNAL_FUNC) key_backward_word); + key_bind("forward_word", "", "meta2-c", NULL, (SIGNAL_FUNC) key_forward_word); + key_bind("beginning_of_line", "", "home", NULL, (SIGNAL_FUNC) key_beginning_of_line); + key_bind("beginning_of_line", NULL, "^A", NULL, (SIGNAL_FUNC) key_beginning_of_line); + key_bind("end_of_line", "", "end", NULL, (SIGNAL_FUNC) key_end_of_line); + key_bind("end_of_line", NULL, "^E", NULL, (SIGNAL_FUNC) key_end_of_line); + + /* history */ + key_bind("backward_history", "", "up", NULL, (SIGNAL_FUNC) key_backward_history); + key_bind("forward_history", "", "down", NULL, (SIGNAL_FUNC) key_forward_history); + + /* line editing */ + key_bind("backspace", "", "^H", NULL, (SIGNAL_FUNC) key_backspace); + key_bind("backspace", "", "^?", NULL, (SIGNAL_FUNC) key_backspace); + key_bind("delete_character", "", "delete", NULL, (SIGNAL_FUNC) key_delete_character); + key_bind("delete_character", NULL, "^D", NULL, (SIGNAL_FUNC) key_delete_character); + key_bind("delete_next_word", "", NULL, NULL, (SIGNAL_FUNC) key_delete_next_word); + key_bind("delete_previous_word", "", NULL, NULL, (SIGNAL_FUNC) key_delete_previous_word); + key_bind("delete_to_previous_space", "", "^W", NULL, (SIGNAL_FUNC) key_delete_to_previous_space); + key_bind("erase_line", "", "^U", NULL, (SIGNAL_FUNC) key_erase_line); + key_bind("erase_to_beg_of_line", "", NULL, NULL, (SIGNAL_FUNC) key_erase_to_beg_of_line); + key_bind("erase_to_end_of_line", "", "^K", NULL, (SIGNAL_FUNC) key_erase_to_end_of_line); + key_bind("yank_from_cutbuffer", "", "^Y", NULL, (SIGNAL_FUNC) key_yank_from_cutbuffer); + key_bind("transpose_characters", "Swap current and previous character", "^T", NULL, (SIGNAL_FUNC) key_transpose_characters); + + /* line transmitting */ + key_bind("send_line", "Execute the input line", "return", NULL, (SIGNAL_FUNC) key_send_line); + key_bind("word_completion", "", "^I", NULL, (SIGNAL_FUNC) key_word_completion); + key_bind("check_replaces", "Check word replaces", " ", NULL, (SIGNAL_FUNC) key_check_replaces); + key_bind("check_replaces", NULL, NULL, NULL, (SIGNAL_FUNC) key_check_replaces); + + /* window managing */ + key_bind("previous_window", "Previous window", "^P", NULL, (SIGNAL_FUNC) key_previous_window); + key_bind("left_window", "Window in left", "meta-left", NULL, (SIGNAL_FUNC) key_left_window); + key_bind("next_window", "Next window", "^N", NULL, (SIGNAL_FUNC) key_next_window); + key_bind("right_window", "Window in right", "meta-right", NULL, (SIGNAL_FUNC) key_right_window); + key_bind("upper_window", "Upper window", "meta-up", NULL, (SIGNAL_FUNC) key_upper_window); + key_bind("lower_window", "Lower window", "meta-down", NULL, (SIGNAL_FUNC) key_lower_window); + key_bind("active_window", "Go to next window with the highest activity", "meta-a", NULL, (SIGNAL_FUNC) key_active_window); + key_bind("next_window_item", "Next channel/query", "^X", NULL, (SIGNAL_FUNC) key_next_window_item); + key_bind("previous_window_item", "Previous channel/query", NULL, NULL, (SIGNAL_FUNC) key_previous_window_item); + + key_bind("refresh_screen", "Redraw screen", "^L", NULL, (SIGNAL_FUNC) irssi_redraw); + key_bind("scroll_backward", "Previous page", "prior", NULL, (SIGNAL_FUNC) key_scroll_backward); + key_bind("scroll_backward", NULL, "meta-p", NULL, (SIGNAL_FUNC) key_scroll_backward); + key_bind("scroll_forward", "Next page", "next", NULL, (SIGNAL_FUNC) key_scroll_forward); + key_bind("scroll_forward", NULL, "meta-n", NULL, (SIGNAL_FUNC) key_scroll_forward); + key_bind("scroll_start", "Beginning of the window", "", NULL, (SIGNAL_FUNC) key_scroll_start); + key_bind("scroll_end", "End of the window", "", NULL, (SIGNAL_FUNC) key_scroll_end); + + /* inserting special input characters to line.. */ + key_bind("insert_text", "Append text to line", NULL, NULL, (SIGNAL_FUNC) key_insert_text); + + key_bind("multi", NULL, "return", "check_replaces;send_line", NULL); + + for (n = 0; changekeys[n] != '\0'; n++) { + key = g_strdup_printf("meta-%c", changekeys[n]); + ltoa(data, n+1); + key_bind("change_window", "Change window", key, data, (SIGNAL_FUNC) key_change_window); + g_free(key); + } + + key_configure_thaw(); + + signal_add("window changed automatic", (SIGNAL_FUNC) sig_window_auto_changed); + signal_add("gui entry redirect", (SIGNAL_FUNC) sig_gui_entry_redirect); +} + +void gui_readline_deinit(void) +{ + g_free_not_null(cutbuffer); + g_source_remove(readtag); + + key_configure_freeze(); + + key_unbind("backward_character", (SIGNAL_FUNC) key_backward_character); + key_unbind("forward_character", (SIGNAL_FUNC) key_forward_character); + key_unbind("backward_word", (SIGNAL_FUNC) key_backward_word); + key_unbind("forward_word", (SIGNAL_FUNC) key_forward_word); + key_unbind("beginning_of_line", (SIGNAL_FUNC) key_beginning_of_line); + key_unbind("end_of_line", (SIGNAL_FUNC) key_end_of_line); + + key_unbind("backward_history", (SIGNAL_FUNC) key_backward_history); + key_unbind("forward_history", (SIGNAL_FUNC) key_forward_history); + + key_unbind("backspace", (SIGNAL_FUNC) key_backspace); + key_unbind("delete_character", (SIGNAL_FUNC) key_delete_character); + key_unbind("delete_next_word", (SIGNAL_FUNC) key_delete_next_word); + key_unbind("delete_previous_word", (SIGNAL_FUNC) key_delete_previous_word); + key_unbind("delete_to_previous_space", (SIGNAL_FUNC) key_delete_to_previous_space); + key_unbind("erase_line", (SIGNAL_FUNC) key_erase_line); + key_unbind("erase_to_beg_of_line", (SIGNAL_FUNC) key_erase_to_beg_of_line); + key_unbind("erase_to_end_of_line", (SIGNAL_FUNC) key_erase_to_end_of_line); + key_unbind("yank_from_cutbuffer", (SIGNAL_FUNC) key_yank_from_cutbuffer); + key_unbind("transpose_characters", (SIGNAL_FUNC) key_transpose_characters); + + key_unbind("word_completion", (SIGNAL_FUNC) key_word_completion); + key_unbind("check_replaces", (SIGNAL_FUNC) key_check_replaces); + + key_unbind("previous_window", (SIGNAL_FUNC) key_previous_window); + key_unbind("next_window", (SIGNAL_FUNC) key_next_window); + key_unbind("upper_window", (SIGNAL_FUNC) key_upper_window); + key_unbind("lower_window", (SIGNAL_FUNC) key_lower_window); + key_unbind("active_window", (SIGNAL_FUNC) key_active_window); + key_unbind("next_window_item", (SIGNAL_FUNC) key_next_window_item); + key_unbind("previous_window_item", (SIGNAL_FUNC) key_previous_window_item); + + key_unbind("refresh_screen", (SIGNAL_FUNC) irssi_redraw); + key_unbind("scroll_backward", (SIGNAL_FUNC) key_scroll_backward); + key_unbind("scroll_forward", (SIGNAL_FUNC) key_scroll_forward); + key_unbind("scroll_start", (SIGNAL_FUNC) key_scroll_start); + key_unbind("scroll_end", (SIGNAL_FUNC) key_scroll_end); + + key_unbind("insert_text", (SIGNAL_FUNC) key_insert_text); + key_unbind("change_window", (SIGNAL_FUNC) key_change_window); + keyboard_destroy(keyboard); + + key_configure_thaw(); + + signal_remove("window changed automatic", (SIGNAL_FUNC) sig_window_auto_changed); + signal_remove("gui entry redirect", (SIGNAL_FUNC) sig_gui_entry_redirect); +} diff --git a/apps/irssi/src/fe-text/gui-readline.h b/apps/irssi/src/fe-text/gui-readline.h new file mode 100644 index 00000000..6464921f --- /dev/null +++ b/apps/irssi/src/fe-text/gui-readline.h @@ -0,0 +1,12 @@ +#ifndef __GUI_READLINE_H +#define __GUI_READLINE_H + +extern char *cutbuffer; + +void readline(void); +time_t get_idle_time(void); + +void gui_readline_init(void); +void gui_readline_deinit(void); + +#endif diff --git a/apps/irssi/src/fe-text/gui-windows.c b/apps/irssi/src/fe-text/gui-windows.c new file mode 100644 index 00000000..9a199cdd --- /dev/null +++ b/apps/irssi/src/fe-text/gui-windows.c @@ -0,0 +1,333 @@ +/* + gui-windows.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "misc.h" +#include "settings.h" +#include "special-vars.h" + +#include "screen.h" +#include "gui-entry.h" +#include "gui-windows.h" +#include "gui-printtext.h" + +static int window_create_override; + +static char *prompt, *prompt_window; + +static GUI_WINDOW_REC *gui_window_init(WINDOW_REC *window, + MAIN_WINDOW_REC *parent) +{ + GUI_WINDOW_REC *gui; + + window->width = parent->width; + window->height = parent->height; + + gui = g_new0(GUI_WINDOW_REC, 1); + gui->parent = parent; + gui->view = textbuffer_view_create(textbuffer_create(), + window->width, window->height, + settings_get_int("indent"), + settings_get_bool("indent_always")); + return gui; +} + +static void gui_window_deinit(GUI_WINDOW_REC *gui) +{ + textbuffer_view_destroy(gui->view); + g_free(gui); +} + +static void sig_window_create_override(gpointer tab) +{ + window_create_override = GPOINTER_TO_INT(tab); +} + +static void gui_window_created(WINDOW_REC *window) +{ + MAIN_WINDOW_REC *parent; + + g_return_if_fail(window != NULL); + + parent = window_create_override != 0 && + active_win != NULL && WINDOW_GUI(active_win) != NULL ? + WINDOW_GUI(active_win)->parent : mainwindow_create(); + if (parent == NULL) { + /* not enough space for new window, but we really can't + abort creation of the window anymore, so create hidden + window instead. */ + parent = WINDOW_GUI(active_win)->parent; + } + window_create_override = -1; + + if (settings_get_bool("autostick_split_windows") && + (parent->sticky_windows != NULL || + (mainwindows->next != NULL && parent->active == NULL))) { + /* set the window sticky */ + parent->sticky_windows = + g_slist_append(parent->sticky_windows, window); + } + + if (parent->active == NULL) parent->active = window; + window->gui_data = gui_window_init(window, parent); + signal_emit("gui window created", 1, window); +} + +static void gui_window_destroyed(WINDOW_REC *window) +{ + MAIN_WINDOW_REC *parent; + GUI_WINDOW_REC *gui; + + g_return_if_fail(window != NULL); + + gui = WINDOW_GUI(window); + parent = gui->parent; + + signal_emit("gui window destroyed", 1, window); + + gui_window_deinit(gui); + window->gui_data = NULL; + + if (parent->active == window && mainwindows->next != NULL) + mainwindow_destroy(parent); +} + +void gui_window_resize(WINDOW_REC *window, int width, int height) +{ + GUI_WINDOW_REC *gui; + + gui = WINDOW_GUI(window); + + window->width = width; + window->height = height; + textbuffer_view_resize(gui->view, width, height); +} + +void gui_window_scroll(WINDOW_REC *window, int lines) +{ + g_return_if_fail(window != NULL); + + textbuffer_view_scroll(WINDOW_GUI(window)->view, lines); + signal_emit("gui page scrolled", 1, window); +} + +void gui_window_scroll_line(WINDOW_REC *window, LINE_REC *line) +{ + g_return_if_fail(window != NULL); + g_return_if_fail(line != NULL); + + textbuffer_view_scroll_line(WINDOW_GUI(window)->view, line); + signal_emit("gui page scrolled", 1, window); +} + +void window_update_prompt(void) +{ + const char *special; + char *prompt, *text; + int var_used; + + special = settings_get_str(active_win->active != NULL ? + "prompt" : "prompt_window"); + if (*special == '\0') { + gui_entry_set_prompt(""); + return; + } + + prompt = parse_special_string(special, active_win->active_server, + active_win->active, "", &var_used, + PARSE_FLAG_ISSET_ANY | + PARSE_FLAG_ESCAPE_VARS); + if (!var_used && strchr(special, '$') != NULL) { + /* none of the $vars had non-empty values, use empty prompt */ + *prompt = '\0'; + } + + /* set prompt */ + text = show_lowascii(prompt); + gui_entry_set_prompt(text); + g_free(text); + + g_free(prompt); +} + +static void window_update_prompt_server(SERVER_REC *server) +{ + if (server == active_win->active_server) + window_update_prompt(); +} + +static void window_update_prompt_window(WINDOW_REC *window) +{ + if (window == active_win) + window_update_prompt(); +} + +static void window_update_prompt_window_item(WI_ITEM_REC *item) +{ + if (item == active_win->active) + window_update_prompt(); +} + +void gui_window_reparent(WINDOW_REC *window, MAIN_WINDOW_REC *parent) +{ + MAIN_WINDOW_REC *oldparent; + + oldparent = WINDOW_GUI(window)->parent; + if (oldparent == parent) + return; + + textbuffer_view_set_window(WINDOW_GUI(window)->view, NULL); + + WINDOW_GUI(window)->parent = parent; + if (parent->height != oldparent->height || + parent->width != oldparent->width) + gui_window_resize(window, parent->width, parent->height); +} + +static MAIN_WINDOW_REC *mainwindow_find_unsticky(void) +{ + GSList *tmp; + + for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { + MAIN_WINDOW_REC *rec = tmp->data; + + if (rec->sticky_windows == NULL) + return rec; + } + + /* all windows are sticky, fallback to active window */ + return active_mainwin; +} + +static void signal_window_changed(WINDOW_REC *window, WINDOW_REC *old_window) +{ + MAIN_WINDOW_REC *parent; + + g_return_if_fail(window != NULL); + + if (quitting) return; + + parent = WINDOW_GUI(window)->parent; + if (is_window_visible(window)) { + /* already visible */ + active_mainwin = parent; + } else if (active_mainwin == NULL) { + /* no main window set yet */ + active_mainwin = parent; + } else if (g_slist_find(parent->sticky_windows, window) != NULL) { + /* window is sticky, switch to correct main window */ + if (parent != active_mainwin) + active_mainwin = parent; + } else { + /* move window to active main window */ + if (active_mainwin->sticky_windows != NULL) { + /* active mainwindow is sticky, we'll need to + set the window active somewhere else */ + active_mainwin = mainwindow_find_unsticky(); + } + gui_window_reparent(window, active_mainwin); + } + active_mainwin->active = window; + + if (old_window != NULL && !is_window_visible(old_window)) + textbuffer_view_set_window(WINDOW_GUI(old_window)->view, NULL); + + textbuffer_view_set_window(WINDOW_GUI(window)->view, + parent->curses_win); + + window_update_prompt(); +} + +static void sig_check_window_update(WINDOW_REC *window) +{ + if (window == active_win) + window_update_prompt(); +} + +static void read_settings(void) +{ + GSList *tmp; + + SIGNAL_FUNC funcs[] = { + (SIGNAL_FUNC) window_update_prompt, + (SIGNAL_FUNC) window_update_prompt_server, + (SIGNAL_FUNC) window_update_prompt_window, + (SIGNAL_FUNC) window_update_prompt_window_item + }; + + if (prompt != NULL) { + special_vars_remove_signals(prompt, 4, funcs); + special_vars_remove_signals(prompt_window, 4, funcs); + g_free(prompt); + g_free(prompt_window); + } + prompt = g_strdup(settings_get_str("prompt")); + prompt_window = g_strdup(settings_get_str("prompt_window")); + + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + textbuffer_view_set_default_indent(WINDOW_GUI(rec)->view, + settings_get_int("indent"), + settings_get_bool("indent_always")); + } + + special_vars_add_signals(prompt, 4, funcs); + special_vars_add_signals(prompt_window, 4, funcs); + + if (active_win != NULL) window_update_prompt(); +} + +void gui_windows_init(void) +{ + settings_add_bool("lookandfeel", "autostick_split_windows", TRUE); + settings_add_int("lookandfeel", "indent", 10); + settings_add_bool("lookandfeel", "indent_always", FALSE); + settings_add_str("lookandfeel", "prompt", "[$[.15]T] "); + settings_add_str("lookandfeel", "prompt_window", "[$winname] "); + + prompt = NULL; prompt_window = NULL; + window_create_override = -1; + + read_settings(); + signal_add("gui window create override", (SIGNAL_FUNC) sig_window_create_override); + signal_add("window created", (SIGNAL_FUNC) gui_window_created); + signal_add("window destroyed", (SIGNAL_FUNC) gui_window_destroyed); + signal_add_first("window changed", (SIGNAL_FUNC) signal_window_changed); + signal_add("window item remove", (SIGNAL_FUNC) sig_check_window_update); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); +} + +void gui_windows_deinit(void) +{ + g_free_not_null(prompt); + g_free_not_null(prompt_window); + + while (windows != NULL) + window_destroy(windows->data); + + signal_remove("gui window create override", (SIGNAL_FUNC) sig_window_create_override); + signal_remove("window created", (SIGNAL_FUNC) gui_window_created); + signal_remove("window destroyed", (SIGNAL_FUNC) gui_window_destroyed); + signal_remove("window changed", (SIGNAL_FUNC) signal_window_changed); + signal_remove("window item remove", (SIGNAL_FUNC) sig_check_window_update); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); +} diff --git a/apps/irssi/src/fe-text/gui-windows.h b/apps/irssi/src/fe-text/gui-windows.h new file mode 100644 index 00000000..fb94b46f --- /dev/null +++ b/apps/irssi/src/fe-text/gui-windows.h @@ -0,0 +1,36 @@ +#ifndef __GUI_WINDOWS_H +#define __GUI_WINDOWS_H + +#include "mainwindows.h" +#include "textbuffer-view.h" + +#define WINDOW_GUI(a) ((GUI_WINDOW_REC *) ((a)->gui_data)) + +#define is_window_visible(win) \ + (WINDOW_GUI(win)->parent->active == (win)) + +typedef struct { + MAIN_WINDOW_REC *parent; + TEXT_BUFFER_VIEW_REC *view; + + unsigned int use_insert_after:1; + LINE_REC *insert_after; +} GUI_WINDOW_REC; + +void gui_windows_init(void); +void gui_windows_deinit(void); + +WINDOW_REC *gui_window_create(MAIN_WINDOW_REC *parent); + +void gui_window_resize(WINDOW_REC *window, int width, int height); +void gui_window_reparent(WINDOW_REC *window, MAIN_WINDOW_REC *parent); + +#define gui_window_redraw(window) \ + textbuffer_view_redraw(WINDOW_GUI(window)->view) + +void gui_window_scroll(WINDOW_REC *window, int lines); +void gui_window_scroll_line(WINDOW_REC *window, LINE_REC *line); + +void window_update_prompt(void); + +#endif diff --git a/apps/irssi/src/fe-text/lastlog.c b/apps/irssi/src/fe-text/lastlog.c new file mode 100644 index 00000000..75e68da3 --- /dev/null +++ b/apps/irssi/src/fe-text/lastlog.c @@ -0,0 +1,267 @@ +/* + lastlog.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "commands.h" +#include "misc.h" +#include "levels.h" +#include "settings.h" + +#include "module-formats.h" +#include "printtext.h" + +#include "gui-windows.h" +#include "gui-printtext.h" + +#define MAX_LINES_WITHOUT_FORCE 1000 + +static void window_lastlog_clear(WINDOW_REC *window) +{ + TEXT_BUFFER_VIEW_REC *view; + GList *tmp, *next; + + screen_refresh_freeze(); + view = WINDOW_GUI(window)->view; + for (tmp = textbuffer_view_get_lines(view); tmp != NULL; tmp = next) { + LINE_REC *line = tmp->data; + + next = tmp->next; + if (line->info.level & MSGLEVEL_LASTLOG) + textbuffer_view_remove_line(view, line); + } + textbuffer_view_redraw(view); + screen_refresh_thaw(); +} + +/* Only unknown keys in `optlist' should be levels. + Returns -1 if unknown option was given. */ +int cmd_options_get_level(const char *cmd, GHashTable *optlist) +{ + GSList *list, *tmp, *next; + int level, retlevel; + + /* get all the options, then remove the known ones. there should + be only one left - the server tag. */ + list = hashtable_get_keys(optlist); + if (cmd != NULL) { + for (tmp = list; tmp != NULL; tmp = next) { + char *option = tmp->data; + next = tmp->next; + + if (command_have_option(cmd, option)) + list = g_slist_remove(list, option); + } + } + + retlevel = 0; + while (list != NULL) { + level = level_get(list->data); + if (level == 0) { + /* unknown option */ + signal_emit("error command", 2, + GINT_TO_POINTER(CMDERR_OPTION_UNKNOWN), + list->data); + retlevel = -1; + break; + } + + retlevel |= level; + list = g_slist_remove(list, list->data); + } + + return retlevel; +} + +static void show_lastlog(const char *searchtext, GHashTable *optlist, + int start, int count, int fhandle) +{ + WINDOW_REC *window; + LINE_REC *startline; + GList *list, *tmp; + GString *line; + char *str; + int level, len; + + level = cmd_options_get_level("lastlog", optlist); + if (level == -1) return; /* error in options */ + if (level == 0) level = MSGLEVEL_ALL; + + if (g_hash_table_lookup(optlist, "clear") != NULL) { + window_lastlog_clear(active_win); + if (*searchtext == '\0') + return; + } + + /* which window's lastlog to look at? */ + window = active_win; + str = g_hash_table_lookup(optlist, "window"); + if (str != NULL) { + window = is_numeric(str, '\0') ? + window_find_refnum(atoi(str)) : + window_find_item(NULL, str); + if (window == NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + TXT_REFNUM_NOT_FOUND, str); + return; + } + } + + if (g_hash_table_lookup(optlist, "new") != NULL) + startline = textbuffer_view_get_bookmark(WINDOW_GUI(window)->view, "lastlog_last_check"); + else if (g_hash_table_lookup(optlist, "away") != NULL) + startline = textbuffer_view_get_bookmark(WINDOW_GUI(window)->view, "lastlog_last_away"); + else + startline = NULL; + + if (startline == NULL) { + list = textbuffer_view_get_lines(WINDOW_GUI(window)->view); + startline = list == NULL ? NULL : list->data; + } + + list = textbuffer_find_text(WINDOW_GUI(window)->view->buffer, startline, + level, MSGLEVEL_LASTLOG, + searchtext, + g_hash_table_lookup(optlist, "regexp") != NULL, + g_hash_table_lookup(optlist, "word") != NULL, + g_hash_table_lookup(optlist, "case") != NULL); + + len = g_list_length(list); + if (count <= 0) + tmp = list; + else { + int pos = len-count; + + if (pos < 0) pos = 0; + pos += start; + + tmp = pos > len ? NULL : g_list_nth(list, pos); + len = g_list_length(tmp); + } + + if (len > MAX_LINES_WITHOUT_FORCE && fhandle == -1 && + g_hash_table_lookup(optlist, "force") == NULL) { + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_LASTLOG_TOO_LONG, len); + g_list_free(list); + return; + } + + if (fhandle == -1 && g_hash_table_lookup(optlist, "-") == NULL) + printformat(NULL, NULL, MSGLEVEL_LASTLOG, TXT_LASTLOG_START); + + line = g_string_new(NULL); + while (tmp != NULL && (count < 0 || count > 0)) { + LINE_REC *rec = tmp->data; + + /* get the line text */ + textbuffer_line2text(rec, fhandle == -1, line); + if (!settings_get_bool("timestamps")) { + struct tm *tm = localtime(&rec->info.time); + char timestamp[10]; + + g_snprintf(timestamp, sizeof(timestamp), + "%02d:%02d ", + tm->tm_hour, tm->tm_min); + g_string_prepend(line, timestamp); + } + + /* write to file/window */ + if (fhandle != -1) { + write(fhandle, line->str, line->len); + write(fhandle, "\n", 1); + } else { + printtext_window(active_win, MSGLEVEL_LASTLOG, + "%s", line->str); + } + + count--; + tmp = tmp->next; + } + g_string_free(line, TRUE); + + if (fhandle == -1 && g_hash_table_lookup(optlist, "-") == NULL) + printformat(NULL, NULL, MSGLEVEL_LASTLOG, TXT_LASTLOG_END); + + textbuffer_view_set_bookmark_bottom(WINDOW_GUI(window)->view, + "lastlog_last_check"); + + textbuffer_line_unref_list(WINDOW_GUI(window)->view->buffer, list); + g_list_free(list); +} + +/* SYNTAX: LASTLOG [-] [-file ] [-clear] [- -] + [-new | -away] [-regexp | -word] [-case] + [-window ] [] [ []] */ +static void cmd_lastlog(const char *data) +{ + GHashTable *optlist; + char *text, *countstr, *start, *fname; + void *free_arg; + int count, fhandle; + + g_return_if_fail(data != NULL); + + if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS | PARAM_FLAG_UNKNOWN_OPTIONS, + "lastlog", &optlist, &text, &countstr, &start)) + return; + + if (*start == '\0' && is_numeric(text, 0)) { + if (is_numeric(countstr, 0)) + start = countstr; + countstr = text; + text = ""; + } + count = atoi(countstr); + if (count == 0) count = -1; + + /* target where to print it */ + fhandle = -1; + fname = g_hash_table_lookup(optlist, "file"); + if (fname != NULL) { + fname = convert_home(fname); + fhandle = open(fname, O_WRONLY | O_APPEND | O_CREAT, + octal2dec(settings_get_int("log_create_mode"))); + g_free(fname); + } + + if (fname != NULL && fhandle == -1) { + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, + "%s", g_strerror(errno)); + } else { + show_lastlog(text, optlist, atoi(start), count, fhandle); + if (fhandle != -1) + close(fhandle); + } + + cmd_params_free(free_arg); +} + +void lastlog_init(void) +{ + command_bind("lastlog", NULL, (SIGNAL_FUNC) cmd_lastlog); + + command_set_options("lastlog", "!- force clear -file -window new away word regexp case"); +} + +void lastlog_deinit(void) +{ + command_unbind("lastlog", (SIGNAL_FUNC) cmd_lastlog); +} diff --git a/apps/irssi/src/fe-text/mainwindow-activity.c b/apps/irssi/src/fe-text/mainwindow-activity.c new file mode 100644 index 00000000..4301d981 --- /dev/null +++ b/apps/irssi/src/fe-text/mainwindow-activity.c @@ -0,0 +1,57 @@ +/* + mainwindow-activity.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" + +#include "gui-windows.h" + +/* Don't send window activity if window is already visible in + another mainwindow */ +static void sig_activity(WINDOW_REC *window) +{ + GSList *tmp; + + if (!is_window_visible(window) || window->data_level == 0) + return; + + window->data_level = 0; + window->hilight_color = 0; + + for (tmp = window->items; tmp != NULL; tmp = tmp->next) { + WI_ITEM_REC *item = tmp->data; + + item->data_level = 0; + item->hilight_color = 0; + } + signal_stop(); +} + +void mainwindow_activity_init(void) +{ + signal_add_first("window hilight", (SIGNAL_FUNC) sig_activity); + signal_add_first("window activity", (SIGNAL_FUNC) sig_activity); +} + +void mainwindow_activity_deinit(void) +{ + signal_remove("window hilight", (SIGNAL_FUNC) sig_activity); + signal_remove("window activity", (SIGNAL_FUNC) sig_activity); +} diff --git a/apps/irssi/src/fe-text/mainwindows-save.c b/apps/irssi/src/fe-text/mainwindows-save.c new file mode 100644 index 00000000..113037b1 --- /dev/null +++ b/apps/irssi/src/fe-text/mainwindows-save.c @@ -0,0 +1,195 @@ +/* + mainwindows-save.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "misc.h" +#include "lib-config/iconfig.h" +#include "settings.h" + +#include "mainwindows.h" +#include "gui-windows.h" + +static void main_window_save(MAIN_WINDOW_REC *window, CONFIG_NODE *node) +{ + GSList *tmp; + GString *str; + char num[MAX_INT_STRLEN]; + + ltoa(num, window->active->refnum); + node = config_node_section(node, num, NODE_TYPE_BLOCK); + + iconfig_node_set_int(node, "first_line", window->first_line); + iconfig_node_set_int(node, "lines", window->height); + + str = g_string_new(NULL); + for (tmp = window->sticky_windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + g_string_sprintfa(str, "%d ", rec->refnum); + } + if (str->len > 1) { + g_string_truncate(str, str->len-1); + iconfig_node_set_str(node, "sticky", str->str); + } + g_string_free(str, TRUE); +} + +static void sig_windows_saved(void) +{ + CONFIG_NODE *node; + + iconfig_set_str(NULL, "mainwindows", NULL); + node = iconfig_node_traverse("mainwindows", TRUE); + + g_slist_foreach(mainwindows, (GFunc) main_window_save, node); +} + +static int window_node_cmp(CONFIG_NODE *n1, CONFIG_NODE *n2) +{ + return config_node_get_int(n1, "first_line", 0) < + config_node_get_int(n2, "first_line", 0) ? -1 : 1; +} + +static GSList *read_sorted_windows(CONFIG_NODE *node) +{ + GSList *tmp, *output; + + output = NULL; + for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + output = g_slist_insert_sorted(output, tmp->data, + (GCompareFunc) window_node_cmp); + } + + return output; +} + +static void restore_sticky_windows(CONFIG_NODE *node, + MAIN_WINDOW_REC *mainwindow) +{ + WINDOW_REC *window; + char **sticky_list, **sticky; + + sticky_list = g_strsplit(config_node_get_str(node, "sticky", ""), " ", -1); + for (sticky = sticky_list; *sticky != NULL; sticky++) { + window = window_find_refnum(atoi(*sticky)); + if (window != NULL) { + mainwindow->sticky_windows = + g_slist_append(mainwindow->sticky_windows, + window); + } + } + g_strfreev(sticky_list); +} + +static WINDOW_REC *window_find_hidden(void) +{ + GSList *tmp; + + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + if (!is_window_visible(rec)) + return rec; + } + + return NULL; +} + +static void sig_windows_restored(void) +{ + MAIN_WINDOW_REC *mainwindow, *lowerwin; + WINDOW_REC *window; + CONFIG_NODE *node; + GSList *tmp, *tmp2, *sorted_windows, *sorted_config; + int count, newsize; + + node = iconfig_node_traverse("mainwindows", FALSE); + if (node == NULL) return; + + /* create all windows, shrink the lower windows to minimum size */ + lowerwin = mainwindows->data; + count = g_slist_length(node->value); + while (count > 1) { + window = window_find_hidden(); + if (window == NULL) + break; + + mainwindow = mainwindow_create(); + if (mainwindow == NULL) + break; + + mainwindow->active = window; + WINDOW_GUI(window)->parent = mainwindow; + + active_mainwin = NULL; + window_set_active(window); + + if (lowerwin->height > WINDOW_MIN_SIZE) + mainwindow_set_size(lowerwin, WINDOW_MIN_SIZE); + count--; + + lowerwin = mainwindow; + } + + sorted_config = read_sorted_windows(node); + sorted_windows = mainwindows_get_sorted(FALSE); + for (tmp = sorted_windows, tmp2 = sorted_config; + tmp != NULL && tmp2 != NULL; + tmp = tmp->next, tmp2 = tmp2->next) { + MAIN_WINDOW_REC *mainwindow = tmp->data; + CONFIG_NODE *node = tmp2->data; + + window = window_find_refnum(atoi(node->key)); + if (window == NULL) { + mainwindow_destroy(mainwindow); + continue; + } + + if (is_window_visible(window)) { + active_mainwin = WINDOW_GUI(window)->parent; + window_set_active(window_find_hidden()); + } + + active_mainwin = mainwindow; + window_set_active(window); + + restore_sticky_windows(node, mainwindow); + + newsize = config_node_get_int(node, "lines", 0); + if (newsize > 0) + mainwindow_set_size(mainwindow, newsize); + } + g_slist_free(sorted_windows); + g_slist_free(sorted_config); + + irssi_redraw(); +} + +void mainwindows_save_init(void) +{ + signal_add("windows saved", (SIGNAL_FUNC) sig_windows_saved); + signal_add("windows restored", (SIGNAL_FUNC) sig_windows_restored); +} + +void mainwindows_save_deinit(void) +{ + signal_remove("windows saved", (SIGNAL_FUNC) sig_windows_saved); + signal_remove("windows restored", (SIGNAL_FUNC) sig_windows_restored); +} diff --git a/apps/irssi/src/fe-text/mainwindows.c b/apps/irssi/src/fe-text/mainwindows.c new file mode 100644 index 00000000..d727464e --- /dev/null +++ b/apps/irssi/src/fe-text/mainwindows.c @@ -0,0 +1,909 @@ +/* + mainwindows.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "module-formats.h" +#include "signals.h" +#include "commands.h" +#include "levels.h" +#include "misc.h" +#include "settings.h" +#include "printtext.h" + +#include "screen.h" +#include "statusbar.h" +#include "gui-windows.h" + +#define NEW_WINDOW_SIZE (WINDOW_MIN_SIZE + 1) + +GSList *mainwindows; +MAIN_WINDOW_REC *active_mainwin; + +static int reserved_up, reserved_down; +static int screen_width, screen_height; + +static MAIN_WINDOW_REC *find_window_with_room(void) +{ + MAIN_WINDOW_REC *biggest_rec; + GSList *tmp; + int space, biggest; + + biggest = 0; biggest_rec = NULL; + for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { + MAIN_WINDOW_REC *rec = tmp->data; + + space = rec->height; + if (space >= WINDOW_MIN_SIZE+NEW_WINDOW_SIZE && space > biggest) { + biggest = space; + biggest_rec = rec; + } + } + + return biggest_rec; +} + +#ifdef USE_CURSES_WINDOWS +static void create_curses_window(MAIN_WINDOW_REC *window) +{ + window->curses_win = newwin(window->height, window->width, + window->first_line, 0); + idlok(window->curses_win, 1); +} +#endif + +static void mainwindow_resize(MAIN_WINDOW_REC *window, int xdiff, int ydiff) +{ + GSList *tmp; + + if (xdiff == 0 && ydiff == 0) + return; + + window->width += xdiff; + window->height = window->last_line-window->first_line+1; +#ifdef USE_CURSES_WINDOWS +#ifdef HAVE_CURSES_WRESIZE + wresize(window->curses_win, window->height, window->width); + mvwin(window->curses_win, window->first_line, 0); +#else + delwin(window->curses_win); + create_curses_window(window); +#endif +#endif + + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + if (rec->gui_data != NULL && + WINDOW_GUI(rec)->parent == window) + gui_window_resize(rec, window->width, window->height); + } + + textbuffer_view_set_window(WINDOW_GUI(window->active)->view, + window->curses_win); + signal_emit("mainwindow resized", 1, window); +} + +void mainwindows_recreate(void) +{ + GSList *tmp; + + for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { + MAIN_WINDOW_REC *rec = tmp->data; + +#ifdef USE_CURSES_WINDOWS + create_curses_window(rec); +#endif + textbuffer_view_set_window(WINDOW_GUI(rec->active)->view, + rec->curses_win); + } +} + +MAIN_WINDOW_REC *mainwindow_create(void) +{ + MAIN_WINDOW_REC *rec, *parent; + int space; + + rec = g_new0(MAIN_WINDOW_REC, 1); + rec->width = screen_width; + rec->statusbar_lines = 1; + + if (mainwindows == NULL) { + active_mainwin = rec; + + rec->first_line = reserved_up; + rec->last_line = screen_height-1 - + reserved_down-rec->statusbar_lines; + rec->height = rec->last_line-rec->first_line+1; + } else { + parent = WINDOW_GUI(active_win)->parent; + if (parent->height < WINDOW_MIN_SIZE+NEW_WINDOW_SIZE) + parent = find_window_with_room(); + if (parent == NULL) + return NULL; /* not enough space */ + + space = (parent->height-parent->statusbar_lines)/2; + rec->first_line = parent->first_line; + rec->last_line = rec->first_line + space-rec->statusbar_lines; + rec->height = rec->last_line-rec->first_line+1; + parent->first_line = rec->last_line+1+rec->statusbar_lines; + parent->height = parent->last_line-parent->first_line+1; + + mainwindow_resize(parent, 0, -space-1); + } + +#ifdef USE_CURSES_WINDOWS + rec->curses_win = newwin(rec->height, rec->width, rec->first_line, 0); + refresh(); +#endif + + mainwindows = g_slist_append(mainwindows, rec); + signal_emit("mainwindow created", 1, rec); + return rec; +} + +static MAIN_WINDOW_REC *mainwindows_find_lower(int line) +{ + MAIN_WINDOW_REC *best; + GSList *tmp; + + best = NULL; + for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { + MAIN_WINDOW_REC *rec = tmp->data; + + if (rec->first_line > line && + (best == NULL || rec->first_line < best->first_line)) + best = rec; + } + + return best; +} + +static MAIN_WINDOW_REC *mainwindows_find_upper(int line) +{ + MAIN_WINDOW_REC *best; + GSList *tmp; + + best = NULL; + for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { + MAIN_WINDOW_REC *rec = tmp->data; + + if (rec->last_line < line && + (best == NULL || rec->last_line > best->last_line)) + best = rec; + } + + return best; +} + +static void mainwindows_add_space(int first_line, int last_line) +{ + MAIN_WINDOW_REC *rec; + int size; + + if (last_line < first_line) + return; + + size = last_line-first_line+1; + + rec = mainwindows_find_lower(last_line); + if (rec != NULL) { + rec->first_line = first_line; + mainwindow_resize(rec, 0, size); + return; + } + + rec = mainwindows_find_upper(first_line); + if (rec != NULL) { + rec->last_line = last_line-rec->statusbar_lines; + mainwindow_resize(rec, 0, size); + } +} + +static void gui_windows_remove_parent(MAIN_WINDOW_REC *window) +{ + MAIN_WINDOW_REC *new_parent; + GSList *tmp; + + new_parent = mainwindows->data; + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + if (rec->gui_data != NULL && WINDOW_GUI(rec)->parent == window) + gui_window_reparent(rec, new_parent); + } +} + +void mainwindow_destroy(MAIN_WINDOW_REC *window) +{ + g_return_if_fail(window != NULL); + +#ifdef USE_CURSES_WINDOWS + delwin(window->curses_win); +#endif + + mainwindows = g_slist_remove(mainwindows, window); + signal_emit("mainwindow destroyed", 1, window); + + if (!quitting && mainwindows != NULL) { + gui_windows_remove_parent(window); + mainwindows_add_space(window->first_line, window->last_line+window->statusbar_lines); + + mainwindows_redraw(); + statusbar_redraw(NULL); + } + g_free(window); + + if (active_mainwin == window) active_mainwin = NULL; +} + +void mainwindows_redraw(void) +{ + GSList *tmp; + + screen_refresh_freeze(); + for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { + MAIN_WINDOW_REC *rec = tmp->data; + + gui_window_redraw(rec->active); + } + screen_refresh_thaw(); +} + +static int mainwindows_compare(MAIN_WINDOW_REC *w1, MAIN_WINDOW_REC *w2) +{ + return w1->first_line < w2->first_line ? -1 : 1; +} + +static int mainwindows_compare_reverse(MAIN_WINDOW_REC *w1, MAIN_WINDOW_REC *w2) +{ + return w1->first_line < w2->first_line ? 1 : -1; +} + +GSList *mainwindows_get_sorted(int reverse) +{ + GSList *tmp, *list; + + list = NULL; + for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { + list = g_slist_insert_sorted(list, tmp->data, (GCompareFunc) + (reverse ? mainwindows_compare_reverse : mainwindows_compare)); + } + + return list; +} + +static void mainwindows_resize_too_small(int xdiff, int ydiff) +{ + GSList *sorted, *tmp; + int space, moved; + + /* terminal is too small - just take the space whereever possible */ + sorted = mainwindows_get_sorted(FALSE); + moved = 0; + for (tmp = sorted; tmp != NULL; tmp = tmp->next) { + MAIN_WINDOW_REC *rec = tmp->data; + + space = rec->height; + if (ydiff == 0 || space <= 0) { + if (moved > 0) { + rec->first_line -= moved; + rec->last_line -= moved; + signal_emit("mainwindow moved", 1, rec); + } + continue; + } + + if (space > -ydiff) space = -ydiff; + ydiff += space; + rec->first_line -= moved; + moved += space; + rec->last_line -= space; + mainwindow_resize(rec, xdiff, -space); + } + g_slist_free(sorted); +} + +static void mainwindows_resize_smaller(int xdiff, int ydiff) +{ + GSList *sorted, *tmp; + int space; + + space = 0; + for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { + MAIN_WINDOW_REC *rec = tmp->data; + + space += rec->height-WINDOW_MIN_SIZE; + } + + if (space < -ydiff) { + /* not enough space, use different algorithm */ + mainwindows_resize_too_small(xdiff, ydiff); + return; + } + + /* resize windows that have space */ + sorted = mainwindows_get_sorted(TRUE); + for (tmp = sorted; tmp != NULL && ydiff < 0; tmp = tmp->next) { + MAIN_WINDOW_REC *rec = tmp->data; + + space = rec->height-WINDOW_MIN_SIZE; + if (space <= 0) { + rec->first_line += ydiff; + rec->last_line += ydiff; + signal_emit("mainwindow moved", 1, rec); + continue; + } + + if (space <= 0) space = 1; + if (space > -ydiff) space = -ydiff; + rec->last_line += ydiff; + ydiff += space; + rec->first_line += ydiff; + + mainwindow_resize(rec, xdiff, -space); + } + g_slist_free(sorted); +} + +static void mainwindows_resize_bigger(int xdiff, int ydiff) +{ + GSList *sorted, *tmp; + int moved, space; + + sorted = mainwindows_get_sorted(FALSE); + moved = 0; + for (tmp = sorted; tmp != NULL; tmp = tmp->next) { + MAIN_WINDOW_REC *rec = tmp->data; + + space = rec->height-WINDOW_MIN_SIZE; + if (ydiff == 0 || (space >= 0 && tmp->next != NULL)) { + if (moved > 0) { + rec->first_line += moved; + rec->last_line += moved; + signal_emit("mainwindow moved", 1, rec); + } + continue; + } + + if (space < 0 && tmp->next != NULL) { + /* space below minimum */ + space = -space; + if (space > ydiff) space = ydiff; + } else { + /* lowest window - give all the extra space for it */ + space = ydiff; + } + ydiff -= space; + rec->first_line += moved; + moved += space; + rec->last_line += moved; + + mainwindow_resize(rec, xdiff, space); + } + g_slist_free(sorted); +} + +void mainwindows_resize_horiz(int xdiff) +{ + GSList *tmp; + + for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { + MAIN_WINDOW_REC *rec = tmp->data; + + mainwindow_resize(rec, xdiff, 0); + } +} + +void mainwindows_resize(int width, int height) +{ + int xdiff, ydiff; + + xdiff = width-screen_width; + ydiff = height-screen_height; + screen_width = width; + screen_height = height; + + screen_refresh_freeze(); + if (ydiff < 0) + mainwindows_resize_smaller(xdiff, ydiff); + else if (ydiff > 0) + mainwindows_resize_bigger(xdiff, ydiff); + else if (xdiff != 0) + mainwindows_resize_horiz(xdiff); + + irssi_redraw(); + screen_refresh_thaw(); +} + +int mainwindows_reserve_lines(int count, int up) +{ + MAIN_WINDOW_REC *window; + int ret; + + if (up) { + g_return_val_if_fail(count > 0 || reserved_up > count, -1); + + ret = reserved_up; + reserved_up += count; + + window = mainwindows_find_lower(-1); + if (window != NULL) window->first_line += count; + } else { + g_return_val_if_fail(count > 0 || reserved_down > count, -1); + + ret = reserved_down; + reserved_down += count; + + window = mainwindows_find_upper(screen_height); + if (window != NULL) window->last_line -= count; + } + + if (window != NULL) + mainwindow_resize(window, 0, -count); + + return ret; +} + +static void mainwindows_resize_two(MAIN_WINDOW_REC *grow_win, + MAIN_WINDOW_REC *shrink_win, int count) +{ + mainwindow_resize(grow_win, 0, count); + mainwindow_resize(shrink_win, 0, -count); + gui_window_redraw(grow_win->active); + gui_window_redraw(shrink_win->active); + statusbar_redraw(grow_win->statusbar); + statusbar_redraw(shrink_win->statusbar); +} + +static int mainwindow_grow(MAIN_WINDOW_REC *window, int count) +{ + MAIN_WINDOW_REC *shrink_win; + + /* shrink lower window */ + shrink_win = mainwindows_find_lower(window->last_line); + if (shrink_win != NULL && shrink_win->height-count >= WINDOW_MIN_SIZE) { + window->last_line += count; + shrink_win->first_line += count; + } else { + /* shrink upper window */ + shrink_win = mainwindows_find_upper(window->first_line); + if (shrink_win == NULL || + shrink_win->height-count < WINDOW_MIN_SIZE) + return FALSE; + + window->first_line -= count; + shrink_win->last_line -= count; + } + + mainwindows_resize_two(window, shrink_win, count); + return TRUE; +} + +static int mainwindow_shrink(MAIN_WINDOW_REC *window, int count) +{ + MAIN_WINDOW_REC *grow_win; + + if (window->height-count < WINDOW_MIN_SIZE) + return FALSE; + + grow_win = mainwindows_find_lower(window->last_line); + if (grow_win != NULL) { + window->last_line -= count; + grow_win->first_line -= count; + } else { + grow_win = mainwindows_find_upper(window->first_line); + if (grow_win == NULL) return FALSE; + + window->first_line += count; + grow_win->last_line += count; + } + + mainwindows_resize_two(grow_win, window, count); + return TRUE; +} + +void mainwindow_set_size(MAIN_WINDOW_REC *window, int size) +{ + size -= window->height; + if (size < 0) + mainwindow_shrink(window, size); + else + mainwindow_grow(window, size); +} + +/* SYNTAX: WINDOW GROW [] */ +static void cmd_window_grow(const char *data) +{ + MAIN_WINDOW_REC *window; + int count; + + count = *data == '\0' ? 1 : atoi(data); + window = WINDOW_GUI(active_win)->parent; + + if (!mainwindow_grow(window, count)) { + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_WINDOW_TOO_SMALL); + } +} + +/* SYNTAX: WINDOW SHRINK [] */ +static void cmd_window_shrink(const char *data) +{ + MAIN_WINDOW_REC *window; + int count; + + count = *data == '\0' ? 1 : atoi(data); + window = WINDOW_GUI(active_win)->parent; + + if (!mainwindow_shrink(window, count)) { + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_WINDOW_TOO_SMALL); + } +} + +/* SYNTAX: WINDOW SIZE */ +static void cmd_window_size(const char *data) +{ + char sizestr[MAX_INT_STRLEN]; + int size; + + if (!is_numeric(data, 0)) return; + size = atoi(data); + + size -= WINDOW_GUI(active_win)->parent->height; + if (size == 0) return; + + ltoa(sizestr, size < 0 ? -size : size); + if (size < 0) + cmd_window_shrink(sizestr); + else + cmd_window_grow(sizestr); +} + +/* SYNTAX: WINDOW BALANCE */ +static void cmd_window_balance(void) +{ + GSList *sorted, *tmp; + int avail_size, unit_size, bigger_units; + int windows, last_line, old_size; + + windows = g_slist_length(mainwindows); + if (windows == 1) return; + + avail_size = screen_height - reserved_up-reserved_down; + unit_size = avail_size/windows; + bigger_units = avail_size%windows; + + sorted = mainwindows_get_sorted(FALSE); + last_line = 0; + for (tmp = sorted; tmp != NULL; tmp = tmp->next) { + MAIN_WINDOW_REC *rec = tmp->data; + + old_size = rec->height; + rec->first_line = last_line+1; + rec->last_line = rec->first_line-1 + unit_size - + rec->statusbar_lines; + rec->height = rec->last_line-rec->first_line+1; + + if (bigger_units > 0) { + rec->last_line++; + bigger_units--; + } + last_line = rec->last_line + rec->statusbar_lines; + + mainwindow_resize(rec, 0, rec->height-old_size); + } + g_slist_free(sorted); + + mainwindows_redraw(); + statusbar_redraw(NULL); +} + +/* SYNTAX: WINDOW HIDE [|] */ +static void cmd_window_hide(const char *data) +{ + WINDOW_REC *window; + + if (mainwindows->next == NULL) { + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_CANT_HIDE_LAST); + return; + } + + if (*data == '\0') + window = active_win; + else if (is_numeric(data, 0)) { + window = window_find_refnum(atoi(data)); + if (window == NULL) { + printformat_window(active_win, MSGLEVEL_CLIENTERROR, + TXT_REFNUM_NOT_FOUND, data); + } + } else { + window = window_find_item(active_win->active_server, data); + } + + if (window == NULL || !is_window_visible(window)) + return; + + if (WINDOW_GUI(window)->parent->sticky_windows != NULL) { + printformat_window(active_win, MSGLEVEL_CLIENTERROR, + TXT_CANT_HIDE_STICKY_WINDOWS); + return; + } + + mainwindow_destroy(WINDOW_GUI(window)->parent); + + if (active_mainwin == NULL) { + active_mainwin = WINDOW_GUI(active_win)->parent; + window_set_active(active_mainwin->active); + } +} + +/* SYNTAX: WINDOW SHOW | */ +static void cmd_window_show(const char *data) +{ + MAIN_WINDOW_REC *parent; + WINDOW_REC *window; + + if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS); + + if (is_numeric(data, '\0')) { + window = window_find_refnum(atoi(data)); + if (window == NULL) { + printformat_window(active_win, MSGLEVEL_CLIENTERROR, + TXT_REFNUM_NOT_FOUND, data); + } + } else { + window = window_find_item(active_win->active_server, data); + } + + if (window == NULL || is_window_visible(window)) + return; + + if (WINDOW_GUI(window)->parent->sticky_windows != NULL) { + printformat_window(active_win, MSGLEVEL_CLIENTERROR, + TXT_CANT_SHOW_STICKY_WINDOWS); + return; + } + + parent = mainwindow_create(); + if (settings_get_bool("autostick_split_windows")) { + parent->sticky_windows = + g_slist_append(parent->sticky_windows, window); + } + parent->active = window; + gui_window_reparent(window, parent); + + active_mainwin = NULL; + window_set_active(window); +} + +/* SYNTAX: WINDOW UP */ +static void cmd_window_up(void) +{ + MAIN_WINDOW_REC *rec; + + rec = mainwindows_find_upper(active_mainwin->first_line); + if (rec != NULL) + window_set_active(rec->active); +} + +/* SYNTAX: WINDOW DOWN */ +static void cmd_window_down(void) +{ + MAIN_WINDOW_REC *rec; + + rec = mainwindows_find_lower(active_mainwin->last_line); + if (rec != NULL) + window_set_active(rec->active); +} + +/* SYNTAX: WINDOW LEFT */ +static void cmd_window_left(const char *data, SERVER_REC *server, void *item) +{ + MAIN_WINDOW_REC *parent; + WINDOW_REC *window; + int pos, num; + + window = NULL; + if (active_mainwin->sticky_windows == NULL) { + /* no sticky windows, go to previous non-sticky window */ + num = active_win->refnum; + do { + num = window_refnum_prev(num, TRUE); + if (num < 0) { + window = NULL; + break; + } + window = window_find_refnum(num); + parent = WINDOW_GUI(window)->parent; + } while (g_slist_find(parent->sticky_windows, window) != NULL); + } else { + pos = g_slist_index(active_mainwin->sticky_windows, + active_win); + if (pos > 0) { + window = g_slist_nth_data( + active_mainwin->sticky_windows, pos-1); + } else { + window = g_slist_last( + active_mainwin->sticky_windows)->data; + } + } + + if (window != NULL) + window_set_active(window); +} + +/* SYNTAX: WINDOW RIGHT */ +static void cmd_window_right(void) +{ + MAIN_WINDOW_REC *parent; + WINDOW_REC *window; + GSList *tmp; + int num; + + window = NULL; + if (active_mainwin->sticky_windows == NULL) { + /* no sticky windows, go to next non-sticky window */ + num = active_win->refnum; + do { + num = window_refnum_next(num, TRUE); + if (num < 0) { + window = NULL; + break; + } + window = window_find_refnum(num); + parent = WINDOW_GUI(window)->parent; + } while (g_slist_find(parent->sticky_windows, window) != NULL); + } else { + tmp = g_slist_find(active_mainwin->sticky_windows, active_win); + if (tmp != NULL) { + window = tmp->next != NULL ? tmp->next->data : + active_mainwin->sticky_windows->data; + } + } + + if (window != NULL) + window_set_active(window); +} + +static void mainwindow_change_window(MAIN_WINDOW_REC *mainwin, + WINDOW_REC *window) +{ + MAIN_WINDOW_REC *parent; + GSList *tmp; + + if (mainwin->sticky_windows != NULL) { + /* sticky window */ + window_set_active(mainwin->sticky_windows->data); + return; + } + + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + parent = WINDOW_GUI(rec)->parent; + if (rec != window && + g_slist_find(parent->sticky_windows, rec) == NULL) { + window_set_active(rec); + return; + } + } + + /* no more non-sticky windows, remove main window */ + mainwindow_destroy(mainwin); +} + +/* SYNTAX: WINDOW STICK [ON|OFF|] */ +static void cmd_window_stick(const char *data) +{ + MAIN_WINDOW_REC *window = active_mainwin; + + if (is_numeric(data, '\0')) { + WINDOW_REC *win = window_find_refnum(atoi(data)); + if (win == NULL) { + printformat_window(active_win, MSGLEVEL_CLIENTERROR, + TXT_REFNUM_NOT_FOUND, data); + return; + } + window = WINDOW_GUI(win)->parent; + } + + if (g_strncasecmp(data, "OF", 2) == 0 || toupper(*data) == 'N') { + /* unset sticky */ + if (g_slist_find(window->sticky_windows, active_win) == NULL) { + printformat_window(active_win, MSGLEVEL_CLIENTERROR, + TXT_WINDOW_NOT_STICKY); + } else { + window->sticky_windows = + g_slist_remove(window->sticky_windows, + active_win); + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_WINDOW_UNSET_STICKY); + } + } else { + /* set sticky */ + active_mainwin->sticky_windows = + g_slist_remove(active_mainwin->sticky_windows, + active_win); + + if (g_slist_find(window->sticky_windows, active_win) == NULL) { + window->sticky_windows = + g_slist_append(window->sticky_windows, + active_win); + } + if (window != active_mainwin) { + WINDOW_REC *movewin; + + movewin = active_win; + gui_window_reparent(movewin, window); + mainwindow_change_window(active_mainwin, movewin); + + active_mainwin = window; + window_set_active(movewin); + } + + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, + TXT_WINDOW_SET_STICKY); + } +} + +void mainwindows_init(void) +{ + screen_width = COLS; + screen_height = LINES; + + mainwindows = NULL; + active_mainwin = NULL; + reserved_up = reserved_down = 0; + + /* for entry line */ + mainwindows_reserve_lines(1, FALSE); + + command_bind("window grow", NULL, (SIGNAL_FUNC) cmd_window_grow); + command_bind("window shrink", NULL, (SIGNAL_FUNC) cmd_window_shrink); + command_bind("window size", NULL, (SIGNAL_FUNC) cmd_window_size); + command_bind("window balance", NULL, (SIGNAL_FUNC) cmd_window_balance); + command_bind("window hide", NULL, (SIGNAL_FUNC) cmd_window_hide); + command_bind("window show", NULL, (SIGNAL_FUNC) cmd_window_show); + command_bind("window up", NULL, (SIGNAL_FUNC) cmd_window_up); + command_bind("window down", NULL, (SIGNAL_FUNC) cmd_window_down); + command_bind("window left", NULL, (SIGNAL_FUNC) cmd_window_left); + command_bind("window right", NULL, (SIGNAL_FUNC) cmd_window_right); + command_bind("window stick", NULL, (SIGNAL_FUNC) cmd_window_stick); +} + +void mainwindows_deinit(void) +{ + while (mainwindows != NULL) + mainwindow_destroy(mainwindows->data); + + command_unbind("window grow", (SIGNAL_FUNC) cmd_window_grow); + command_unbind("window shrink", (SIGNAL_FUNC) cmd_window_shrink); + command_unbind("window size", (SIGNAL_FUNC) cmd_window_size); + command_unbind("window balance", (SIGNAL_FUNC) cmd_window_balance); + command_unbind("window hide", (SIGNAL_FUNC) cmd_window_hide); + command_unbind("window show", (SIGNAL_FUNC) cmd_window_show); + command_unbind("window up", (SIGNAL_FUNC) cmd_window_up); + command_unbind("window down", (SIGNAL_FUNC) cmd_window_down); + command_unbind("window left", (SIGNAL_FUNC) cmd_window_left); + command_unbind("window right", (SIGNAL_FUNC) cmd_window_right); + command_unbind("window stick", (SIGNAL_FUNC) cmd_window_stick); +} diff --git a/apps/irssi/src/fe-text/mainwindows.h b/apps/irssi/src/fe-text/mainwindows.h new file mode 100644 index 00000000..491449c6 --- /dev/null +++ b/apps/irssi/src/fe-text/mainwindows.h @@ -0,0 +1,42 @@ +#ifndef __MAINWINDOWS_H +#define __MAINWINDOWS_H + +#include "fe-windows.h" +#include "screen.h" + +#define WINDOW_MIN_SIZE 2 + +typedef struct { + WINDOW_REC *active; + GSList *sticky_windows; /* list of windows allowed to show only in this mainwindow */ + +#ifdef USE_CURSES_WINDOWS + WINDOW *curses_win; +#else +#error disable-curses-windows is currently broken /* FIXME */ +#endif + int first_line, last_line, width, height; + int statusbar_lines; + void *statusbar; + void *statusbar_window_item; +} MAIN_WINDOW_REC; + +extern GSList *mainwindows; +extern MAIN_WINDOW_REC *active_mainwin; + +void mainwindows_init(void); +void mainwindows_deinit(void); + +MAIN_WINDOW_REC *mainwindow_create(void); +void mainwindow_destroy(MAIN_WINDOW_REC *window); + +void mainwindows_redraw(void); +void mainwindows_recreate(void); + +void mainwindow_set_size(MAIN_WINDOW_REC *window, int size); +void mainwindows_resize(int width, int height); + +int mainwindows_reserve_lines(int count, int up); +GSList *mainwindows_get_sorted(int reverse); + +#endif diff --git a/apps/irssi/src/fe-text/module-formats.c b/apps/irssi/src/fe-text/module-formats.c new file mode 100644 index 00000000..be3c0eab --- /dev/null +++ b/apps/irssi/src/fe-text/module-formats.c @@ -0,0 +1,42 @@ +/* + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "formats.h" + +FORMAT_REC gui_text_formats[] = +{ + { MODULE_NAME, "Text user interface", 0 }, + + { "lastlog_too_long", "/LASTLOG would print $0 lines. If you really want to print all these lines use -force option.", 1, { 1 } }, + { "lastlog_start", "{hilight Lastlog}:", 0 }, + { "lastlog_end", "{hilight End of Lastlog}", 0 }, + + { "refnum_not_found", "Window number $0 not found", 1, { 0 } }, + { "window_too_small", "Not enough room to resize this window", 0 }, + { "cant_hide_last", "You can't hide the last window", 0 }, + { "cant_hide_sticky_windows", "You can't hide sticky windows (use /WINDOW STICK OFF)", 0 }, + { "cant_show_sticky_windows", "You can't show sticky windows (use /WINDOW STICK OFF)", 0 }, + { "window_not_sticky", "Window is not sticky", 0 }, + { "window_set_sticky", "Window set sticky", 0 }, + { "window_unset_sticky", "Window is not sticky anymore", 0 }, + + { NULL, NULL, 0 } +}; diff --git a/apps/irssi/src/fe-text/module-formats.h b/apps/irssi/src/fe-text/module-formats.h new file mode 100644 index 00000000..fe33c8e2 --- /dev/null +++ b/apps/irssi/src/fe-text/module-formats.h @@ -0,0 +1,20 @@ +#include "formats.h" + +enum { + TXT_MODULE_NAME, + + TXT_LASTLOG_TOO_LONG, + TXT_LASTLOG_START, + TXT_LASTLOG_END, + + TXT_REFNUM_NOT_FOUND, + TXT_WINDOW_TOO_SMALL, + TXT_CANT_HIDE_LAST, + TXT_CANT_HIDE_STICKY_WINDOWS, + TXT_CANT_SHOW_STICKY_WINDOWS, + TXT_WINDOW_NOT_STICKY, + TXT_WINDOW_SET_STICKY, + TXT_WINDOW_UNSET_STICKY +}; + +extern FORMAT_REC gui_text_formats[]; diff --git a/apps/irssi/src/fe-text/module.h b/apps/irssi/src/fe-text/module.h new file mode 100644 index 00000000..ba5888ad --- /dev/null +++ b/apps/irssi/src/fe-text/module.h @@ -0,0 +1,6 @@ +#include "common.h" + +#define MODULE_NAME "fe-text" + +extern int quitting; +void irssi_redraw(void); diff --git a/apps/irssi/src/fe-text/screen.c b/apps/irssi/src/fe-text/screen.c new file mode 100644 index 00000000..5378b5c9 --- /dev/null +++ b/apps/irssi/src/fe-text/screen.c @@ -0,0 +1,273 @@ +/* + screen.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "misc.h" +#include "settings.h" + +#include "screen.h" +#include "gui-readline.h" +#include "mainwindows.h" + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#include + +#ifndef COLOR_PAIRS +#define COLOR_PAIRS 64 +#endif + +#define MIN_SCREEN_WIDTH 20 + +static int scrx, scry; +static int use_colors; +static int freeze_refresh; + +static int init_screen_int(void); +static void deinit_screen_int(void); + +#ifdef SIGWINCH + +static void sig_winch(int p) +{ +#if defined (TIOCGWINSZ) && defined (HAVE_CURSES_RESIZETERM) + struct winsize ws; + + /* Get new window size */ + if (ioctl(0, TIOCGWINSZ, &ws) < 0) + return; + + if (ws.ws_row == LINES && ws.ws_col == COLS) { + /* Same size, abort. */ + return; + } + + if (ws.ws_col < MIN_SCREEN_WIDTH) + ws.ws_col = MIN_SCREEN_WIDTH; + + /* Resize curses terminal */ + resizeterm(ws.ws_row, ws.ws_col); +#else + deinit_screen_int(); + init_screen_int(); + mainwindows_recreate(); +#endif + + mainwindows_resize(COLS, LINES); +} +#endif + +static void read_signals(void) +{ +#ifndef WIN32 + int signals[] = { + SIGHUP, SIGINT, SIGQUIT, SIGTERM, + SIGALRM, SIGUSR1, SIGUSR2 + }; + char *signames[] = { + "hup", "int", "quit", "term", + "alrm", "usr1", "usr2" + }; + + const char *ignores; + struct sigaction act; + int n; + + ignores = settings_get_str("ignore_signals"); + + sigemptyset (&act.sa_mask); + act.sa_flags = 0; + + for (n = 0; n < sizeof(signals)/sizeof(signals[0]); n++) { + act.sa_handler = find_substr(ignores, signames[n]) ? + SIG_IGN : SIG_DFL; + sigaction(signals[n], &act, NULL); + } +#endif +} + +static void read_settings(void) +{ + int old_colors = use_colors; + + use_colors = settings_get_bool("colors"); + read_signals(); + if (use_colors && !has_colors()) + use_colors = FALSE; + + if (use_colors != old_colors) + irssi_redraw(); +} + +static int init_curses(void) +{ + char ansi_tab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + int num; +#ifndef WIN32 + struct sigaction act; +#endif + + if (!initscr()) + return FALSE; + + if (COLS < MIN_SCREEN_WIDTH) + COLS = MIN_SCREEN_WIDTH; + +#ifdef SIGWINCH + sigemptyset (&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = sig_winch; + sigaction(SIGWINCH, &act, NULL); +#endif + raw(); noecho(); idlok(stdscr, 1); +#ifdef HAVE_CURSES_IDCOK + idcok(stdscr, 1); +#endif + intrflush(stdscr, FALSE); nodelay(stdscr, TRUE); + + if (has_colors()) + start_color(); + else if (use_colors) + use_colors = FALSE; + +#ifdef HAVE_NCURSES_USE_DEFAULT_COLORS + /* this lets us to use the "default" background color for colors <= 7 so + background pixmaps etc. show up right */ + use_default_colors(); + + for (num = 1; num < COLOR_PAIRS; num++) + init_pair(num, ansi_tab[num & 7], num <= 7 ? -1 : ansi_tab[num >> 3]); + + init_pair(63, 0, -1); /* hm.. not THAT good idea, but probably more + people want dark grey than white on white.. */ +#else + for (num = 1; num < COLOR_PAIRS; num++) + init_pair(num, ansi_tab[num & 7], ansi_tab[num >> 3]); + init_pair(63, 0, 0); +#endif + + clear(); + return TRUE; +} + +static int init_screen_int(void) +{ + use_colors = settings_get_bool("colors"); + read_signals(); + + scrx = scry = 0; + freeze_refresh = 0; + + return init_curses(); +} + +static void deinit_screen_int(void) +{ + endwin(); +} + +/* Initialize screen, detect screen length */ +int init_screen(void) +{ + settings_add_bool("lookandfeel", "colors", TRUE); + settings_add_str("misc", "ignore_signals", ""); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); + + return init_screen_int(); +} + +/* Deinitialize screen */ +void deinit_screen(void) +{ + deinit_screen_int(); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); +} + +void set_color(WINDOW *window, int col) +{ + int attr; + + if (!use_colors) + attr = (col & 0x70) ? A_REVERSE : 0; + else if (col & ATTR_COLOR8) + attr = (A_DIM | COLOR_PAIR(63)); + else if ((col & 0x77) == 0) + attr = A_NORMAL; + else + attr = (COLOR_PAIR((col&7) + (col&0x70)/2)); + + if (col & 0x08) attr |= A_BOLD; + if (col & 0x80) attr |= A_BLINK; + + if (col & ATTR_UNDERLINE) attr |= A_UNDERLINE; + if (col & ATTR_REVERSE) attr |= A_REVERSE; + + wattrset(window, attr); +} + +void set_bg(WINDOW *window, int col) +{ + int attr; + + if (!use_colors) + attr = (col & 0x70) ? A_REVERSE : 0; + else { + attr = (col == 8) ? + (A_DIM | COLOR_PAIR(63)) : + (COLOR_PAIR((col&7) + (col&0x70)/2)); + } + + if (col & 0x08) attr |= A_BOLD; + if (col & 0x80) attr |= A_BLINK; + + wbkgdset(window, ' ' | attr); +} + +void move_cursor(int y, int x) +{ + scry = y; + scrx = x; +} + +void screen_refresh_freeze(void) +{ + freeze_refresh++; +} + +void screen_refresh_thaw(void) +{ + if (freeze_refresh > 0) { + freeze_refresh--; + if (freeze_refresh == 0) screen_refresh(NULL); + } +} + +void screen_refresh(WINDOW *window) +{ + if (window != NULL) + wnoutrefresh(window); + if (freeze_refresh == 0) { + move(scry, scrx); + wnoutrefresh(stdscr); + doupdate(); + } +} diff --git a/apps/irssi/src/fe-text/screen.h b/apps/irssi/src/fe-text/screen.h new file mode 100644 index 00000000..c1449bfb --- /dev/null +++ b/apps/irssi/src/fe-text/screen.h @@ -0,0 +1,46 @@ +#ifndef __SCREEN_H +#define __SCREEN_H + +#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES) +# include +#else +# include +#endif + +/* Some curseses include term.h, which #defines some things breaking irssi */ +#undef lines +#undef key_backspace +#undef tab + +#define ATTR_UNDERLINE 0x100 +#define ATTR_COLOR8 0x200 +#define ATTR_REVERSE 0x400 + +/* XXX I hope this could be integrated into BX. + * XXX Well, this should be done via libc, + * but FreeBSD libc support is quite LAME. + * Macro below are copied from lynx. + * + * clive@FreeBSD.org + */ +#ifdef WANT_BIG5 +/* XXX I didn't check the encoding range of big5+. This is standard big5. */ +#define is_big5_los(lo) (((char)0x40<=lo)&&(lo<=(char)0x7E)) /* standard */ +#define is_big5_lox(lo) (((char)0x80<=lo)&&(lo<=(char)0xFE)) /* extended */ +#define is_big5_hi(hi) (((char)0x81<=hi)&&(hi<=(char)0xFE)) +#define is_big5(hi,lo) is_big5_hi(hi) && (is_big5_los(lo) || is_big5_lox(lo)) +#endif WANT_BIG5 + +int init_screen(void); /* Initialize screen, detect screen length */ +void deinit_screen(void); /* Deinitialize screen */ + +void set_color(WINDOW *window, int col); +void set_bg(WINDOW *window, int col); + +void move_cursor(int y, int x); + +void screen_refresh_freeze(void); +void screen_refresh_thaw(void); +void screen_refresh(WINDOW *window); + +#endif diff --git a/apps/irssi/src/fe-text/silc.c b/apps/irssi/src/fe-text/silc.c new file mode 100644 index 00000000..9112b568 --- /dev/null +++ b/apps/irssi/src/fe-text/silc.c @@ -0,0 +1,298 @@ +/* + irssi.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "module-formats.h" +#include "args.h" +#include "signals.h" +#include "levels.h" +#include "core.h" +#include "settings.h" + +#include "printtext.h" +#include "fe-common-core.h" +#include "fe-common-silc.h" +#include "themes.h" + +#include "screen.h" +#include "gui-entry.h" +#include "mainwindows.h" +#include "gui-printtext.h" +#include "gui-readline.h" +#include "statusbar.h" +#include "gui-windows.h" + +#include + +#ifdef HAVE_STATIC_PERL +void perl_core_init(void); +void perl_core_deinit(void); + +void fe_perl_init(void); +void fe_perl_deinit(void); +#endif + +void silc_init(void); +void silc_deinit(void); + +void gui_expandos_init(void); +void gui_expandos_deinit(void); + +void textbuffer_commands_init(void); +void textbuffer_commands_deinit(void); + +void lastlog_init(void); +void lastlog_deinit(void); + +void mainwindow_activity_init(void); +void mainwindow_activity_deinit(void); + +void mainwindows_save_init(void); +void mainwindows_save_deinit(void); + +static GMainLoop *main_loop; +int quitting; + +static const char *firsttimer_text = + "Looks like this is the first time you run irssi.\n" + "This is just a reminder that you really should go read\n" + "startup-HOWTO if you haven't already. Irssi's default\n" + "settings aren't probably what you've used to, and you\n" + "shouldn't judge the whole client as crap based on them.\n\n" + "You can find startup-HOWTO and more irssi beginner info at\n" + "http://irssi.org/beginner/"; +static int display_firsttimer = FALSE; + + +static void sig_exit(void) +{ + g_main_quit(main_loop); +} + +/* redraw irssi's screen.. */ +void irssi_redraw(void) +{ + clear(); + refresh(); + + /* windows */ + mainwindows_redraw(); + /* statusbar */ + statusbar_redraw(NULL); + /* entry line */ + gui_entry_redraw(); +} + +static void textui_init(void) +{ + static struct poptOption options[] = { + POPT_AUTOHELP + { NULL, '\0', 0, NULL } + }; + + args_register(options); + + irssi_gui = IRSSI_GUI_TEXT; + core_init(); + silc_init(); + fe_common_core_init(); + fe_common_silc_init(); + + theme_register(gui_text_formats); + signal_add("gui exit", (SIGNAL_FUNC) sig_exit); +} + +static void textui_finish_init(void) +{ + quitting = FALSE; + + screen_refresh_freeze(); + textbuffer_init(); + textbuffer_view_init(); + textbuffer_commands_init(); + gui_entry_init(); + gui_expandos_init(); + gui_printtext_init(); + gui_readline_init(); + lastlog_init(); + mainwindows_init(); + mainwindow_activity_init(); + mainwindows_save_init(); + gui_windows_init(); + statusbar_init(); + + settings_check(); + + fe_common_core_finish_init(); + +#ifdef HAVE_STATIC_PERL + perl_core_init(); + fe_perl_init(); +#endif + signal_emit("irssi init finished", 0); + + if (display_firsttimer) { + printtext_window(active_win, MSGLEVEL_CLIENTNOTICE, + "%s", firsttimer_text); + } + + screen_refresh_thaw(); +} + +static void textui_deinit(void) +{ + quitting = TRUE; + signal(SIGINT, SIG_DFL); + + screen_refresh_freeze(); + while (modules != NULL) + module_unload(modules->data); + + signal_remove("gui exit", (SIGNAL_FUNC) sig_exit); + + lastlog_deinit(); + statusbar_deinit(); + gui_printtext_deinit(); + gui_readline_deinit(); + gui_windows_deinit(); + mainwindows_save_deinit(); + mainwindow_activity_deinit(); + mainwindows_deinit(); + gui_expandos_deinit(); + gui_entry_deinit(); + textbuffer_commands_deinit(); + textbuffer_view_deinit(); + textbuffer_deinit(); + + screen_refresh_thaw(); + deinit_screen(); + +#ifdef HAVE_STATIC_PERL + fe_perl_deinit(); + perl_core_deinit(); +#endif + + theme_unregister(); + + fe_common_silc_deinit(); + fe_common_core_deinit(); + silc_deinit(); + core_deinit(); +} + +static void check_oldcrap(void) +{ + FILE *f; + char *path, str[256]; + int found; + + /* check that default.theme is up-to-date */ + path = g_strdup_printf("%s/.irssi/default.theme", g_get_home_dir()); + f = fopen(path, "r+"); + if (f == NULL) { + g_free(path); + return; + } + found = FALSE; + while (!found && fgets(str, sizeof(str), f) != NULL) + found = strstr(str, "abstracts = ") != NULL; + fclose(f); + + if (found) { + g_free(path); + return; + } + + printf("\nYou seem to have old default.theme in ~/.irssi/ directory.\n"); + printf("Themeing system has changed a bit since last irssi release,\n"); + printf("you should either delete your old default.theme or manually\n"); + printf("merge it with the new default.theme.\n\n"); + printf("Do you want to delete the old theme now? (Y/n)\n"); + + str[0] = '\0'; + fgets(str, sizeof(str), stdin); + if (toupper(str[0]) == 'Y' || str[0] == '\n' || str[0] == '\0') + remove(path); + g_free(path); +} + +static void check_files(void) +{ + struct stat statbuf; + char *path; + + path = g_strdup_printf("%s/.irssi", g_get_home_dir()); + if (stat(path, &statbuf) != 0) { + /* ~/.irssi doesn't exist, first time running irssi */ + display_firsttimer = TRUE; + } else { + check_oldcrap(); + } + g_free(path); +} + +#ifdef WIN32 +static void winsock_init(void) +{ + WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD(2, 2); + + if (WSAStartup(wVersionRequested, &wsaData) != 0) { + printf("Error initializing winsock\n"); + exit(1); + } +} +#endif + +int main(int argc, char **argv) +{ + check_files(); +#ifdef WIN32 + winsock_init(); +#endif +#ifdef HAVE_SOCKS + SOCKSinit(argv[0]); +#endif +#ifdef ENABLE_NLS + /* initialize the i18n stuff */ + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); +#endif + + textui_init(); + args_execute(argc, argv); + + if (!init_screen()) + g_error("Can't initialize screen handling, quitting.\n"); + + textui_finish_init(); + main_loop = g_main_new(TRUE); + g_main_run(main_loop); + g_main_destroy(main_loop); + textui_deinit(); + +#ifdef MEM_DEBUG + ig_mem_profile(); +#endif + + return 0; +} diff --git a/apps/irssi/src/fe-text/statusbar-items.c b/apps/irssi/src/fe-text/statusbar-items.c new file mode 100644 index 00000000..137e7417 --- /dev/null +++ b/apps/irssi/src/fe-text/statusbar-items.c @@ -0,0 +1,765 @@ +/* + statusbar-items.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "misc.h" +#include "settings.h" +#include "special-vars.h" + +#include "window-items.h" +#include "formats.h" + +#include "statusbar.h" +#include "gui-printtext.h" + +/* how often to redraw lagging time (seconds) */ +#define LAG_REFRESH_TIME 10 + +/* how often to check for new mail (seconds) */ +#define MAIL_REFRESH_TIME 60 + +/* If we haven't been able to check lag for this long, "(??)" is added after + the lag */ +#define MAX_LAG_UNKNOWN_TIME 30 + +static STATUSBAR_REC *mainbar; +static MAIN_WINDOW_REC *mainbar_window; +static int use_colors; + +/* clock */ +static SBAR_ITEM_REC *clock_item; +static int clock_timetag; +static time_t clock_last; + +/* nick */ +static SBAR_ITEM_REC *nick_item; + +/* channel */ +static SBAR_ITEM_REC *window_item; + +/* activity */ +static SBAR_ITEM_REC *activity_item; +static GList *activity_list; + +/* more */ +static SBAR_ITEM_REC *more_item; + +/* lag */ +static SBAR_ITEM_REC *lag_item; +static int lag_timetag, lag_min_show; +static time_t lag_last_draw; + +/* mbox counter */ +static SBAR_ITEM_REC *mail_item; +static int mail_timetag, mail_last_count; +static time_t mail_last_mtime = -1; +static off_t mail_last_size = -1; + +/* topic */ +static SBAR_ITEM_REC *topic_item; +static STATUSBAR_REC *topic_bar; + +static void item_default(SBAR_ITEM_REC *item, int get_size_only, + const char *str, const char *data) +{ + SERVER_REC *server; + WI_ITEM_REC *wiitem; + char *tmpstr, *tmpstr2; + int len; + + if (active_win == NULL) { + server = NULL; + wiitem = NULL; + } else { + server = active_win->active_server; + wiitem = active_win->active; + } + + /* expand $variables */ + tmpstr = parse_special_string(str, server, wiitem, data, NULL, + PARSE_FLAG_ESCAPE_VARS); + + /* expand templates */ + str = tmpstr; + tmpstr2 = theme_format_expand_data(current_theme, &str, + 'n', '0' + item->bar->color, + NULL, NULL, + EXPAND_FLAG_ROOT | + EXPAND_FLAG_IGNORE_REPLACES | + EXPAND_FLAG_IGNORE_EMPTY); + g_free(tmpstr); + + /* remove color codes */ + tmpstr = strip_codes(tmpstr2); + g_free(tmpstr2); + + if (get_size_only) { + item->min_size = item->max_size = format_get_length(tmpstr); + } else { + if (item->size < item->min_size) { + /* they're forcing us smaller than minimum size.. */ + len = format_real_length(tmpstr, item->size); + tmpstr[len] = '\0'; + } + + tmpstr2 = g_strconcat(item->bar->color_string, tmpstr, NULL); + gui_printtext(item->xpos, item->bar->ypos, tmpstr2); + g_free(tmpstr2); + } + g_free(tmpstr); +} + +/* redraw clock */ +static void statusbar_clock(SBAR_ITEM_REC *item, int get_size_only) +{ + item_default(item, get_size_only, "{sb $Z}", ""); +} + +/* check if we need to redraw clock.. */ +static int statusbar_clock_timeout(void) +{ + struct tm *tm; + time_t t; + int min; + + tm = localtime(&clock_last); + min = tm->tm_min; + + t = time(NULL); + tm = localtime(&t); + + if (tm->tm_min != min) { + /* minute changed, redraw! */ + clock_last = t; + statusbar_item_redraw(clock_item); + } + return 1; +} + +/* redraw nick */ +static void statusbar_nick(SBAR_ITEM_REC *item, int get_size_only) +{ + item_default(item, get_size_only, + "{sb $P$N{sbmode $usermode}{sbaway $A}}", ""); +} + +static void sig_statusbar_nick_redraw(void) +{ + statusbar_item_redraw(nick_item); +} + +/* redraw window */ +static void statusbar_window(SBAR_ITEM_REC *item, int get_size_only) +{ + if (active_win->active != NULL) { + item_default(item, get_size_only, + "{sb $winref:$T{sbmode $M}}", ""); + } else { + item_default(item, get_size_only, + "{sb $winref{sbservertag $tag}}", ""); + } +} + +static void sig_statusbar_window_redraw(void) +{ + statusbar_item_redraw(window_item); +} + +static void sig_statusbar_window_redraw_window(WINDOW_REC *window) +{ + if (is_window_visible(window)) + statusbar_item_redraw(window_item); +} + +static void sig_statusbar_window_redraw_window_item(WI_ITEM_REC *item) +{ + WINDOW_REC *window; + + window = window_item_window(item); + if (window->active == item && is_window_visible(window)) + statusbar_item_redraw(window_item); +} + +static char *get_activity_list(int normal, int hilight) +{ + GString *str; + GList *tmp; + char *ret; + int is_det; + + str = g_string_new(NULL); + + for (tmp = activity_list; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *window = tmp->data; + + is_det = window->data_level >= DATA_LEVEL_HILIGHT; + if ((!is_det && !normal) || (is_det && !hilight)) + continue; + + g_string_append(str, "%c"); + if (str->len > 2) + g_string_append_c(str, ','); + + switch (window->data_level) { + case DATA_LEVEL_NONE: + case DATA_LEVEL_TEXT: + break; + case DATA_LEVEL_MSG: + g_string_append(str, "%W"); + break; + default: + g_string_append(str, window->hilight_color == NULL ? + "%M" : window->hilight_color); + break; + } + g_string_sprintfa(str, "%d", window->refnum); + + /* make sure the background is returned to default */ + g_string_append(str, "%n"); + } + + ret = str->len == 0 ? NULL : str->str; + g_string_free(str, ret == NULL); + return ret; +} + +/* redraw activity, FIXME: if we didn't get enough size, this gets buggy. + At least "Det:" isn't printed properly. also we should rearrange the + act list so that the highest priority items comes first. */ +static void statusbar_activity(SBAR_ITEM_REC *item, int get_size_only) +{ + char *actlist, *detlist, *data; + + if (use_colors) { + actlist = get_activity_list(TRUE, TRUE); + detlist = NULL; + } else { + actlist = get_activity_list(TRUE, FALSE); + detlist = get_activity_list(FALSE, TRUE); + } + + if (actlist == NULL && detlist == NULL) { + if (get_size_only) + item->min_size = item->max_size = 0; + return; + } + + data = g_strconcat("{sbact ", actlist != NULL ? actlist : "", + " ", detlist != NULL ? detlist : "", "}", NULL); + item_default(item, get_size_only, data, ""); + g_free(data); + + g_free_not_null(actlist); + g_free_not_null(detlist); +} + +static void sig_statusbar_activity_hilight(WINDOW_REC *window, gpointer oldlevel) +{ + GList *tmp; + int inspos; + + g_return_if_fail(window != NULL); + + if (settings_get_bool("actlist_moves")) { + /* Move the window to the first in the activity list */ + if (g_list_find(activity_list, window) != NULL) + activity_list = g_list_remove(activity_list, window); + if (window->data_level != 0) + activity_list = g_list_prepend(activity_list, window); + statusbar_item_redraw(activity_item); + return; + } + + if (g_list_find(activity_list, window) != NULL) { + /* already in activity list */ + if (window->data_level == 0) { + /* remove from activity list */ + activity_list = g_list_remove(activity_list, window); + statusbar_item_redraw(activity_item); + } else if (window->data_level != GPOINTER_TO_INT(oldlevel) || + window->hilight_color != 0) { + /* different level as last time (or maybe different + hilight color?), just redraw it. */ + statusbar_item_redraw(activity_item); + } + return; + } + + if (window->data_level == 0) + return; + + /* add window to activity list .. */ + inspos = 0; + for (tmp = activity_list; tmp != NULL; tmp = tmp->next, inspos++) { + WINDOW_REC *rec = tmp->data; + + if (window->refnum < rec->refnum) { + activity_list = + g_list_insert(activity_list, window, inspos); + break; + } + } + if (tmp == NULL) + activity_list = g_list_append(activity_list, window); + + statusbar_item_redraw(activity_item); +} + +static void sig_statusbar_activity_window_destroyed(WINDOW_REC *window) +{ + g_return_if_fail(window != NULL); + + if (g_list_find(activity_list, window) != NULL) + activity_list = g_list_remove(activity_list, window); + statusbar_item_redraw(activity_item); +} + +static void sig_statusbar_activity_updated(void) +{ + statusbar_item_redraw(activity_item); +} + +/* redraw -- more -- */ +static void statusbar_more(SBAR_ITEM_REC *item, int get_size_only) +{ + item_default(item, get_size_only, "{sbmore}", ""); +} + +static void sig_statusbar_more_check_remove(WINDOW_REC *window) +{ + g_return_if_fail(window != NULL); + + if (!is_window_visible(window)) + return; + + if (more_item != NULL && WINDOW_GUI(window)->view->bottom) { + statusbar_item_remove(more_item); + more_item = NULL; + } +} + +static void sig_statusbar_more_check(WINDOW_REC *window) +{ + if (window == NULL || !is_window_visible(window)) + return; + + if (!WINDOW_GUI(window)->view->bottom) { + if (more_item == NULL) { + more_item = statusbar_item_create(mainbar, SBAR_PRIORITY_LOW, FALSE, statusbar_more); + statusbar_redraw(mainbar); + } + } else if (more_item != NULL) { + statusbar_item_remove(more_item); + more_item = NULL; + } +} + +static void statusbar_lag(SBAR_ITEM_REC *item, int get_size_only) +{ + SERVER_REC *server; + GString *str; + int lag_unknown; + time_t now; + + server = active_win == NULL ? NULL : active_win->active_server; + if (server == NULL || server->lag_last_check == 0) { + /* No lag information */ + if (get_size_only) + item->min_size = item->max_size = 0; + return; + } + + now = time(NULL); + str = g_string_new(NULL); + + /* FIXME: ugly ugly.. */ + if (server->lag_sent == 0 || now-server->lag_sent < 5) { + lag_unknown = now-server->lag_last_check > + MAX_LAG_UNKNOWN_TIME+settings_get_int("lag_check_time"); + + if (lag_min_show < 0 || (server->lag < lag_min_show && !lag_unknown)) { + /* small lag, don't display */ + } else { + g_string_sprintfa(str, "%d.%02d", server->lag/1000, + (server->lag % 1000)/10); + if (lag_unknown) + g_string_append(str, " (??)"); + } + } else { + /* big lag, still waiting .. */ + g_string_sprintfa(str, "%ld (??)", + (long) (now-server->lag_sent)); + } + + item_default(item, get_size_only, "{sblag $0-}", str->str); + + g_string_free(str, TRUE); +} + +static void sig_statusbar_lag_redraw(void) +{ + statusbar_item_redraw(lag_item); +} + +static int statusbar_lag_timeout(void) +{ + /* refresh statusbar every 10 seconds */ + if (time(NULL)-lag_last_draw < LAG_REFRESH_TIME) + return 1; + + statusbar_item_redraw(lag_item); + return 1; +} + +/* FIXME: this isn't very good.. it handles only mbox mailboxes. + this whole mail feature should really be in it's own module with lots + of other mail formats supported and people who don't want to use it + wouldn't need to.. */ +static int get_mail_count(void) +{ + struct stat statbuf; + FILE *f; + char str[512], *fname; + int count; + + fname = g_getenv("MAIL"); + if (fname == NULL) return 0; + + if (stat(fname, &statbuf) != 0) { + mail_last_mtime = -1; + mail_last_size = -1; + mail_last_count = 0; + return 0; + } + + if (statbuf.st_mtime == mail_last_mtime && + statbuf.st_size == mail_last_size) + return mail_last_count; + mail_last_mtime = statbuf.st_mtime; + mail_last_size = statbuf.st_size; + + f = fopen(fname, "r"); + if (f == NULL) { + mail_last_count = 0; + return 0; + } + + count = 0; + while (fgets(str, sizeof(str), f) != NULL) { + if (strncmp(str, "From ", 5) == 0) + count++; + if (strncmp(str, "Subject: ", 9) == 0 && + strstr(str, "FOLDER INTERNAL DATA")) { + /* don't count these. */ + count--; + } + } + + fclose(f); + mail_last_count = count; + return count; +} + +static void statusbar_mail(SBAR_ITEM_REC *item, int get_size_only) +{ + char countstr[MAX_INT_STRLEN]; + int mail_count; + + mail_count = settings_get_bool("mail_counter") ? get_mail_count() : 0; + + if (mail_count <= 0) { + if (get_size_only) + item->min_size = item->max_size = 0; + return; + } + + ltoa(countstr, mail_count); + item_default(item, get_size_only, "{sbmail $0-}", countstr); +} + +static int statusbar_mail_timeout(void) +{ + statusbar_item_redraw(mail_item); + return 1; +} + +static void statusbar_topic(SBAR_ITEM_REC *item, int get_size_only) +{ + item_default(item, get_size_only, "$topic", ""); +} + +static void sig_statusbar_topic_redraw(void) +{ + if (topic_item != NULL) statusbar_item_redraw(topic_item); +} + +static void sig_sidebars_redraw(void) +{ + GSList *tmp; + + for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { + MAIN_WINDOW_REC *rec = tmp->data; + + if (rec->statusbar_window_item != NULL) + statusbar_item_redraw(rec->statusbar_window_item); + } +} + +static void topicbar_create(void) +{ + if (topic_bar != NULL) + return; + + topic_bar = statusbar_create(STATUSBAR_POS_UP, 0); + topic_item = statusbar_item_create(topic_bar, SBAR_PRIORITY_NORMAL, FALSE, statusbar_topic); + topic_item->max_size = TRUE; + statusbar_redraw(topic_bar); + + signal_add("window changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw); + signal_add("window item changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw); + signal_add("channel topic changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw); + signal_add("query address changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw); +} + +static void topicbar_destroy(void) +{ + if (topic_bar == NULL) + return; + + statusbar_destroy(topic_bar); + topic_item = NULL; + topic_bar = NULL; + + signal_remove("window changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw); + signal_remove("window item changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw); + signal_remove("channel topic changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw); + signal_remove("query address changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw); +} + +static void mainbar_remove_items(void) +{ + statusbar_item_remove(clock_item); + statusbar_item_remove(nick_item); + statusbar_item_remove(window_item); + statusbar_item_remove(mail_item); + statusbar_item_remove(lag_item); + statusbar_item_remove(activity_item); +} + +static void mainbar_add_items(MAIN_WINDOW_REC *window) +{ + mainbar = window->statusbar; + mainbar_window = window; + + clock_item = statusbar_item_create(mainbar, SBAR_PRIORITY_HIGH, FALSE, statusbar_clock); + nick_item = statusbar_item_create(mainbar, SBAR_PRIORITY_NORMAL, FALSE, statusbar_nick); + window_item = statusbar_item_create(mainbar, SBAR_PRIORITY_NORMAL, FALSE, statusbar_window); + mail_item = statusbar_item_create(mainbar, SBAR_PRIORITY_LOW, FALSE, statusbar_mail); + lag_item = statusbar_item_create(mainbar, SBAR_PRIORITY_LOW, FALSE, statusbar_lag); + activity_item = statusbar_item_create(mainbar, SBAR_PRIORITY_HIGH, FALSE, statusbar_activity); +} + +static void sidebar_add_items(MAIN_WINDOW_REC *window) +{ + window->statusbar_window_item = + statusbar_item_create(window->statusbar, SBAR_PRIORITY_NORMAL, FALSE, statusbar_window); +} + +static void sidebar_remove_items(MAIN_WINDOW_REC *window) +{ + if (window->statusbar_window_item != NULL) { + statusbar_item_remove(window->statusbar_window_item); + window->statusbar_window_item = NULL; + } +} + +static void sig_mainwindow_created(MAIN_WINDOW_REC *window) +{ + window->statusbar = + statusbar_create(STATUSBAR_POS_MIDDLE, + window->first_line+window->height); + ((STATUSBAR_REC *) window->statusbar)->window = window; + sidebar_add_items(window); +} + +static void sig_mainwindow_destroyed(MAIN_WINDOW_REC *window) +{ + if (window == mainbar_window) { + mainbar = NULL; + mainbar_window = NULL; + } + + if (window->statusbar != NULL) + statusbar_destroy(window->statusbar); +} + +static void sig_main_statusbar_changed(WINDOW_REC *window) +{ + MAIN_WINDOW_REC *parent; + + if (window == NULL) + return; + + parent = WINDOW_GUI(window)->parent; + if (mainbar == parent->statusbar) + return; + + if (mainbar != NULL) { + mainbar_remove_items(); + sidebar_add_items(mainbar_window); + } + sidebar_remove_items(parent); + mainbar_add_items(parent); +} + +static void read_settings(void) +{ + use_colors = settings_get_bool("colors") && has_colors(); + if (settings_get_bool("topicbar")) + topicbar_create(); + else + topicbar_destroy(); + + lag_min_show = settings_get_int("lag_min_show")*10; + statusbar_redraw(NULL); +} + +void statusbar_items_init(void) +{ + GSList *tmp; + + settings_add_int("misc", "lag_min_show", 100); + settings_add_bool("lookandfeel", "topicbar", TRUE); + settings_add_bool("lookandfeel", "actlist_moves", FALSE); + settings_add_bool("misc", "mail_counter", TRUE); + + /* clock */ + clock_timetag = g_timeout_add(1000, (GSourceFunc) statusbar_clock_timeout, NULL); + + /* nick */ + signal_add("server connected", (SIGNAL_FUNC) sig_statusbar_nick_redraw); + signal_add("channel wholist", (SIGNAL_FUNC) sig_statusbar_nick_redraw); + signal_add("window changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); + signal_add("window item changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); + signal_add("nick mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); + signal_add("user mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); + signal_add("server nick changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); + signal_add("window server changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); + signal_add("away mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); + + /* channel */ + signal_add("window changed", (SIGNAL_FUNC) sig_statusbar_window_redraw); + signal_add("window item changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window); + signal_add("channel mode changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window_item); + signal_add("window server changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window); + signal_add("window refnum changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window); + + /* activity */ + activity_list = NULL; + signal_add("window activity", (SIGNAL_FUNC) sig_statusbar_activity_hilight); + signal_add("window destroyed", (SIGNAL_FUNC) sig_statusbar_activity_window_destroyed); + signal_add("window refnum changed", (SIGNAL_FUNC) sig_statusbar_activity_updated); + + /* more */ + more_item = NULL; + signal_add("gui page scrolled", (SIGNAL_FUNC) sig_statusbar_more_check_remove); + signal_add("window changed", (SIGNAL_FUNC) sig_statusbar_more_check); + signal_add("gui print text", (SIGNAL_FUNC) sig_statusbar_more_check); + + /* lag */ + lag_timetag = g_timeout_add(1000*LAG_REFRESH_TIME, (GSourceFunc) statusbar_lag_timeout, NULL); + signal_add("server lag", (SIGNAL_FUNC) sig_statusbar_lag_redraw); + signal_add("window server changed", (SIGNAL_FUNC) sig_statusbar_lag_redraw); + + /* mail */ + mail_timetag = g_timeout_add(1000*MAIL_REFRESH_TIME, (GSourceFunc) statusbar_mail_timeout, NULL); + + /* topic */ + topic_item = NULL; topic_bar = NULL; + signal_add("setup changed", (SIGNAL_FUNC) read_settings); + + read_settings(); + statusbar_redraw(NULL); + + /* middle bars */ + signal_add("mainwindow created", (SIGNAL_FUNC) sig_mainwindow_created); + signal_add("mainwindow destroyed", (SIGNAL_FUNC) sig_mainwindow_destroyed); + signal_add("window changed", (SIGNAL_FUNC) sig_main_statusbar_changed); + signal_add("window refnum changed", (SIGNAL_FUNC) sig_sidebars_redraw); + + /* add statusbars to existing windows */ + for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) + sig_mainwindow_created(tmp->data); + sig_main_statusbar_changed(active_win); +} + +void statusbar_items_deinit(void) +{ + /* clock */ + g_source_remove(clock_timetag); + + /* nick */ + signal_remove("server connected", (SIGNAL_FUNC) sig_statusbar_nick_redraw); + signal_remove("channel wholist", (SIGNAL_FUNC) sig_statusbar_nick_redraw); + signal_remove("window changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); + signal_remove("window item changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); + signal_remove("nick mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); + signal_remove("user mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); + signal_remove("server nick changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); + signal_remove("window server changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); + signal_remove("away mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw); + + /* channel */ + signal_remove("window changed", (SIGNAL_FUNC) sig_statusbar_window_redraw); + signal_remove("window item changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window); + signal_remove("channel mode changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window_item); + signal_remove("window server changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window); + signal_remove("window refnum changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window); + + /* activity */ + signal_remove("window activity", (SIGNAL_FUNC) sig_statusbar_activity_hilight); + signal_remove("window destroyed", (SIGNAL_FUNC) sig_statusbar_activity_window_destroyed); + signal_remove("window refnum changed", (SIGNAL_FUNC) sig_statusbar_activity_updated); + g_list_free(activity_list); + + /* more */ + signal_remove("gui page scrolled", (SIGNAL_FUNC) sig_statusbar_more_check_remove); + signal_remove("window changed", (SIGNAL_FUNC) sig_statusbar_more_check); + signal_remove("gui print text", (SIGNAL_FUNC) sig_statusbar_more_check); + + /* lag */ + g_source_remove(lag_timetag); + signal_remove("server lag", (SIGNAL_FUNC) sig_statusbar_lag_redraw); + signal_remove("window server changed", (SIGNAL_FUNC) sig_statusbar_lag_redraw); + + /* mail */ + g_source_remove(mail_timetag); + + /* topic */ + topicbar_destroy(); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); + + /* middle bars */ + signal_remove("mainwindow created", (SIGNAL_FUNC) sig_mainwindow_created); + signal_remove("mainwindow destroyed", (SIGNAL_FUNC) sig_mainwindow_destroyed); + signal_remove("window changed", (SIGNAL_FUNC) sig_main_statusbar_changed); + signal_remove("window refnum changed", (SIGNAL_FUNC) sig_sidebars_redraw); +} diff --git a/apps/irssi/src/fe-text/statusbar.c b/apps/irssi/src/fe-text/statusbar.c new file mode 100644 index 00000000..8e99c4fe --- /dev/null +++ b/apps/irssi/src/fe-text/statusbar.c @@ -0,0 +1,369 @@ +/* + statusbar.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" + +#include "themes.h" + +#include "statusbar.h" +#include "gui-windows.h" + +static int backs[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; /* FIXME: should be in some more generic place.. */ + +void statusbar_items_init(void); +void statusbar_items_deinit(void); + +static GSList *statusbars; +static int sbar_uppest, sbar_lowest, sbars_up, sbars_down; + +static void statusbar_item_destroy(SBAR_ITEM_REC *rec) +{ + rec->bar->items = g_slist_remove(rec->bar->items, rec); + g_free(rec); +} + +static int sbar_item_cmp(SBAR_ITEM_REC *item1, SBAR_ITEM_REC *item2) +{ + return item1->priority == item2->priority ? 0 : + item1->priority < item2->priority ? -1 : 1; +} + +static int statusbar_shrink_to_min(GSList *items, int size, int max_width) +{ + GSList *tmp; + + for (tmp = items; tmp != NULL; tmp = tmp->next) { + SBAR_ITEM_REC *rec = tmp->data; + + size -= (rec->max_size-rec->min_size); + rec->size = rec->min_size; + + if (size <= max_width) { + rec->size += max_width-size; + break; + } + + if (rec->size == 0) { + /* min_size was 0, item removed. + remove the marginal too */ + size--; + } + } + + return size; +} + +static void statusbar_shrink_forced(GSList *items, int size, int max_width) +{ + GSList *tmp; + + for (tmp = items; tmp != NULL; tmp = tmp->next) { + SBAR_ITEM_REC *rec = tmp->data; + + if (size-rec->size > max_width) { + /* remove the whole item */ + size -= rec->size+1; /* +1 == the marginal */ + rec->size = 0; + } else { + /* shrink the item */ + rec->size -= size-max_width; + break; + } + } +} + +static void statusbar_get_sizes(STATUSBAR_REC *bar, int max_width) +{ + GSList *tmp, *prior_sorted; + int width; + + /* first give items their max. size */ + prior_sorted = NULL; + width = -1; /* -1 because of the marginals */ + for (tmp = bar->items; tmp != NULL; tmp = tmp->next) { + SBAR_ITEM_REC *rec = tmp->data; + + rec->func(rec, TRUE); + rec->size = rec->max_size; + + if (rec->size > 0) { + /* +1 == marginal between items */ + width += rec->max_size+1; + + prior_sorted = g_slist_insert_sorted(prior_sorted, rec, + (GCompareFunc) + sbar_item_cmp); + } + } + + if (width > max_width) { + /* too big, start shrinking from items with lowest priority + and shrink until everything fits or until we've shrinked + all items. */ + width = statusbar_shrink_to_min(prior_sorted, width, + max_width); + if (width > max_width) { + /* still need to shrink, remove the items with lowest + priority until everything fits to screen */ + statusbar_shrink_forced(prior_sorted, width, + max_width); + } + } + + g_slist_free(prior_sorted); +} + +static void statusbar_redraw_line(STATUSBAR_REC *bar) +{ + WINDOW_REC *old_active_win; + GSList *tmp; + int xpos, rxpos; + + old_active_win = active_win; + if (bar->window != NULL) + active_win = bar->window->active; + + statusbar_get_sizes(bar, COLS-2); + + xpos = 1; + for (tmp = bar->items; tmp != NULL; tmp = tmp->next) { + SBAR_ITEM_REC *rec = tmp->data; + + if (!rec->right_justify && rec->size > 0) { + rec->xpos = xpos; + xpos += rec->size+1; + rec->func(rec, FALSE); + } + } + + rxpos = COLS-1; + for (tmp = bar->items; tmp != NULL; tmp = tmp->next) { + SBAR_ITEM_REC *rec = tmp->data; + + if (rec->right_justify && rec->size > 0) { + rxpos -= rec->size+1; + rec->xpos = rxpos+1; + rec->func(rec, FALSE); + } + } + + active_win = old_active_win; +} + +static void statusbar_redraw_all(void) +{ + screen_refresh_freeze(); + g_slist_foreach(statusbars, (GFunc) statusbar_redraw, NULL); + screen_refresh_thaw(); +} + +STATUSBAR_REC *statusbar_find(int pos, int line) +{ + GSList *tmp; + + for (tmp = statusbars; tmp != NULL; tmp = tmp->next) { + STATUSBAR_REC *rec = tmp->data; + + if (rec->pos == pos && rec->line == line) + return rec; + } + + return NULL; +} + +void statusbar_redraw(STATUSBAR_REC *bar) +{ + if (bar == NULL) { + statusbar_redraw_all(); + return; + } + + set_bg(stdscr, backs[bar->color] << 4); + move(bar->ypos, 0); clrtoeol(); + set_bg(stdscr, 0); + + statusbar_redraw_line(bar); + + screen_refresh(NULL); +} + +void statusbar_item_redraw(SBAR_ITEM_REC *item) +{ + g_return_if_fail(item != NULL); + + item->func(item, TRUE); + if (item->max_size != item->size) + statusbar_redraw(item->bar); + else { + item->func(item, FALSE); + screen_refresh(NULL); + } +} + +static int get_last_bg(const char *str) +{ + int last = -1; + + while (*str != '\0') { + if (*str == '%' && str[1] != '\0') { + str++; + if (*str >= '0' && *str <= '7') + last = *str-'0'; + } + str++; + } + + return last; +} + +/* ypos is used only when pos == STATUSBAR_POS_MIDDLE */ +STATUSBAR_REC *statusbar_create(int pos, int ypos) +{ + STATUSBAR_REC *rec; + char *str; + + rec = g_new0(STATUSBAR_REC, 1); + statusbars = g_slist_append(statusbars, rec); + + rec->pos = pos; + rec->line = pos == STATUSBAR_POS_MIDDLE ? ypos : + mainwindows_reserve_lines(1, pos == STATUSBAR_POS_UP); + rec->ypos = pos == STATUSBAR_POS_MIDDLE ? ypos : + pos == STATUSBAR_POS_UP ? rec->line : LINES-1-rec->line; + + /* get background color from sb_background abstract */ + str = theme_format_expand(current_theme, "{sb_background}"); + if (str == NULL) str = g_strdup("%n%8"); + rec->color_string = g_strconcat("%n", str, NULL); + g_free(str); + + rec->color = get_last_bg(rec->color_string); + if (rec->color < 0) rec->color = current_theme->default_real_color; + + if (pos == STATUSBAR_POS_UP) { + if (sbars_up == 0) sbar_uppest = rec->line; + sbars_up++; + rec->line -= sbar_uppest; + } else if (pos == STATUSBAR_POS_DOWN) { + if (sbars_down == 0) sbar_lowest = rec->line; + sbars_down++; + rec->line -= sbar_lowest; + } + + set_bg(stdscr, backs[rec->color] << 4); + move(rec->ypos, 0); clrtoeol(); + set_bg(stdscr, 0); + + return rec; +} + +static void statusbars_pack(int pos, int line) +{ + GSList *tmp; + + for (tmp = statusbars; tmp != NULL; tmp = tmp->next) { + STATUSBAR_REC *rec = tmp->data; + + if (rec->pos == pos && rec->line > line) { + rec->line--; + rec->ypos += (pos == STATUSBAR_POS_UP ? -1 : 1); + } + } +} + +void statusbar_destroy(STATUSBAR_REC *bar) +{ + g_return_if_fail(bar != NULL); + + if (bar->pos != STATUSBAR_POS_MIDDLE) + mainwindows_reserve_lines(-1, bar->pos == STATUSBAR_POS_UP); + + if (bar->pos == STATUSBAR_POS_UP) sbars_up--; + if (bar->pos == STATUSBAR_POS_DOWN) sbars_down--; + statusbars = g_slist_remove(statusbars, bar); + + while (bar->items != NULL) + statusbar_item_destroy(bar->items->data); + + if (bar->pos != STATUSBAR_POS_MIDDLE) + statusbars_pack(bar->pos, bar->pos); + g_free(bar->color_string); + g_free(bar); + + if (!quitting) statusbar_redraw_all(); +} + +SBAR_ITEM_REC *statusbar_item_create(STATUSBAR_REC *bar, + int priority, int right_justify, + STATUSBAR_FUNC func) +{ + SBAR_ITEM_REC *rec; + + g_return_val_if_fail(bar != NULL, NULL); + g_return_val_if_fail(func != NULL, NULL); + + rec = g_new0(SBAR_ITEM_REC, 1); + rec->bar = bar; + bar->items = g_slist_append(bar->items, rec); + + rec->priority = priority; + rec->right_justify = right_justify; + rec->func = func; + + return rec; +} + +void statusbar_item_remove(SBAR_ITEM_REC *item) +{ + g_return_if_fail(item != NULL); + + statusbar_item_destroy(item); + if (!quitting) statusbar_redraw_all(); +} + +static void sig_mainwindow_resized(MAIN_WINDOW_REC *window) +{ + STATUSBAR_REC *rec; + + rec = window->statusbar; + rec->ypos = window->first_line+window->height; +} + +void statusbar_init(void) +{ + statusbars = NULL; + sbars_up = sbars_down = 0; + + statusbar_items_init(); + signal_add("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized); + signal_add("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized); +} + +void statusbar_deinit(void) +{ + statusbar_items_deinit(); + + while (statusbars != NULL) + statusbar_destroy(statusbars->data); + + signal_remove("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized); + signal_remove("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized); +} diff --git a/apps/irssi/src/fe-text/statusbar.h b/apps/irssi/src/fe-text/statusbar.h new file mode 100644 index 00000000..32d35b82 --- /dev/null +++ b/apps/irssi/src/fe-text/statusbar.h @@ -0,0 +1,63 @@ +#ifndef __STATUSBAR_H +#define __STATUSBAR_H + +#include "mainwindows.h" + +#define SBAR_PRIORITY_HIGH 100 +#define SBAR_PRIORITY_NORMAL 0 +#define SBAR_PRIORITY_LOW -100 + +enum { + STATUSBAR_POS_UP, + STATUSBAR_POS_MIDDLE, + STATUSBAR_POS_DOWN +}; + +typedef struct SBAR_ITEM_REC SBAR_ITEM_REC; +typedef void (*STATUSBAR_FUNC) (SBAR_ITEM_REC *item, int get_size_only); + +typedef struct { + MAIN_WINDOW_REC *window; + + int pos; + int line; + + char *color_string; + int color; + + int ypos; /* real position in screen at the moment */ + GSList *items; +} STATUSBAR_REC; + +struct SBAR_ITEM_REC { + STATUSBAR_REC *bar; + STATUSBAR_FUNC func; + + /* what item wants */ + int priority; + int min_size, max_size; + unsigned int right_justify:1; + + /* what item gets */ + int xpos, size; +}; + +/* ypos is used only when pos == STATUSBAR_POS_MIDDLE */ +STATUSBAR_REC *statusbar_create(int pos, int ypos); +void statusbar_destroy(STATUSBAR_REC *bar); + +STATUSBAR_REC *statusbar_find(int pos, int line); + +SBAR_ITEM_REC *statusbar_item_create(STATUSBAR_REC *bar, + int priority, int right_justify, + STATUSBAR_FUNC func); +void statusbar_item_remove(SBAR_ITEM_REC *item); + +/* redraw statusbar, NULL = all */ +void statusbar_redraw(STATUSBAR_REC *bar); +void statusbar_item_redraw(SBAR_ITEM_REC *item); + +void statusbar_init(void); +void statusbar_deinit(void); + +#endif diff --git a/apps/irssi/src/fe-text/textbuffer-commands.c b/apps/irssi/src/fe-text/textbuffer-commands.c new file mode 100644 index 00000000..92ec70bd --- /dev/null +++ b/apps/irssi/src/fe-text/textbuffer-commands.c @@ -0,0 +1,297 @@ +/* + textbuffer-commands.c : Text buffer handling + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "commands.h" +#include "misc.h" +#include "levels.h" + +#include "printtext.h" +#include "gui-windows.h" + +/* SYNTAX: CLEAR */ +static void cmd_clear(const char *data) +{ + GHashTable *optlist; + void *free_arg; + GSList *tmp; + + g_return_if_fail(data != NULL); + + if (!cmd_get_params(data, &free_arg, PARAM_FLAG_OPTIONS, + "clear", &optlist)) return; + + if (g_hash_table_lookup(optlist, "all") == NULL) { + /* clear active window */ + textbuffer_view_clear(WINDOW_GUI(active_win)->view); + } else { + /* clear all windows */ + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *window = tmp->data; + + textbuffer_view_clear(WINDOW_GUI(window)->view); + } + } + + cmd_params_free(free_arg); +} + +static void cmd_scrollback(const char *data, SERVER_REC *server, + WI_ITEM_REC *item) +{ + command_runsub("scrollback", data, server, item); +} + +/* SYNTAX: SCROLLBACK CLEAR */ +static void cmd_scrollback_clear(void) +{ + textbuffer_view_remove_all_lines(WINDOW_GUI(active_win)->view); +} + +static void scrollback_goto_line(int linenum) +{ + TEXT_BUFFER_VIEW_REC *view; + + view = WINDOW_GUI(active_win)->view; + if (view->buffer->lines_count == 0) + return; + + textbuffer_view_scroll_line(view, view->buffer->lines->data); + gui_window_scroll(active_win, linenum); +} + +static void scrollback_goto_time(const char *datearg, const char *timearg) +{ + GList *tmp; + struct tm tm; + time_t now, stamp; + int day, month; + + /* [dd[.mm] | -] hh:mi[:ss] */ + now = stamp = time(NULL); + if (*datearg == '-') { + /* - */ + stamp -= atoi(datearg+1) * 3600*24; + memcpy(&tm, localtime(&stamp), sizeof(struct tm)); + } else if (*timearg != '\0') { + /* dd[.mm] */ + memcpy(&tm, localtime(&stamp), sizeof(struct tm)); + + day = month = 0; + sscanf(datearg, "%d.%d", &day, &month); + if (day <= 0) return; + + if (month <= 0) { + /* month not given */ + if (day > tm.tm_mday) { + /* last month's day */ + if (tm.tm_mon > 0) + tm.tm_mon--; + else { + /* last year's day.. */ + tm.tm_year--; + tm.tm_mon = 11; + } + } + } else { + month--; + if (month > tm.tm_mon) + tm.tm_year--; + tm.tm_mon = month; + } + + tm.tm_mday = day; + stamp = mktime(&tm); + } + else + { + /* only time given, move it to timearg */ + timearg = datearg; + } + + /* hh:mi[:ss] */ + memcpy(&tm, localtime(&stamp), sizeof(struct tm)); + tm.tm_sec = 0; + sscanf(timearg, "%d:%d:%d", &tm.tm_hour, &tm.tm_min, &tm.tm_sec); + stamp = mktime(&tm); + + if (stamp > now && timearg == datearg) { + /* we used /SB GOTO 23:59 or something, we want to jump to + previous day's 23:59 time instead of into future. */ + stamp -= 3600*24; + } + + if (stamp > now) { + /* we're still looking into future, don't bother checking */ + return; + } + + /* scroll to first line after timestamp */ + tmp = textbuffer_view_get_lines(WINDOW_GUI(active_win)->view); + for (; tmp != NULL; tmp = tmp->next) { + LINE_REC *rec = tmp->data; + + if (rec->info.time >= stamp) { + gui_window_scroll_line(active_win, rec); + break; + } + } +} + +/* SYNTAX: SCROLLBACK GOTO <+|-linecount>|| */ +static void cmd_scrollback_goto(const char *data) +{ + char *datearg, *timearg; + void *free_arg; + int lines; + + if (!cmd_get_params(data, &free_arg, 2, &datearg, &timearg)) + return; + + if (*timearg == '\0' && (*datearg == '-' || *datearg == '+')) { + /* go forward/backward n lines */ + lines = atoi(datearg + (*datearg == '-' ? 0 : 1)); + gui_window_scroll(active_win, lines); + } else if (*timearg == '\0' && is_numeric(datearg, '\0')) { + /* go to n'th line. */ + scrollback_goto_line(atoi(datearg)); + } else { + /* should be timestamp */ + scrollback_goto_time(datearg, timearg); + } + + cmd_params_free(free_arg); +} + +/* SYNTAX: SCROLLBACK HOME */ +static void cmd_scrollback_home(const char *data) +{ + TEXT_BUFFER_REC *buffer; + + buffer = WINDOW_GUI(active_win)->view->buffer; + if (buffer->lines_count > 0) + gui_window_scroll_line(active_win, buffer->lines->data); +} + +/* SYNTAX: SCROLLBACK END */ +static void cmd_scrollback_end(const char *data) +{ + TEXT_BUFFER_VIEW_REC *view; + + view = WINDOW_GUI(active_win)->view; + if (view->bottom_startline == NULL) + return; + + textbuffer_view_scroll_line(view, view->bottom_startline->data); + gui_window_scroll(active_win, view->bottom_subline); +} + +/* SYNTAX: SCROLLBACK REDRAW */ +static void cmd_scrollback_redraw(void) +{ +#if 0 + GUI_WINDOW_REC *gui; + GList *tmp, *next; + + gui = WINDOW_GUI(active_win); + + screen_refresh_freeze(); + for (tmp = gui->lines; tmp != NULL; tmp = next) { + next = tmp->next; + gui_window_reformat_line(active_win, tmp->data); + } + + gui_window_redraw(active_win); + screen_refresh_thaw(); +#endif +} + +static void cmd_scrollback_status(void) +{ + GSList *tmp; + int window_kb, total_lines, total_kb; + + total_lines = 0; total_kb = 0; + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *window = tmp->data; + TEXT_BUFFER_VIEW_REC *view; + + view = WINDOW_GUI(window)->view; + + window_kb = g_slist_length(view->buffer->text_chunks)* + LINE_TEXT_CHUNK_SIZE/1024; + total_lines += view->buffer->lines_count; + total_kb += window_kb; + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, + "Window %d: %d lines, %dkB of data", + window->refnum, view->buffer->lines_count, + window_kb); + } + + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, + "Total: %d lines, %dkB of data", + total_lines, total_kb); +} + +static void sig_away_changed(SERVER_REC *server) +{ + GSList *tmp; + + if (!server->usermode_away) + return; + + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + textbuffer_view_set_bookmark_bottom(WINDOW_GUI(rec)->view, + "lastlog_last_away"); + } +} + +void textbuffer_commands_init(void) +{ + command_bind("clear", NULL, (SIGNAL_FUNC) cmd_clear); + command_bind("scrollback", NULL, (SIGNAL_FUNC) cmd_scrollback); + command_bind("scrollback clear", NULL, (SIGNAL_FUNC) cmd_scrollback_clear); + command_bind("scrollback goto", NULL, (SIGNAL_FUNC) cmd_scrollback_goto); + command_bind("scrollback home", NULL, (SIGNAL_FUNC) cmd_scrollback_home); + command_bind("scrollback end", NULL, (SIGNAL_FUNC) cmd_scrollback_end); + command_bind("scrollback redraw", NULL, (SIGNAL_FUNC) cmd_scrollback_redraw); + command_bind("scrollback status", NULL, (SIGNAL_FUNC) cmd_scrollback_status); + + command_set_options("clear", "all"); + + signal_add("away mode changed", (SIGNAL_FUNC) sig_away_changed); +} + +void textbuffer_commands_deinit(void) +{ + command_unbind("clear", (SIGNAL_FUNC) cmd_clear); + command_unbind("scrollback", (SIGNAL_FUNC) cmd_scrollback); + command_unbind("scrollback clear", (SIGNAL_FUNC) cmd_scrollback_clear); + command_unbind("scrollback goto", (SIGNAL_FUNC) cmd_scrollback_goto); + command_unbind("scrollback home", (SIGNAL_FUNC) cmd_scrollback_home); + command_unbind("scrollback end", (SIGNAL_FUNC) cmd_scrollback_end); + command_unbind("scrollback redraw", (SIGNAL_FUNC) cmd_scrollback_redraw); + command_unbind("scrollback status", (SIGNAL_FUNC) cmd_scrollback_status); + + signal_remove("away mode changed", (SIGNAL_FUNC) sig_away_changed); +} diff --git a/apps/irssi/src/fe-text/textbuffer-view.c b/apps/irssi/src/fe-text/textbuffer-view.c new file mode 100644 index 00000000..f3a74080 --- /dev/null +++ b/apps/irssi/src/fe-text/textbuffer-view.c @@ -0,0 +1,1118 @@ +/* + textbuffer-view.c : Text buffer handling + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "textbuffer-view.h" +#include "screen.h" + +typedef struct { + char *name; + LINE_REC *line; +} BOOKMARK_REC; + +/* how often to scan line cache for lines not accessed for a while (ms) */ +#define LINE_CACHE_CHECK_TIME (5*60*1000) +/* how long to keep line cache in memory (seconds) */ +#define LINE_CACHE_KEEP_TIME (10*60) + +static int linecache_tag; +static GSList *views; + +#define view_is_bottom(view) \ + ((view)->ypos >= -1 && (view)->ypos < (view)->height) + +#define view_get_linecount(view, line) \ + textbuffer_view_get_line_cache(view, line)->count + +static GSList *textbuffer_get_views(TEXT_BUFFER_REC *buffer) +{ + GSList *tmp, *list; + + for (tmp = views; tmp != NULL; tmp = tmp->next) { + TEXT_BUFFER_VIEW_REC *view = tmp->data; + + if (view->buffer == buffer) { + list = g_slist_copy(view->siblings); + return g_slist_prepend(list, view); + } + } + + return NULL; +} + +static TEXT_BUFFER_CACHE_REC * +textbuffer_cache_get(GSList *views, int width) +{ + TEXT_BUFFER_CACHE_REC *cache; + + /* check if there's existing cache with correct width */ + while (views != NULL) { + TEXT_BUFFER_VIEW_REC *view = views->data; + + if (view->width == width) { + view->cache->refcount++; + return view->cache; + } + views = views->next; + } + + /* create new cache */ + cache = g_new0(TEXT_BUFFER_CACHE_REC, 1); + cache->refcount = 1; + cache->width = width; + cache->line_cache = g_hash_table_new((GHashFunc) g_direct_hash, + (GCompareFunc) g_direct_equal); + return cache; +} + +static int line_cache_destroy(void *key, LINE_CACHE_REC *cache) +{ + g_free(cache); + return TRUE; +} + +static void textbuffer_cache_destroy(TEXT_BUFFER_CACHE_REC *cache) +{ + g_hash_table_foreach(cache->line_cache, + (GHFunc) line_cache_destroy, NULL); + g_hash_table_destroy(cache->line_cache); + g_free(cache); +} + +static void textbuffer_cache_unref(TEXT_BUFFER_CACHE_REC *cache) +{ + if (--cache->refcount == 0) + textbuffer_cache_destroy(cache); +} + +static LINE_CACHE_REC * +view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) +{ + LINE_CACHE_REC *rec; + LINE_CACHE_SUB_REC *sub; + GSList *lines; + unsigned char cmd; + char *ptr, *last_space_ptr; + int xpos, pos, indent_pos, last_space, last_color, color, linecount; + + g_return_val_if_fail(line->text != NULL, NULL); + + xpos = 0; color = 0; indent_pos = view->default_indent; + last_space = last_color = 0; last_space_ptr = NULL; sub = NULL; + + linecount = 1; + lines = NULL; + for (ptr = line->text;;) { + if (*ptr == '\0') { + /* command */ + ptr++; + cmd = *ptr; + ptr++; + + if (cmd == LINE_CMD_EOL || cmd == LINE_CMD_FORMAT) + break; + + if (cmd == LINE_CMD_CONTINUE) { + char *tmp; + + memcpy(&tmp, ptr, sizeof(char *)); + ptr = tmp; + continue; + } + + if ((cmd & 0x80) == 0) { + /* set color */ + color = (color & ATTR_UNDERLINE) | cmd; + } else switch (cmd) { + case LINE_CMD_UNDERLINE: + color ^= ATTR_UNDERLINE; + break; + case LINE_CMD_COLOR0: + color = color & ATTR_UNDERLINE; + break; + case LINE_CMD_COLOR8: + color &= 0xfff0; + color |= 8|ATTR_COLOR8; + break; + case LINE_CMD_BLINK: + color |= 0x80; + break; + case LINE_CMD_INDENT: + /* set indentation position here - don't do + it if we're too close to right border */ + if (xpos < view->width-5) indent_pos = xpos; + break; + } + continue; + } + + if (xpos == view->width && sub != NULL && + (last_space <= indent_pos || last_space <= 10) && + !view->longword_noindent) { + /* long word, remove the indentation from this line */ + xpos -= sub->indent; + sub->indent = 0; + } + + if (xpos == view->width) { + xpos = indent_pos; + + sub = g_new0(LINE_CACHE_SUB_REC, 1); + if (last_space > indent_pos && last_space > 10) { + /* go back to last space */ + color = last_color; + ptr = last_space_ptr; + while (*ptr == ' ') ptr++; + } else if (!view->longword_noindent) { + /* long word, no indentation in next line */ + xpos = 0; + sub->continues = TRUE; + } + + sub->start = ptr; + sub->indent = xpos; + sub->color = color; + + lines = g_slist_append(lines, sub); + linecount++; + + last_space = 0; + continue; + } + + xpos++; + if (*ptr++ == ' ') { + last_space = xpos-1; + last_space_ptr = ptr; + last_color = color; + } + } + + rec = g_malloc(sizeof(LINE_CACHE_REC)-sizeof(LINE_CACHE_SUB_REC) + + sizeof(LINE_CACHE_SUB_REC) * (linecount-1)); + rec->last_access = time(NULL); + rec->count = linecount; + + if (rec->count > 1) { + for (pos = 0; lines != NULL; pos++) { + memcpy(&rec->lines[pos], lines->data, + sizeof(LINE_CACHE_SUB_REC)); + + g_free(lines->data); + lines = g_slist_remove(lines, lines->data); + } + } + + g_hash_table_insert(view->cache->line_cache, line, rec); + return rec; +} + +static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, + int subline, int ypos, int max) +{ + LINE_CACHE_REC *cache; + const unsigned char *text, *text_newline; + char *tmp; + int xpos, color, drawcount, first; + + cache = textbuffer_view_get_line_cache(view, line); + if (subline >= cache->count) + return 0; + + xpos = color = drawcount = 0; first = TRUE; + text_newline = text = + subline == 0 ? line->text : cache->lines[subline-1].start; + for (;;) { + if (text == text_newline) { + if (first) + first = FALSE; + else { + ypos++; + if (--max == 0) + break; + } + + if (subline > 0) { + xpos = cache->lines[subline-1].indent; + color = cache->lines[subline-1].color; + } + + set_color(view->window, 0); + wmove(view->window, ypos, 0); + wclrtoeol(view->window); + + wmove(view->window, ypos, xpos); + set_color(view->window, color); + + /* get the beginning of the next subline */ + text_newline = subline == cache->count-1 ? NULL : + cache->lines[subline].start; + + drawcount++; + subline++; + } + + if (*text == '\0') { + /* command */ + text++; + if (*text == LINE_CMD_EOL || *text == LINE_CMD_FORMAT) + break; + + if ((*text & 0x80) == 0) { + /* set color */ + color = (color & ATTR_UNDERLINE) | *text; + } else if (*text == LINE_CMD_CONTINUE) { + /* jump to next block */ + memcpy(&tmp, text+1, sizeof(unsigned char *)); + text = tmp; + continue; + } else switch (*text) { + case LINE_CMD_UNDERLINE: + color ^= ATTR_UNDERLINE; + break; + case LINE_CMD_COLOR0: + color = color & ATTR_UNDERLINE; + break; + case LINE_CMD_COLOR8: + color &= 0xfff0; + color |= 8|ATTR_COLOR8; + break; + case LINE_CMD_BLINK: + color |= 0x80; + break; + } + set_color(view->window, color); + text++; + continue; + } + + if ((*text & 127) >= 32) + waddch(view->window, *text); + else { + /* low-ascii */ + set_color(view->window, ATTR_REVERSE); + waddch(view->window, (*text & 127)+'A'-1); + set_color(view->window, color); + } + text++; + } + + return drawcount; +} + +/* Recalculate view's bottom line information - try to keep the + original if possible */ +static void textbuffer_view_init_bottom(TEXT_BUFFER_VIEW_REC *view) +{ + GList *tmp; + int linecount, total; + + if (view->empty_linecount == 0) { + /* no empty lines in screen, no need to try to keep + the old bottom startline */ + view->bottom_startline = NULL; + } + + total = 0; + tmp = g_list_last(view->buffer->lines); + for (; tmp != NULL; tmp = tmp->prev) { + LINE_REC *line = tmp->data; + + linecount = view_get_linecount(view, line); + if (tmp == view->bottom_startline) { + /* keep the old one, make sure that subline is ok */ + if (view->bottom_subline > linecount) + view->bottom_subline = linecount; + view->empty_linecount = view->height - total - + (linecount-view->bottom_subline); + return; + } + + total += linecount; + if (total >= view->height) { + view->bottom_startline = tmp; + view->bottom_subline = total - view->height; + view->empty_linecount = 0; + return; + } + } + + /* not enough lines so we must be at the beginning of the buffer */ + view->bottom_startline = view->buffer->lines; + view->bottom_subline = 0; + view->empty_linecount = view->height - total; +} + +static void textbuffer_view_init_ypos(TEXT_BUFFER_VIEW_REC *view) +{ + GList *tmp; + + g_return_if_fail(view != NULL); + + view->ypos = -view->subline-1; + for (tmp = view->startline; tmp != NULL; tmp = tmp->next) + view->ypos += view_get_linecount(view, tmp->data); +} + +/* Create new view. */ +TEXT_BUFFER_VIEW_REC *textbuffer_view_create(TEXT_BUFFER_REC *buffer, + int width, int height, + int default_indent, + int longword_noindent) +{ + TEXT_BUFFER_VIEW_REC *view; + + g_return_val_if_fail(buffer != NULL, NULL); + g_return_val_if_fail(width > 0, NULL); + + view = g_new0(TEXT_BUFFER_VIEW_REC, 1); + view->buffer = buffer; + view->siblings = textbuffer_get_views(buffer); + + view->width = width; + view->height = height; + view->default_indent = default_indent; + view->longword_noindent = longword_noindent; + + view->cache = textbuffer_cache_get(view->siblings, width); + textbuffer_view_init_bottom(view); + + view->startline = view->bottom_startline; + view->subline = view->bottom_subline; + view->bottom = TRUE; + + textbuffer_view_init_ypos(view); + + view->bookmarks = g_hash_table_new((GHashFunc) g_str_hash, + (GCompareFunc) g_str_equal); + + views = g_slist_append(views, view); + return view; +} + +/* Destroy the view. */ +void textbuffer_view_destroy(TEXT_BUFFER_VIEW_REC *view) +{ + GSList *tmp; + + g_return_if_fail(view != NULL); + + views = g_slist_remove(views, view); + + if (view->siblings == NULL) { + /* last view for textbuffer, destroy */ + textbuffer_destroy(view->buffer); + } else { + /* remove ourself from siblings lists */ + for (tmp = view->siblings; tmp != NULL; tmp = tmp->next) { + TEXT_BUFFER_VIEW_REC *rec = tmp->data; + + rec->siblings = g_slist_remove(rec->siblings, view); + } + g_slist_free(view->siblings); + } + + g_hash_table_foreach(view->bookmarks, (GHFunc) g_free, NULL); + g_hash_table_destroy(view->bookmarks); + + textbuffer_cache_unref(view->cache); + g_free(view); +} + +/* Change the default indent position */ +void textbuffer_view_set_default_indent(TEXT_BUFFER_VIEW_REC *view, + int default_indent, + int longword_noindent) +{ + view->default_indent = default_indent; + view->longword_noindent = longword_noindent; +} + +static int view_get_linecount_all(TEXT_BUFFER_VIEW_REC *view, GList *lines) +{ + int linecount; + + linecount = 0; + while (lines != NULL) { + linecount += view_get_linecount(view, lines->data); + lines = lines->next; + } + + return linecount; +} + +static void view_draw(TEXT_BUFFER_VIEW_REC *view, GList *line, + int subline, int ypos, int lines) +{ + int linecount; + + while (line != NULL && lines > 0) { + LINE_REC *rec = line->data; + + linecount = view_line_draw(view, rec, subline, ypos, lines); + ypos += linecount; lines -= linecount; + + subline = 0; + line = line->next; + } + + /* clear the rest of the view */ + while (lines > 0) { + wmove(view->window, ypos, 0); + wclrtoeol(view->window); + ypos++; lines--; + } +} + +#define view_draw_top(view, lines) \ + view_draw(view, (view)->startline, (view)->subline, 0, lines) + +static void view_draw_bottom(TEXT_BUFFER_VIEW_REC *view, int lines) +{ + GList *line; + int ypos, maxline, subline, linecount; + + maxline = view->height-lines; + line = view->startline; ypos = -view->subline; subline = 0; + while (line != NULL && ypos < maxline) { + linecount = view_get_linecount(view, line->data); + ypos += linecount; + if (ypos > maxline) { + subline = maxline-(ypos-linecount); + break; + } + line = line->next; + } + + view_draw(view, line, subline, maxline, lines); +} + +/* Returns number of lines actually scrolled */ +static int view_scroll(TEXT_BUFFER_VIEW_REC *view, GList **lines, int *subline, + int scrollcount, int draw_nonclean) +{ + int linecount, realcount, scroll_visible; + + if (*lines == NULL) + return 0; + + /* scroll down */ + scroll_visible = lines == &view->startline; + + realcount = -*subline; + scrollcount += *subline; + *subline = 0; + while (scrollcount > 0) { + linecount = view_get_linecount(view, (*lines)->data); + + if ((scroll_visible && *lines == view->bottom_startline) && + (scrollcount >= view->bottom_subline)) { + *subline = view->bottom_subline; + realcount += view->bottom_subline; + scrollcount = 0; + break; + } + + realcount += linecount; + scrollcount -= linecount; + if (scrollcount < 0) { + realcount += scrollcount; + *subline = linecount+scrollcount; + scrollcount = 0; + break; + } + + *lines = (*lines)->next; + } + + /* scroll up */ + while (scrollcount < 0 && (*lines)->prev != NULL) { + *lines = (*lines)->prev; + linecount = view_get_linecount(view, (*lines)->data); + + realcount -= linecount; + scrollcount += linecount; + if (scrollcount > 0) { + realcount += scrollcount; + *subline = scrollcount; + break; + } + } + + if (scroll_visible && realcount != 0 && view->window != NULL) { + if (realcount <= -view->height || realcount >= view->height) { + /* scrolled more than screenful, redraw the + whole view */ + textbuffer_view_redraw(view); + } else { + scrollok(view->window, TRUE); + wscrl(view->window, realcount); + scrollok(view->window, FALSE); + + if (draw_nonclean) { + if (realcount < 0) + view_draw_top(view, -realcount); + else + view_draw_bottom(view, realcount); + } + + screen_refresh(view->window); + } + } + + return realcount >= 0 ? realcount : -realcount; +} + +/* Resize the view. */ +void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height) +{ + int linecount; + + g_return_if_fail(view != NULL); + g_return_if_fail(width > 0); + + if (view->buffer->lines == NULL) + return; + + if (view->width != width) { + /* line cache needs to be recreated */ + textbuffer_cache_unref(view->cache); + view->cache = textbuffer_cache_get(view->siblings, width); + } + + view->width = width; + view->height = height; + + textbuffer_view_init_bottom(view); + + /* check that we didn't scroll lower than bottom startline.. */ + if (g_list_find(view->bottom_startline->next, + view->startline->data) != NULL) { + view->startline = view->bottom_startline; + view->subline = view->bottom_subline; + } else if (view->startline == view->bottom_startline && + view->subline > view->bottom_subline) { + view->subline = view->bottom_subline; + } else { + /* make sure the subline is still in allowed range */ + linecount = view_get_linecount(view, view->startline->data); + if (view->subline > linecount) + view->subline = linecount; + } + + textbuffer_view_init_ypos(view); + if (view->bottom && !view_is_bottom(view)) { + /* we scrolled to far up, need to get down. go right over + the empty lines if there's any */ + view->startline = view->bottom_startline; + view->subline = view->bottom_subline; + if (view->empty_linecount > 0) { + view_scroll(view, &view->startline, &view->subline, + -view->empty_linecount, FALSE); + } + textbuffer_view_init_ypos(view); + } + + view->bottom = view_is_bottom(view); + if (view->bottom) { + /* check if we left empty space at the bottom.. */ + linecount = view_get_linecount_all(view, view->startline) - + view->subline; + if (view->empty_linecount < view->height-linecount) + view->empty_linecount = view->height-linecount; + } + + textbuffer_view_redraw(view); +} + +/* Clear the view, don't actually remove any lines from buffer. */ +void textbuffer_view_clear(TEXT_BUFFER_VIEW_REC *view) +{ + g_return_if_fail(view != NULL); + + view->ypos = -1; + view->bottom_startline = view->startline = + g_list_last(view->buffer->lines); + view->bottom_subline = view->subline = + view->buffer->cur_line == NULL ? 0 : + view_get_linecount(view, view->buffer->cur_line); + view->empty_linecount = view->height; + view->bottom = TRUE; + + textbuffer_view_redraw(view); +} + +/* Scroll the view up/down */ +void textbuffer_view_scroll(TEXT_BUFFER_VIEW_REC *view, int lines) +{ + int count; + + g_return_if_fail(view != NULL); + + count = view_scroll(view, &view->startline, &view->subline, + lines, TRUE); + view->ypos += lines < 0 ? count : -count; + view->bottom = view_is_bottom(view); + + if (view->window != NULL) + screen_refresh(view->window); +} + +/* Scroll to specified line */ +void textbuffer_view_scroll_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) +{ + GList *tmp; + + g_return_if_fail(view != NULL); + + if (g_list_find(view->bottom_startline->next, line) != NULL) { + view->startline = view->bottom_startline; + view->subline = view->bottom_subline; + } else { + for (tmp = view->buffer->lines; tmp != NULL; tmp = tmp->next) { + LINE_REC *rec = tmp->data; + + if (rec == line) { + view->startline = tmp; + view->subline = 0; + break; + } + } + } + + textbuffer_view_init_ypos(view); + view->bottom = view_is_bottom(view); + + textbuffer_view_redraw(view); +} + +/* Return line cache */ +LINE_CACHE_REC *textbuffer_view_get_line_cache(TEXT_BUFFER_VIEW_REC *view, + LINE_REC *line) +{ + LINE_CACHE_REC *cache; + + g_return_val_if_fail(view != NULL, NULL); + g_return_val_if_fail(line != NULL, NULL); + + cache = g_hash_table_lookup(view->cache->line_cache, line); + if (cache == NULL) + cache = view_update_line_cache(view, line); + else + cache->last_access = time(NULL); + + return cache; +} + +static void view_remove_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, + unsigned char update_counter) +{ + LINE_CACHE_REC *cache; + + if (view->cache->update_counter == update_counter) + return; + view->cache->update_counter = update_counter; + + cache = g_hash_table_lookup(view->cache->line_cache, line); + if (cache != NULL) { + g_free(cache); + g_hash_table_remove(view->cache->line_cache, line); + } +} + +static void view_update_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, + unsigned char update_counter) +{ + view_remove_cache(view, line, update_counter); + + if (view->buffer->cur_line == line) + view->cache->last_linecount = view_get_linecount(view, line); +} + +static void view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) +{ + int linecount, ypos, subline; + + if (view->bottom_startline == NULL) { + view->startline = view->bottom_startline = + view->buffer->lines; + } + + if (view->buffer->cur_line != line && + g_list_find(view->bottom_startline, line) == NULL) + return; + + linecount = view->cache->last_linecount; + view->ypos += linecount; + if (view->empty_linecount > 0) { + view->empty_linecount -= linecount; + if (view->empty_linecount >= 0) + linecount = 0; + else { + linecount = -view->empty_linecount; + view->empty_linecount = 0; + } + } + + if (linecount > 0) { + view_scroll(view, &view->bottom_startline, + &view->bottom_subline, linecount, FALSE); + } + + if (view->bottom) { + if (view->ypos >= view->height) { + linecount = view->ypos-view->height+1; + view_scroll(view, &view->startline, + &view->subline, linecount, FALSE); + view->ypos -= linecount; + } + + if (view->window != NULL) { + ypos = view->ypos+1 - view->cache->last_linecount; + if (ypos >= 0) + subline = 0; + else { + subline = -ypos; + ypos = 0; + } + view_line_draw(view, line, subline, ypos, + view->height - ypos); + } + } + + if (view->window != NULL) + screen_refresh(view->window); +} + +/* Update some line in the buffer which has been modified using + textbuffer_append() or textbuffer_insert(). */ +void textbuffer_view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) +{ + GSList *tmp; + unsigned char update_counter; + + g_return_if_fail(view != NULL); + g_return_if_fail(line != NULL); + + if (!view->buffer->last_eol) + return; + + update_counter = view->cache->update_counter+1; + view_update_cache(view, line, update_counter); + view_insert_line(view, line); + + for (tmp = view->siblings; tmp != NULL; tmp = tmp->next) { + TEXT_BUFFER_VIEW_REC *rec = tmp->data; + + view_update_cache(rec, line, update_counter); + view_insert_line(rec, line); + } +} + +typedef struct { + LINE_REC *remove_line; + GSList *remove_list; +} BOOKMARK_FIND_REC; + +static void bookmark_check_remove(char *key, LINE_REC *line, + BOOKMARK_FIND_REC *rec) +{ + if (line == rec->remove_line) + rec->remove_list = g_slist_append(rec->remove_list, key); +} + +static void view_bookmarks_check(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) +{ + BOOKMARK_FIND_REC rec; + LINE_REC *newline; + GSList *tmp; + + rec.remove_line = line; + rec.remove_list = NULL; + g_hash_table_foreach(view->bookmarks, + (GHFunc) bookmark_check_remove, &rec); + + if (rec.remove_list != NULL) { + GList *pos = g_list_find(view->buffer->lines, line); + + newline = pos == NULL || pos->prev == NULL ? NULL : + (pos->next == NULL ? pos->prev->data : + pos->next->data); + for (tmp = rec.remove_list; tmp != NULL; tmp = tmp->next) { + g_hash_table_remove(view->bookmarks, tmp->data); + if (newline != NULL) { + g_hash_table_insert(view->bookmarks, + tmp->data, newline); + } + } + g_slist_free(rec.remove_list); + } +} + +/* Return number of real lines `lines' list takes - + stops counting when the height reaches the view height */ +static int view_get_lines_height(TEXT_BUFFER_VIEW_REC *view, + GList *lines, int subline, + LINE_REC *skip_line) +{ + int height, linecount; + + height = -subline; + while (lines != NULL && height < view->height) { + LINE_REC *line = lines->data; + + if (line != skip_line) { + linecount = view_get_linecount(view, line); + height += linecount; + } + lines = lines->next; + } + + return height < view->height ? height : view->height; +} + +static void view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, + int linecount) +{ + int realcount, scroll; + + view_bookmarks_check(view, line); + + if (view->buffer->cur_line == line) { + /* the last line is being removed */ + LINE_REC *prevline; + + prevline = view->buffer->lines->data == line ? NULL : + g_list_last(view->bottom_startline)->data; + view->cache->last_linecount = prevline == NULL ? 0 : + view_get_linecount(view, prevline); + } + + if (line == view->buffer->lines->data) { + /* first line in the buffer - this is the most commonly + removed line.. */ + if (view->bottom_startline->data == line) { + /* very small scrollback.. */ + view->bottom_startline = view->bottom_startline->next; + view->bottom_subline = 0; + } + + if (view->startline->data == line) { + /* removing the first line in screen */ + realcount = view_scroll(view, &view->startline, + &view->subline, + linecount, TRUE); + view->ypos -= realcount; + view->empty_linecount += linecount-realcount; + } + } else if (g_list_find(view->bottom_startline, line) != NULL) { + realcount = view_scroll(view, &view->bottom_startline, + &view->bottom_subline, + -linecount, FALSE); + if (view->bottom) { + /* we're at the bottom, remove the same amount as + from bottom_startline */ + view_scroll(view, &view->startline, + &view->subline, -linecount, TRUE); + view->ypos -= linecount-realcount; + } else { + if (view->startline->data == line) { + view->startline = + view->startline->next != NULL ? + view->startline->next : + view->startline->prev; + view->subline = 0; + } + scroll = view->height - + view_get_lines_height(view, view->startline, + view->subline, line); + if (scroll > 0) { + view_scroll(view, &view->startline, + &view->subline, -scroll, TRUE); + view->ypos -= scroll; + } + } + view->empty_linecount += linecount-realcount; + } + + view->bottom = view_is_bottom(view); + if (view->window != NULL) + screen_refresh(view->window); +} + +/* Remove one line from buffer. */ +void textbuffer_view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) +{ + GSList *tmp; + unsigned char update_counter; + int linecount; + + g_return_if_fail(view != NULL); + g_return_if_fail(line != NULL); + + linecount = view_get_linecount(view, line); + update_counter = view->cache->update_counter+1; + + view_remove_line(view, line, linecount); + view_remove_cache(view, line, update_counter); + + for (tmp = view->siblings; tmp != NULL; tmp = tmp->next) { + TEXT_BUFFER_VIEW_REC *rec = tmp->data; + + view_remove_line(rec, line, linecount); + view_remove_cache(rec, line, update_counter); + } + + textbuffer_remove(view->buffer, line); +} + +/* Remove all lines from buffer. */ +void textbuffer_view_remove_all_lines(TEXT_BUFFER_VIEW_REC *view) +{ + GSList *tmp; + + g_return_if_fail(view != NULL); + + textbuffer_remove_all_lines(view->buffer); + + /* destroy line caches - note that you can't do simultaneously + unrefs + cache_get()s or it will keep using the old caches */ + textbuffer_cache_unref(view->cache); + g_slist_foreach(view->siblings, (GFunc) textbuffer_cache_unref, NULL); + + /* recreate caches, clear screens */ + view->cache = textbuffer_cache_get(view->siblings, view->width); + textbuffer_view_clear(view); + + for (tmp = view->siblings; tmp != NULL; tmp = tmp->next) { + TEXT_BUFFER_VIEW_REC *rec = tmp->data; + + rec->cache = textbuffer_cache_get(rec->siblings, rec->width); + textbuffer_view_clear(rec); + } +} + +/* Set a bookmark in view */ +void textbuffer_view_set_bookmark(TEXT_BUFFER_VIEW_REC *view, + const char *name, LINE_REC *line) +{ + gpointer key, value; + + g_return_if_fail(view != NULL); + g_return_if_fail(name != NULL); + + if (g_hash_table_lookup_extended(view->bookmarks, name, + &key, &value)) { + g_hash_table_remove(view->bookmarks, key); + g_free(key); + } + + g_hash_table_insert(view->bookmarks, g_strdup(name), line); +} + +/* Set a bookmark in view to the bottom line */ +void textbuffer_view_set_bookmark_bottom(TEXT_BUFFER_VIEW_REC *view, + const char *name) +{ + LINE_REC *line; + + g_return_if_fail(view != NULL); + g_return_if_fail(name != NULL); + + if (view->bottom_startline != NULL) { + line = g_list_last(view->bottom_startline)->data; + textbuffer_view_set_bookmark(view, name, line); + } +} + +/* Return the line for bookmark */ +LINE_REC *textbuffer_view_get_bookmark(TEXT_BUFFER_VIEW_REC *view, + const char *name) +{ + g_return_val_if_fail(view != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + + return g_hash_table_lookup(view->bookmarks, name); +} + +/* Specify window where the changes in view should be drawn, + NULL disables it. */ +void textbuffer_view_set_window(TEXT_BUFFER_VIEW_REC *view, WINDOW *window) +{ + g_return_if_fail(view != NULL); + + if (view->window != window) { + view->window = window; + if (window != NULL) + textbuffer_view_redraw(view); + } +} + +/* Redraw a view to window */ +void textbuffer_view_redraw(TEXT_BUFFER_VIEW_REC *view) +{ + g_return_if_fail(view != NULL); + + if (view->window != NULL) { + werase(view->window); + view_draw_top(view, view->height); + screen_refresh(view->window); + } +} + +static int line_cache_check_remove(void *key, LINE_CACHE_REC *cache, + time_t *now) +{ + if (cache->last_access+LINE_CACHE_KEEP_TIME > *now) + return FALSE; + + line_cache_destroy(NULL, cache); + return TRUE; +} + +static int sig_check_linecache(void) +{ + GSList *tmp, *caches; + time_t now; + + now = time(NULL); caches = NULL; + for (tmp = views; tmp != NULL; tmp = tmp->next) { + TEXT_BUFFER_VIEW_REC *rec = tmp->data; + + if (g_slist_find(caches, rec->cache) != NULL) + continue; + + caches = g_slist_append(caches, rec->cache); + g_hash_table_foreach_remove(rec->cache->line_cache, + (GHRFunc) line_cache_check_remove, + &now); + } + return 1; +} + +void textbuffer_view_init(void) +{ + linecache_tag = g_timeout_add(LINE_CACHE_CHECK_TIME, (GSourceFunc) sig_check_linecache, NULL); +} + +void textbuffer_view_deinit(void) +{ + g_source_remove(linecache_tag); +} diff --git a/apps/irssi/src/fe-text/textbuffer-view.h b/apps/irssi/src/fe-text/textbuffer-view.h new file mode 100644 index 00000000..21ed28cf --- /dev/null +++ b/apps/irssi/src/fe-text/textbuffer-view.h @@ -0,0 +1,131 @@ +#ifndef __TEXTBUFFER_VIEW_H +#define __TEXTBUFFER_VIEW_H + +#include "textbuffer.h" +#include "screen.h" + +typedef struct { + unsigned char *start; + int indent; + int color; + + /* first word in line belong to the end of the last word in + previous line */ + unsigned int continues:1; +} LINE_CACHE_SUB_REC; + +typedef struct { + time_t last_access; + + int count; /* number of real lines */ + + /* variable sized array, actually. starts from the second line, + so size of it is count-1 */ + LINE_CACHE_SUB_REC lines[1]; +} LINE_CACHE_REC; + +typedef struct { + int refcount; + int width; + + GHashTable *line_cache; + + /* should contain the same value for each cache that uses the + same buffer */ + unsigned char update_counter; + /* number of real lines used by the last line in buffer */ + int last_linecount; +} TEXT_BUFFER_CACHE_REC; + +typedef struct { + TEXT_BUFFER_REC *buffer; + GSList *siblings; /* other views that use the same buffer */ + + WINDOW *window; + int width, height; + + int default_indent; + int longword_noindent:1; + + TEXT_BUFFER_CACHE_REC *cache; + int ypos; /* cursor position - visible area is 0..height-1 */ + + GList *startline; /* line at the top of the screen */ + int subline; /* number of "real lines" to skip from `startline' */ + + /* marks the bottom of the text buffer */ + GList *bottom_startline; + int bottom_subline; + + /* how many empty lines are in screen. a screenful when started + or used /CLEAR */ + int empty_linecount; + /* window is at the bottom of the text buffer */ + unsigned int bottom:1; + + /* Bookmarks to the lines in the buffer - removed automatically + when the line gets removed from buffer */ + GHashTable *bookmarks; +} TEXT_BUFFER_VIEW_REC; + +/* Create new view. */ +TEXT_BUFFER_VIEW_REC *textbuffer_view_create(TEXT_BUFFER_REC *buffer, + int width, int height, + int default_indent, + int longword_noindent); +/* Destroy the view. */ +void textbuffer_view_destroy(TEXT_BUFFER_VIEW_REC *view); +/* Change the default indent position */ +void textbuffer_view_set_default_indent(TEXT_BUFFER_VIEW_REC *view, + int default_indent, + int longword_noindent); + +/* Resize the view. */ +void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height); +/* Clear the view, don't actually remove any lines from buffer. */ +void textbuffer_view_clear(TEXT_BUFFER_VIEW_REC *view); + +#define textbuffer_view_get_lines(view) \ + ((view)->buffer->lines) + +/* Scroll the view up/down */ +void textbuffer_view_scroll(TEXT_BUFFER_VIEW_REC *view, int lines); +/* Scroll to specified line */ +void textbuffer_view_scroll_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line); +/* Return line cache */ +LINE_CACHE_REC *textbuffer_view_get_line_cache(TEXT_BUFFER_VIEW_REC *view, + LINE_REC *line); + +/* + Functions for manipulating the text buffer, using these commands update + all views that use the buffer. +*/ + +/* Update some line in the buffer which has been modified using + textbuffer_append() or textbuffer_insert(). */ +void textbuffer_view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line); +/* Remove one line from buffer. */ +void textbuffer_view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line); +/* Remove all lines from buffer. */ +void textbuffer_view_remove_all_lines(TEXT_BUFFER_VIEW_REC *view); + +/* Set a bookmark in view */ +void textbuffer_view_set_bookmark(TEXT_BUFFER_VIEW_REC *view, + const char *name, LINE_REC *line); +/* Set a bookmark in view to the bottom line */ +void textbuffer_view_set_bookmark_bottom(TEXT_BUFFER_VIEW_REC *view, + const char *name); +/* Return the line for bookmark */ +LINE_REC *textbuffer_view_get_bookmark(TEXT_BUFFER_VIEW_REC *view, + const char *name); + +/* Specify window where the changes in view should be drawn, + NULL disables it. */ +void textbuffer_view_set_window(TEXT_BUFFER_VIEW_REC *view, WINDOW *window); +/* Redraw the view */ +void textbuffer_view_redraw(TEXT_BUFFER_VIEW_REC *view); + +void textbuffer_view_init(void); +void textbuffer_view_deinit(void); + +#endif diff --git a/apps/irssi/src/fe-text/textbuffer.c b/apps/irssi/src/fe-text/textbuffer.c new file mode 100644 index 00000000..f97f46c7 --- /dev/null +++ b/apps/irssi/src/fe-text/textbuffer.c @@ -0,0 +1,613 @@ +/* + textbuffer.c : Text buffer handling + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "misc.h" +#include "formats.h" + +#include "textbuffer.h" + +#ifdef HAVE_REGEX_H +# include +#endif + +#define TEXT_CHUNK_USABLE_SIZE (LINE_TEXT_CHUNK_SIZE-2-(int)sizeof(char*)) + +static GMemChunk *buffer_chunk, *line_chunk, *text_chunk; + +TEXT_BUFFER_REC *textbuffer_create(void) +{ + TEXT_BUFFER_REC *buffer; + + buffer = g_mem_chunk_alloc0(buffer_chunk); + buffer->last_eol = TRUE; + return buffer; +} + +void textbuffer_destroy(TEXT_BUFFER_REC *buffer) +{ + g_return_if_fail(buffer != NULL); + + textbuffer_remove_all_lines(buffer); + g_mem_chunk_free(buffer_chunk, buffer); +} + +static TEXT_CHUNK_REC *text_chunk_find(TEXT_BUFFER_REC *buffer, + const unsigned char *data) +{ + GSList *tmp; + + for (tmp = buffer->text_chunks; tmp != NULL; tmp = tmp->next) { + TEXT_CHUNK_REC *rec = tmp->data; + + if (data >= rec->buffer && + data < rec->buffer+sizeof(rec->buffer)) + return rec; + } + + return NULL; +} + +#define mark_temp_eol(chunk) G_STMT_START { \ + (chunk)->buffer[(chunk)->pos] = 0; \ + (chunk)->buffer[(chunk)->pos+1] = LINE_CMD_EOL; \ + } G_STMT_END + +static TEXT_CHUNK_REC *text_chunk_create(TEXT_BUFFER_REC *buffer) +{ + TEXT_CHUNK_REC *rec; + char *buf, *ptr, **pptr; + + rec = g_mem_chunk_alloc(text_chunk); + rec->pos = 0; + rec->refcount = 0; + + if (buffer->cur_line != NULL && buffer->cur_line->text != NULL) { + /* create a link to new block from the old block */ + buf = buffer->cur_text->buffer + buffer->cur_text->pos; + *buf++ = 0; *buf++ = (char) LINE_CMD_CONTINUE; + + /* we want to store pointer to beginning of the new text + block to char* buffer. this probably isn't ANSI-C + compatible, and trying this without the pptr variable + breaks at least NetBSD/Alpha, so don't go "optimize" + it :) */ + ptr = rec->buffer; pptr = &ptr; + memcpy(buf, pptr, sizeof(char *)); + } else { + /* just to be safe */ + mark_temp_eol(rec); + } + + buffer->cur_text = rec; + buffer->text_chunks = g_slist_append(buffer->text_chunks, rec); + return rec; +} + +static void text_chunk_destroy(TEXT_BUFFER_REC *buffer, TEXT_CHUNK_REC *chunk) +{ + buffer->text_chunks = g_slist_remove(buffer->text_chunks, chunk); + g_mem_chunk_free(text_chunk, chunk); +} + +static void text_chunk_line_free(TEXT_BUFFER_REC *buffer, LINE_REC *line) +{ + TEXT_CHUNK_REC *chunk; + const unsigned char *text; + unsigned char *tmp = NULL; + + for (text = line->text;; text++) { + if (*text != '\0') + continue; + + text++; + if (*text == LINE_CMD_CONTINUE || *text == LINE_CMD_EOL) { + if (*text == LINE_CMD_CONTINUE) + memcpy(&tmp, text+1, sizeof(char *)); + + /* free the previous block */ + chunk = text_chunk_find(buffer, text); + if (--chunk->refcount == 0) { + if (buffer->cur_text == chunk) + chunk->pos = 0; + else + text_chunk_destroy(buffer, chunk); + } + + if (*text == LINE_CMD_EOL) + break; + + text = tmp-1; + } + } +} + +static void text_chunk_append(TEXT_BUFFER_REC *buffer, + const char *data, int len) +{ + TEXT_CHUNK_REC *chunk; + int left; + + if (len == 0) + return; + + chunk = buffer->cur_text; + while (chunk->pos + len >= TEXT_CHUNK_USABLE_SIZE) { + left = TEXT_CHUNK_USABLE_SIZE - chunk->pos; + if (data[left-1] == 0) left--; /* don't split the commands */ + + memcpy(chunk->buffer + chunk->pos, data, left); + chunk->pos += left; + + chunk = text_chunk_create(buffer); + chunk->refcount++; + len -= left; data += left; + } + + memcpy(chunk->buffer + chunk->pos, data, len); + chunk->pos += len; + + mark_temp_eol(chunk); +} + +static LINE_REC *textbuffer_line_create(TEXT_BUFFER_REC *buffer) +{ + LINE_REC *rec; + + if (buffer->cur_text == NULL) + text_chunk_create(buffer); + + rec = g_mem_chunk_alloc(line_chunk); + rec->refcount = 1; + rec->text = buffer->cur_text->buffer + buffer->cur_text->pos; + + buffer->cur_text->refcount++; + return rec; +} + +static LINE_REC *textbuffer_line_insert(TEXT_BUFFER_REC *buffer, + LINE_REC *prev) +{ + LINE_REC *line; + + line = textbuffer_line_create(buffer); + if (prev == buffer->cur_line) { + buffer->cur_line = line; + buffer->lines = g_list_append(buffer->lines, buffer->cur_line); + } else { + buffer->lines = g_list_insert(buffer->lines, line, + g_list_index(buffer->lines, prev)+1); + } + buffer->lines_count++; + + return line; +} + +void textbuffer_line_ref(LINE_REC *line) +{ + g_return_if_fail(line != NULL); + + if (++line->refcount == 255) + g_error("line reference counter wrapped - shouldn't happen"); +} + +void textbuffer_line_unref(TEXT_BUFFER_REC *buffer, LINE_REC *line) +{ + g_return_if_fail(buffer != NULL); + g_return_if_fail(line != NULL); + + if (--line->refcount == 0) { + text_chunk_line_free(buffer, line); + g_mem_chunk_free(line_chunk, line); + } +} + +void textbuffer_line_unref_list(TEXT_BUFFER_REC *buffer, GList *list) +{ + g_return_if_fail(buffer != NULL); + + while (list != NULL) { + textbuffer_line_unref(buffer, list->data); + list = list->next; + } +} + +LINE_REC *textbuffer_append(TEXT_BUFFER_REC *buffer, + const unsigned char *data, int len, + LINE_INFO_REC *info) +{ + return textbuffer_insert(buffer, buffer->cur_line, data, len, info); +} + +LINE_REC *textbuffer_insert(TEXT_BUFFER_REC *buffer, LINE_REC *insert_after, + const unsigned char *data, int len, + LINE_INFO_REC *info) +{ + LINE_REC *line; + + g_return_val_if_fail(buffer != NULL, NULL); + g_return_val_if_fail(data != NULL, NULL); + + line = !buffer->last_eol ? insert_after : + textbuffer_line_insert(buffer, insert_after); + + if (info != NULL) + memcpy(&line->info, info, sizeof(line->info)); + + text_chunk_append(buffer, data, len); + + buffer->last_eol = len >= 2 && + data[len-2] == 0 && data[len-1] == LINE_CMD_EOL; + + return line; +} + +void textbuffer_remove(TEXT_BUFFER_REC *buffer, LINE_REC *line) +{ + g_return_if_fail(buffer != NULL); + g_return_if_fail(line != NULL); + + buffer->lines = g_list_remove(buffer->lines, line); + + if (buffer->cur_line == line) { + buffer->cur_line = buffer->lines == NULL ? NULL : + g_list_last(buffer->lines)->data; + } + + buffer->lines_count--; + textbuffer_line_unref(buffer, line); +} + +/* Removes all lines from buffer, ignoring reference counters */ +void textbuffer_remove_all_lines(TEXT_BUFFER_REC *buffer) +{ + GSList *tmp; + + g_return_if_fail(buffer != NULL); + + for (tmp = buffer->text_chunks; tmp != NULL; tmp = tmp->next) + g_mem_chunk_free(text_chunk, tmp->data); + g_slist_free(buffer->text_chunks); + buffer->text_chunks = NULL; + + g_list_free(buffer->lines); + buffer->lines = NULL; + + buffer->cur_line = NULL; + buffer->lines_count = 0; +} + +void textbuffer_line2text(LINE_REC *line, int coloring, GString *str) +{ + unsigned char cmd; + char *ptr, *tmp; + + g_return_if_fail(line != NULL); + g_return_if_fail(str != NULL); + + g_string_truncate(str, 0); + + for (ptr = line->text;;) { + if (*ptr != 0) { + g_string_append_c(str, *ptr); + ptr++; + continue; + } + + ptr++; + cmd = (unsigned char) *ptr; + ptr++; + + if (cmd == LINE_CMD_EOL || cmd == LINE_CMD_FORMAT) { + /* end of line */ + break; + } + + if (cmd == LINE_CMD_CONTINUE) { + /* line continues in another address.. */ + memcpy(&tmp, ptr, sizeof(char *)); + ptr = tmp; + continue; + } + + if (!coloring) { + /* no colors, skip coloring commands */ + continue; + } + + if ((cmd & 0x80) == 0) { + /* set color */ + g_string_sprintfa(str, "\004%c%c", + (cmd & 0x0f)+'0', + ((cmd & 0xf0) >> 4)+'0'); + } else switch (cmd) { + case LINE_CMD_UNDERLINE: + g_string_append_c(str, 31); + break; + case LINE_CMD_COLOR0: + g_string_sprintfa(str, "\004%c%c", + '0', FORMAT_COLOR_NOCHANGE); + break; + case LINE_CMD_COLOR8: + g_string_sprintfa(str, "\004%c%c", + '8', FORMAT_COLOR_NOCHANGE); + break; + case LINE_CMD_BLINK: + g_string_sprintfa(str, "\004%c", FORMAT_STYLE_BLINK); + break; + case LINE_CMD_INDENT: + break; + } + } +} + +GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline, + int level, int nolevel, const char *text, + int regexp, int fullword, int case_sensitive) +{ +#ifdef HAVE_REGEX_H + regex_t preg; +#endif + GList *line, *tmp; + GList *matches; + GString *str; + + g_return_val_if_fail(buffer != NULL, NULL); + g_return_val_if_fail(text != NULL, NULL); + + if (regexp) { +#ifdef HAVE_REGEX_H + int flags = REG_EXTENDED | REG_NOSUB | + (case_sensitive ? 0 : REG_ICASE); + if (regcomp(&preg, text, flags) != 0) + return NULL; +#else + return NULL; +#endif + } + + matches = NULL; + str = g_string_new(NULL); + + line = g_list_find(buffer->lines, startline); + if (line == NULL) + line = buffer->lines; + + for (tmp = line; tmp != NULL; tmp = tmp->next) { + LINE_REC *rec = tmp->data; + + if ((rec->info.level & level) == 0 || + (rec->info.level & nolevel) != 0) + continue; + + if (*text == '\0') { + /* no search word, everything matches */ + textbuffer_line_ref(rec); + matches = g_list_append(matches, rec); + continue; + } + + textbuffer_line2text(rec, FALSE, str); + + if ( +#ifdef HAVE_REGEX_H + regexp ? regexec(&preg, str->str, 0, NULL, 0) == 0 : +#endif + fullword ? strstr_full_case(str->str, text, + !case_sensitive) != NULL : + case_sensitive ? strstr(str->str, text) != NULL : + stristr(str->str, text) != NULL) { + /* matched */ + textbuffer_line_ref(rec); + matches = g_list_append(matches, rec); + } + } +#ifdef HAVE_REGEX_H + if (regexp) regfree(&preg); +#endif + g_string_free(str, TRUE); + return matches; +} + +#if 0 /* FIXME: saving formats is broken */ +static char *line_read_format(unsigned const char **text) +{ + GString *str; + char *ret; + + str = g_string_new(NULL); + for (;;) { + if (**text == '\0') { + if ((*text)[1] == LINE_CMD_EOL) { + /* leave text at \0 */ + break; + } + if ((*text)[1] == LINE_CMD_FORMAT_CONT) { + /* leave text at \0 */ + break; + } + (*text)++; + + if (**text == LINE_CMD_FORMAT) { + /* move text to start after \0 */ + (*text)++; + break; + } + + if (**text == LINE_CMD_CONTINUE) { + unsigned char *tmp; + + memcpy(&tmp, (*text)+1, sizeof(char *)); + *text = tmp; + continue; + } else if (**text & 0x80) + (*text)++; + continue; + } + + g_string_append_c(str, (char) **text); + (*text)++; + } + + ret = str->str; + g_string_free(str, FALSE); + return ret; +} + +static char *textbuffer_line_get_format(WINDOW_REC *window, LINE_REC *line, + GString *raw) +{ + const unsigned char *text; + char *module, *format_name, *args[MAX_FORMAT_PARAMS], *ret; + TEXT_DEST_REC dest; + int formatnum, argcount; + + text = (const unsigned char *) line->text; + + /* skip the beginning of the line until we find the format */ + g_free(line_read_format(&text)); + if (text[1] == LINE_CMD_FORMAT_CONT) { + g_string_append_c(raw, '\0'); + g_string_append_c(raw, (char)LINE_CMD_FORMAT_CONT); + return NULL; + } + + /* read format information */ + module = line_read_format(&text); + format_name = line_read_format(&text); + + if (raw != NULL) { + g_string_append_c(raw, '\0'); + g_string_append_c(raw, (char)LINE_CMD_FORMAT); + + g_string_append(raw, module); + + g_string_append_c(raw, '\0'); + g_string_append_c(raw, (char)LINE_CMD_FORMAT); + + g_string_append(raw, format_name); + } + + formatnum = format_find_tag(module, format_name); + if (formatnum == -1) + ret = NULL; + else { + argcount = 0; + memset(args, 0, sizeof(args)); + while (*text != '\0' || text[1] != LINE_CMD_EOL) { + args[argcount] = line_read_format(&text); + if (raw != NULL) { + g_string_append_c(raw, '\0'); + g_string_append_c(raw, + (char)LINE_CMD_FORMAT); + + g_string_append(raw, args[argcount]); + } + argcount++; + } + + /* get the format text */ + format_create_dest(&dest, NULL, NULL, line->level, window); + ret = format_get_text_theme_charargs(current_theme, + module, &dest, + formatnum, args); + while (argcount > 0) + g_free(args[--argcount]); + } + + g_free(module); + g_free(format_name); + + return ret; +} + +void textbuffer_reformat_line(WINDOW_REC *window, LINE_REC *line) +{ + GUI_WINDOW_REC *gui; + TEXT_DEST_REC dest; + GString *raw; + char *str, *tmp, *prestr, *linestart, *leveltag; + + gui = WINDOW_GUI(window); + + raw = g_string_new(NULL); + str = textbuffer_line_get_format(window, line, raw); + + if (str == NULL && raw->len == 2 && + raw->str[1] == (char)LINE_CMD_FORMAT_CONT) { + /* multiline format, format explained in one the + following lines. remove this line. */ + textbuffer_line_remove(window, line, FALSE); + } else if (str != NULL) { + /* FIXME: ugly ugly .. and this can't handle + non-formatted lines.. */ + g_string_append_c(raw, '\0'); + g_string_append_c(raw, (char)LINE_CMD_EOL); + + textbuffer_line_text_free(gui, line); + + gui->temp_line = line; + gui->temp_line->text = gui->cur_text->buffer+gui->cur_text->pos; + gui->cur_text->lines++; + gui->eol_marked = FALSE; + + format_create_dest(&dest, NULL, NULL, line->level, window); + + linestart = format_get_line_start(current_theme, &dest, line->time); + leveltag = format_get_level_tag(current_theme, &dest); + + prestr = g_strconcat(linestart == NULL ? "" : linestart, + leveltag, NULL); + g_free_not_null(linestart); + g_free_not_null(leveltag); + + tmp = format_add_linestart(str, prestr); + g_free(str); + g_free(prestr); + + format_send_to_gui(&dest, tmp); + g_free(tmp); + + textbuffer_line_append(gui, raw->str, raw->len); + + gui->eol_marked = TRUE; + gui->temp_line = NULL; + } + g_string_free(raw, TRUE); +} +#endif + +void textbuffer_init(void) +{ + buffer_chunk = g_mem_chunk_new("text buffer chunk", + sizeof(TEXT_BUFFER_REC), + sizeof(TEXT_BUFFER_REC)*32, G_ALLOC_AND_FREE); + line_chunk = g_mem_chunk_new("line chunk", sizeof(LINE_REC), + sizeof(LINE_REC)*1024, G_ALLOC_AND_FREE); + text_chunk = g_mem_chunk_new("text chunk", sizeof(TEXT_CHUNK_REC), + sizeof(TEXT_CHUNK_REC)*32, G_ALLOC_AND_FREE); +} + +void textbuffer_deinit(void) +{ + g_mem_chunk_destroy(buffer_chunk); + g_mem_chunk_destroy(line_chunk); + g_mem_chunk_destroy(text_chunk); +} diff --git a/apps/irssi/src/fe-text/textbuffer.h b/apps/irssi/src/fe-text/textbuffer.h new file mode 100644 index 00000000..21f70e26 --- /dev/null +++ b/apps/irssi/src/fe-text/textbuffer.h @@ -0,0 +1,90 @@ +#ifndef __TEXTBUFFER_H +#define __TEXTBUFFER_H + +/* FIXME: Textbuffer code gets a lot faster in some points when I get rid of + GList and make prev/next pointers directly in LINE_REC. However, this + can still wait for a while until I get rid of GList entirely everywhere. */ + +#define LINE_TEXT_CHUNK_SIZE 16384 + +enum { + LINE_CMD_EOL=0x80, /* line ends here */ + LINE_CMD_CONTINUE, /* line continues in next block */ + LINE_CMD_COLOR0, /* change to black, would be same as \0\0 but it breaks things.. */ + LINE_CMD_COLOR8, /* change to dark grey, normally 8 = bold black */ + LINE_CMD_UNDERLINE, /* enable/disable underlining */ + LINE_CMD_INDENT, /* if line is split, indent it at this position */ + LINE_CMD_BLINK, /* blinking background */ + LINE_CMD_FORMAT, /* end of line, but next will come the format that was used to create the + text in format - fields are separated + with \0 and last argument ends with \0. \0 is allowed + anywhere */ + LINE_CMD_FORMAT_CONT /* multiline format, continues to next line */ +}; + +typedef struct { + int level; + time_t time; +} LINE_INFO_REC; + +typedef struct { + /* text in the line. \0 means that the next char will be a + color or command. <= 127 = color or if 8. bit is set, the + first 7 bits are the command. See LINE_CMD_xxxx. + + DO NOT ADD BLACK WITH \0\0 - this will break things. Use + LINE_CMD_COLOR0 instead. */ + unsigned char *text; + unsigned char refcount; + LINE_INFO_REC info; +} LINE_REC; + +typedef struct { + unsigned char buffer[LINE_TEXT_CHUNK_SIZE]; + int pos; + int refcount; +} TEXT_CHUNK_REC; + +typedef struct { + GSList *text_chunks; + GList *lines; + int lines_count; + + LINE_REC *cur_line; + TEXT_CHUNK_REC *cur_text; + + unsigned int last_eol:1; +} TEXT_BUFFER_REC; + +/* Create new buffer */ +TEXT_BUFFER_REC *textbuffer_create(void); +/* Destroy the buffer */ +void textbuffer_destroy(TEXT_BUFFER_REC *buffer); + +void textbuffer_line_ref(LINE_REC *line); +void textbuffer_line_unref(TEXT_BUFFER_REC *buffer, LINE_REC *line); +void textbuffer_line_unref_list(TEXT_BUFFER_REC *buffer, GList *list); + +/* Append text to buffer. When \0 is found at the END OF DATA, a new + line is created. You must send the EOL command before you can do anything + else with the buffer. */ +LINE_REC *textbuffer_append(TEXT_BUFFER_REC *buffer, + const unsigned char *data, int len, + LINE_INFO_REC *info); +LINE_REC *textbuffer_insert(TEXT_BUFFER_REC *buffer, LINE_REC *insert_after, + const unsigned char *data, int len, + LINE_INFO_REC *info); + +void textbuffer_remove(TEXT_BUFFER_REC *buffer, LINE_REC *line); +/* Removes all lines from buffer, ignoring reference counters */ +void textbuffer_remove_all_lines(TEXT_BUFFER_REC *buffer); + +void textbuffer_line2text(LINE_REC *line, int coloring, GString *str); +GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline, + int level, int nolevel, const char *text, + int regexp, int fullword, int case_sensitive); + +void textbuffer_init(void); +void textbuffer_deinit(void); + +#endif diff --git a/apps/irssi/src/lib-config/Makefile.am b/apps/irssi/src/lib-config/Makefile.am new file mode 100644 index 00000000..3907ac3d --- /dev/null +++ b/apps/irssi/src/lib-config/Makefile.am @@ -0,0 +1,15 @@ +noinst_LIBRARIES = libirssi_config.a + +INCLUDES = \ + $(GLIB_CFLAGS) \ + -I$(top_srcdir)/src + +libirssi_config_a_SOURCES = \ + get.c \ + set.c \ + parse.c \ + write.c + +noinst_HEADERS = \ + iconfig.h \ + module.h diff --git a/apps/irssi/src/lib-config/get.c b/apps/irssi/src/lib-config/get.c new file mode 100644 index 00000000..98e2a30b --- /dev/null +++ b/apps/irssi/src/lib-config/get.c @@ -0,0 +1,310 @@ +/* + get.c : irssi configuration - get settings from memory + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" + +CONFIG_NODE *config_node_find(CONFIG_NODE *node, const char *key) +{ + GSList *tmp; + + g_return_val_if_fail(node != NULL, NULL); + g_return_val_if_fail(key != NULL, NULL); + g_return_val_if_fail(is_node_list(node), NULL); + + for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + CONFIG_NODE *node = tmp->data; + + if (node->key != NULL && g_strcasecmp(node->key, key) == 0) + return node; + } + + return NULL; +} + +/* find the section from node - if not found create it unless new_type is -1. + you can also specify in new_type if it's NODE_TYPE_LIST or NODE_TYPE_BLOCK */ +CONFIG_NODE *config_node_section(CONFIG_NODE *parent, const char *key, int new_type) +{ + CONFIG_NODE *node; + + g_return_val_if_fail(parent != NULL, NULL); + g_return_val_if_fail(is_node_list(parent), NULL); + + node = key == NULL ? NULL : config_node_find(parent, key); + if (node != NULL) { + g_return_val_if_fail(new_type == -1 || new_type == node->type, NULL); + return node; + } + + if (new_type == -1) + return NULL; + + node = g_new0(CONFIG_NODE, 1); + parent->value = g_slist_append(parent->value, node); + + node->type = new_type; + node->key = key == NULL ? NULL : g_strdup(key); + + return node; +} + +/* find the section with the whole path. + create the path if necessary `create' is TRUE. */ +CONFIG_NODE *config_node_traverse(CONFIG_REC *rec, const char *section, int create) +{ + CONFIG_NODE *node; + char **list, **tmp, *str; + int is_list, new_type; + + g_return_val_if_fail(rec != NULL, NULL); + + if (section == NULL || *section == '\0') + return rec->mainnode; + + /* check if it already exists in cache */ + node = g_hash_table_lookup(rec->cache, section); + if (node != NULL) return node; + + new_type = -1; + + node = rec->mainnode; + list = g_strsplit(section, "/", -1); + for (tmp = list; *tmp != NULL; tmp++) { + is_list = **tmp == '('; + if (create) new_type = is_list ? NODE_TYPE_LIST : NODE_TYPE_BLOCK; + + node = config_node_section(node, *tmp + is_list, new_type); + if (node == NULL) { + g_strfreev(list); + return NULL; + } + } + g_strfreev(list); + + /* save to cache */ + str = g_strdup(section); + g_hash_table_insert(rec->cache, str, node); + g_hash_table_insert(rec->cache_nodes, node, str); + return node; +} + +char *config_get_str(CONFIG_REC *rec, const char *section, const char *key, const char *def) +{ + CONFIG_NODE *parent, *node; + char *path; + + g_return_val_if_fail(rec != NULL, (char *) def); + g_return_val_if_fail(key != NULL, (char *) def); + + /* check if it already exists in cache */ + path = g_strconcat(section == NULL ? "" : section, "/", key, NULL); + node = g_hash_table_lookup(rec->cache, path); + + if (node != NULL) + g_free(path); + else { + parent = config_node_traverse(rec, section, FALSE); + node = parent == NULL ? NULL : + config_node_find(parent, key); + + /* save to cache */ + if (node == NULL) + g_free(path); + else { + g_hash_table_insert(rec->cache, path, node); + g_hash_table_insert(rec->cache_nodes, node, path); + } + } + + return (node == NULL || !has_node_value(node)) ? (char *) def : node->value; +} + +int config_get_int(CONFIG_REC *rec, const char *section, const char *key, int def) +{ + char *str; + + str = config_get_str(rec, section, key, NULL); + if (str == NULL) return def; + + return atoi(str); +} + +int config_get_bool(CONFIG_REC *rec, const char *section, const char *key, int def) +{ + char *str; + + str = config_get_str(rec, section, key, NULL); + if (str == NULL) return def; + + return toupper(*str) == 'T' || toupper(*str) == 'Y'; +} + +/* Return value of key `value_key' from list item where `key' is `value' */ +const char *config_list_find(CONFIG_REC *rec, const char *section, const char *key, const char *value, const char *value_key) +{ + CONFIG_NODE *node; + + node = config_list_find_node(rec, section, key, value, value_key); + return node != NULL && node->type == NODE_TYPE_KEY ? + node->value : NULL; +} + +/* Like config_list_find(), but return node instead of it's value */ +CONFIG_NODE *config_list_find_node(CONFIG_REC *rec, const char *section, const char *key, const char *value, const char *value_key) +{ + CONFIG_NODE *node, *keynode; + GSList *tmp; + + g_return_val_if_fail(rec != NULL, NULL); + g_return_val_if_fail(key != NULL, NULL); + g_return_val_if_fail(value_key != NULL, NULL); + + node = config_node_traverse(rec, section, FALSE); + if (node == NULL || !is_node_list(node)) return NULL; + + for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + node = tmp->data; + + if (node->type != NODE_TYPE_BLOCK) + continue; + + /* key matches value? */ + keynode = config_node_find(node, key); + if (keynode == NULL || keynode->type != NODE_TYPE_KEY || + g_strcasecmp(keynode->value, value) != 0) continue; + + return config_node_find(node, value_key); + } + + return NULL; +} + +char *config_node_get_str(CONFIG_NODE *parent, const char *key, const char *def) +{ + CONFIG_NODE *node; + + if (parent == NULL) return (char *) def; + + node = config_node_find(parent, key); + return (char *) ((node != NULL && has_node_value(node)) ? + node->value : def); +} + +int config_node_get_int(CONFIG_NODE *parent, const char *key, int def) +{ + char *str; + + str = config_node_get_str(parent, key, NULL); + if (str == NULL) return def; + + return atoi(str); +} + +int config_node_get_bool(CONFIG_NODE *parent, const char *key, int def) +{ + char *str; + + str = config_node_get_str(parent, key, NULL); + if (str == NULL) return def; + + return toupper(*str) == 'T' || toupper(*str) == 'Y' || + (toupper(*str) == 'O' && toupper(str[1]) == 'N'); +} + +/* Get the value of keys `key' and `key_value' and put them to + `ret_key' and `ret_value'. Returns -1 if not found. */ +int config_node_get_keyvalue(CONFIG_NODE *node, const char *key, const char *value_key, char **ret_key, char **ret_value) +{ + CONFIG_NODE *keynode, *valuenode; + GSList *tmp; + + g_return_val_if_fail(node != NULL, -1); + g_return_val_if_fail(key != NULL, -1); + g_return_val_if_fail(value_key != NULL, -1); + g_return_val_if_fail(ret_key != NULL, -1); + g_return_val_if_fail(ret_value != NULL, -1); + + for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + node = tmp->data; + + if (node->type != NODE_TYPE_BLOCK) + continue; + + keynode = config_node_find(node, key); + if (keynode == NULL || keynode->type != NODE_TYPE_KEY) + continue; + + valuenode = config_node_find(node, value_key); + + *ret_key = keynode->key; + *ret_value = valuenode != NULL && valuenode->type == NODE_TYPE_KEY ? + valuenode->value : NULL; + return 0; + } + + return -1; +} + +/* Return all values from from the list `node' in a g_strsplit() array */ +char **config_node_get_list(CONFIG_NODE *node) +{ + GString *values; + GSList *tmp; + char **ret; + + g_return_val_if_fail(node != NULL, NULL); + g_return_val_if_fail(is_node_list(node), NULL); + + /* put values to string */ + values = g_string_new(NULL); + for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + node = tmp->data; + + if (node->type == NODE_TYPE_VALUE) + g_string_sprintfa(values, "%s ", (char *) node->value); + } + + /* split the values to **str array */ + if (values->len == 0) + ret = NULL; + else { + g_string_truncate(values, values->len-1); + ret = g_strsplit(values->str, " ", -1); + } + + g_string_free(values, TRUE); + return ret; +} + +/* Returns n'th node from list. */ +CONFIG_NODE *config_node_index(CONFIG_NODE *node, int index) +{ + GSList *tmp; + + g_return_val_if_fail(node != NULL, NULL); + g_return_val_if_fail(is_node_list(node), NULL); + + for (tmp = node->value; tmp != NULL; tmp = tmp->next, index--) { + if (index == 0) + return tmp->data; + } + + return NULL; +} diff --git a/apps/irssi/src/lib-config/iconfig.h b/apps/irssi/src/lib-config/iconfig.h new file mode 100644 index 00000000..ea2e6c37 --- /dev/null +++ b/apps/irssi/src/lib-config/iconfig.h @@ -0,0 +1,144 @@ +#ifndef __ICONFIG_H +#define __ICONFIG_H + +enum { + NODE_TYPE_KEY, + NODE_TYPE_VALUE, + NODE_TYPE_BLOCK, + NODE_TYPE_LIST, + NODE_TYPE_COMMENT +}; + +#define has_node_value(a) \ + ((a)->type == NODE_TYPE_KEY || (a)->type == NODE_TYPE_VALUE) +#define is_node_list(a) \ + ((a)->type == NODE_TYPE_BLOCK || (a)->type == NODE_TYPE_LIST) + +struct _CONFIG_NODE { + int type; + char *key; + void *value; +}; + +/* a = { x=y; y=z; } + + node1: type = NODE_TYPE_BLOCK, key = "a", value = (GSList *) nodes + nodes: (node2, node3) + node2: type = NODE_TYPE_KEY, key = "x", value = (char *) "y" + node3: type = NODE_TYPE_KEY, key = "y", value = (char *) "z" + + b = ( a, { b=c; d=e; } ) + + node1: type = NODE_TYPE_LIST, key = "b", value = (GSList *) nodes + nodes: (node2, node3) + node2: type = NODE_TYPE_VALUE, key = NULL, value = (char *) "a" + node4: type = NODE_TYPE_BLOCK, key = NULL, value = (GSList *) nodes2 + nodes2: (node4, node5) + node4: type = NODE_TYPE_KEY, key = "b", value = (char *) "c" + node5: type = NODE_TYPE_KEY, key = "d", value = (char *) "e" + + Comments node has key=NULL and value is the comment line. Empty lines are + also in comments so they won't be forgotten when the config file is + written. + +*/ + +struct _CONFIG_REC { + char *fname; + int handle; + int create_mode; + int modifycounter; /* increase every time something is changed */ + + char *last_error; + CONFIG_NODE *mainnode; + GHashTable *cache; /* path -> node (for querying) */ + GHashTable *cache_nodes; /* node -> path (for removing) */ + + GScanner *scanner; + + /* while writing to configuration file.. */ + int tmp_indent_level; /* indentation position */ + int tmp_last_lf; /* last character was a line feed */ +}; + +/* Open configuration. The file is created if it doesn't exist, unless + `create_mode' is -1. `fname' can be NULL if you just want to use + config_parse_data() */ +CONFIG_REC *config_open(const char *fname, int create_mode); +/* Release all memory used by configuration */ +void config_close(CONFIG_REC *rec); +/* Change file name of config file */ +void config_change_file_name(CONFIG_REC *rec, const char *fname, int create_mode); + +/* Parse configuration file */ +int config_parse(CONFIG_REC *rec); +/* Parse configuration found from `data'. `input_name' is specifies the + "configuration name" which is displayed in error messages. */ +int config_parse_data(CONFIG_REC *rec, const char *data, const char *input_name); + +/* Write configuration file. Write to `fname' if it's not NULL. + If `create_mode' is -1, use the one that was given to config_open(). */ +int config_write(CONFIG_REC *rec, const char *fname, int create_mode); + +#define config_last_error(rec) \ + (rec)->last_error + +/* Getting values + + `section' is something like "maingroup/key/subkey", or with lists + "maingroup/(list/subkey" + + `def' is returned if the value is not found. */ +char *config_get_str(CONFIG_REC *rec, const char *section, const char *key, const char *def); +int config_get_int(CONFIG_REC *rec, const char *section, const char *key, int def); +int config_get_bool(CONFIG_REC *rec, const char *section, const char *key, int def); + +/* Return value of key `value_key' from list item where `key' is `value' */ +const char *config_list_find(CONFIG_REC *rec, const char *section, const char *key, const char *value, const char *value_key); +/* Like config_list_find(), but return node instead of it's value */ +CONFIG_NODE *config_list_find_node(CONFIG_REC *rec, const char *section, const char *key, const char *value, const char *value_key); +/* Returns n'th node from list. */ +CONFIG_NODE *config_node_index(CONFIG_NODE *node, int index); + +/* Setting values */ +int config_set_str(CONFIG_REC *rec, const char *section, const char *key, const char *value); +int config_set_int(CONFIG_REC *rec, const char *section, const char *key, int value); +int config_set_bool(CONFIG_REC *rec, const char *section, const char *key, int value); + +/* Handling the configuration directly with nodes - + useful when you need to read all values in a block/list. */ +CONFIG_NODE *config_node_find(CONFIG_NODE *node, const char *key); +/* Find the section from node - if not found create it unless new_type is -1. + You can also specify in new_type if it's NODE_TYPE_LIST or NODE_TYPE_BLOCK */ +CONFIG_NODE *config_node_section(CONFIG_NODE *parent, const char *key, int new_type); +/* Find the section with the whole path. + Create the path if necessary `create' is TRUE. */ +CONFIG_NODE *config_node_traverse(CONFIG_REC *rec, const char *section, int create); +/* Get the value of keys `key' and `key_value' and put them to + `ret_key' and `ret_value'. Returns -1 if not found. */ +int config_node_get_keyvalue(CONFIG_NODE *node, const char *key, const char *value_key, char **ret_key, char **ret_value); +/* Return all values from from the list `node' in a g_strsplit() array */ +char **config_node_get_list(CONFIG_NODE *node); +/* Add all values in `array' to `node' */ +void config_node_add_list(CONFIG_REC *rec, CONFIG_NODE *node, char **array); + +char *config_node_get_str(CONFIG_NODE *parent, const char *key, const char *def); +int config_node_get_int(CONFIG_NODE *parent, const char *key, int def); +int config_node_get_bool(CONFIG_NODE *parent, const char *key, int def); + +void config_node_set_str(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, const char *value); +void config_node_set_int(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int value); +void config_node_set_bool(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int value); + +/* Remove one node from block/list. + ..set_str() with value = NULL does the same. */ +void config_node_remove(CONFIG_REC *rec, CONFIG_NODE *parent, CONFIG_NODE *node); +/* Remove n'th node from a list */ +void config_node_list_remove(CONFIG_REC *rec, CONFIG_NODE *node, int index); + +/* Clear all data inside node, but leave the node */ +void config_node_clear(CONFIG_REC *rec, CONFIG_NODE *node); +/* Clear the entire configuration */ +void config_nodes_remove_all(CONFIG_REC *rec); + +#endif diff --git a/apps/irssi/src/lib-config/module.h b/apps/irssi/src/lib-config/module.h new file mode 100644 index 00000000..22e0e7c7 --- /dev/null +++ b/apps/irssi/src/lib-config/module.h @@ -0,0 +1,6 @@ +#include "common.h" +#include "iconfig.h" + +/* private */ +int config_error(CONFIG_REC *rec, const char *msg); + diff --git a/apps/irssi/src/lib-config/parse.c b/apps/irssi/src/lib-config/parse.c new file mode 100644 index 00000000..883a920b --- /dev/null +++ b/apps/irssi/src/lib-config/parse.c @@ -0,0 +1,360 @@ +/* + parse.c : irssi configuration - parse configuration file + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" + +static int g_istr_equal(gconstpointer v, gconstpointer v2) +{ + return g_strcasecmp((const char *) v, (const char *) v2) == 0; +} + +/* a char* hash function from ASU */ +static unsigned int g_istr_hash(gconstpointer v) +{ + const char *s = (const char *) v; + unsigned int h = 0, g; + + while (*s != '\0') { + h = (h << 4) + toupper(*s); + if ((g = h & 0xf0000000UL)) { + h = h ^ (g >> 24); + h = h ^ g; + } + s++; + } + + return h /* % M */; +} + +int config_error(CONFIG_REC *rec, const char *msg) +{ + g_free_and_null(rec->last_error); + rec->last_error = g_strdup(msg); + return -1; +} + +static int node_add_comment(CONFIG_NODE *parent, const char *str) +{ + CONFIG_NODE *node; + + g_return_val_if_fail(parent != NULL, -1); + + if (!is_node_list(parent)) + return -1; + + node = g_new0(CONFIG_NODE, 1); + node->type = NODE_TYPE_COMMENT; + node->value = str == NULL ? NULL : g_strdup(str); + + parent->value = g_slist_append(parent->value, node); + return 0; +} + +/* same as g_scanner_get_next_token() except skips and reads the comments */ +static void config_parse_get_token(GScanner *scanner, CONFIG_NODE *node) +{ + int prev_empty = FALSE; + + for (;;) { + g_scanner_get_next_token(scanner); + + if (scanner->token == G_TOKEN_COMMENT_SINGLE) + node_add_comment(node, scanner->value.v_string); + else if (scanner->token == '\n') { + if (prev_empty) node_add_comment(node, NULL); + } else { + if (scanner->token == G_TOKEN_INT) { + scanner->token = G_TOKEN_STRING; +#undef g_strdup_printf /* This is free'd by GLib itself */ + scanner->value.v_string = g_strdup_printf("%lu", scanner->value.v_int); +#ifdef MEM_DEBUG +#define g_strdup_printf(a, b...) ig_strdup_printf(__FILE__, __LINE__, a, ##b) +#endif + } + break; + } + + prev_empty = TRUE; + } +} + +/* same as g_scanner_peek_next_token() except skips and reads the comments */ +static void config_parse_peek_token(GScanner *scanner, CONFIG_NODE *node) +{ + int prev_empty = FALSE; + + for (;;) { + g_scanner_peek_next_token(scanner); + + if (scanner->next_token == G_TOKEN_COMMENT_SINGLE) + node_add_comment(node, scanner->next_value.v_string); + else if (scanner->next_token == '\n') { + if (prev_empty) node_add_comment(node, NULL); + } else + break; + + prev_empty = TRUE; + g_scanner_get_next_token(scanner); + } +} + +/* get optional token, optionally warn if it's missing */ +static void config_parse_warn_missing(CONFIG_REC *rec, CONFIG_NODE *node, + GTokenType expected_token, int print_warning) +{ + config_parse_peek_token(rec->scanner, node); + if (rec->scanner->next_token == expected_token) { + g_scanner_get_next_token(rec->scanner); + return; + } + + if (print_warning) + g_scanner_warn(rec->scanner, "Warning: missing '%c'", expected_token); +} + +static void config_parse_loop(CONFIG_REC *rec, CONFIG_NODE *node, GTokenType expect); + +static GTokenType config_parse_symbol(CONFIG_REC *rec, CONFIG_NODE *node) +{ + CONFIG_NODE *newnode; + GTokenType last_char; + int print_warning; + char *key; + + g_return_val_if_fail(rec != NULL, G_TOKEN_ERROR); + g_return_val_if_fail(node != NULL, G_TOKEN_ERROR); + + config_parse_get_token(rec->scanner, node); + + last_char = (GTokenType) (node->type == NODE_TYPE_LIST ? ',' : ';'); + + /* key */ + key = NULL; + if (node->type != NODE_TYPE_LIST && + (rec->scanner->token == G_TOKEN_STRING)) { + key = g_strdup(rec->scanner->value.v_string); + + config_parse_warn_missing(rec, node, '=', TRUE); + config_parse_get_token(rec->scanner, node); + } + + switch (rec->scanner->token) { + case G_TOKEN_STRING: + /* value */ + config_node_set_str(rec, node, key, rec->scanner->value.v_string); + g_free_not_null(key); + + print_warning = TRUE; + if (node->type == NODE_TYPE_LIST) { + /* if it's last item it doesn't need comma */ + config_parse_peek_token(rec->scanner, node); + if (rec->scanner->next_token == ')') + print_warning = FALSE; + } + + config_parse_warn_missing(rec, node, last_char, print_warning); + break; + + case '{': + /* block */ + if (key == NULL && node->type != NODE_TYPE_LIST) + return G_TOKEN_ERROR; + + newnode = config_node_section(node, key, NODE_TYPE_BLOCK); + config_parse_loop(rec, newnode, (GTokenType) '}'); + g_free_not_null(key); + + config_parse_get_token(rec->scanner, node); + if (rec->scanner->token != '}') + return (GTokenType) '}'; + + config_parse_warn_missing(rec, node, last_char, FALSE); + break; + + case '(': + /* list */ + if (key == NULL) + return G_TOKEN_ERROR; + newnode = config_node_section(node, key, NODE_TYPE_LIST); + config_parse_loop(rec, newnode, (GTokenType) ')'); + g_free_not_null(key); + + config_parse_get_token(rec->scanner, node); + if (rec->scanner->token != ')') + return (GTokenType) ')'; + + config_parse_warn_missing(rec, node, last_char, FALSE); + break; + + default: + /* error */ + g_free_not_null(key); + return G_TOKEN_STRING; + } + + return G_TOKEN_NONE; +} + +static void config_parse_loop(CONFIG_REC *rec, CONFIG_NODE *node, GTokenType expect) +{ + GTokenType expected_token; + + g_return_if_fail(rec != NULL); + g_return_if_fail(node != NULL); + + for (;;) { + config_parse_peek_token(rec->scanner, node); + if (rec->scanner->next_token == expect || + rec->scanner->next_token == G_TOKEN_EOF) break; + + expected_token = config_parse_symbol(rec, node); + if (expected_token != G_TOKEN_NONE) { + if (expected_token == G_TOKEN_ERROR) + expected_token = G_TOKEN_NONE; + g_scanner_unexp_token(rec->scanner, expected_token, NULL, "symbol", NULL, NULL, TRUE); + } + } +} + +static void config_parse_error_func(GScanner *scanner, char *message, int is_error) +{ + CONFIG_REC *rec = scanner->user_data; + char *old; + + old = rec->last_error; + rec->last_error = g_strdup_printf("%s%s:%d: %s%s\n", + old == NULL ? "" : old, + scanner->input_name, scanner->line, + is_error ? "error: " : "", + message); + g_free_not_null(old); +} + +void config_parse_init(CONFIG_REC *rec, const char *name) +{ + GScanner *scanner; + + g_free_and_null(rec->last_error); + config_nodes_remove_all(rec); + + rec->scanner = scanner = g_scanner_new(NULL); + scanner->config->skip_comment_single = FALSE; + scanner->config->cset_skip_characters = " \t"; + scanner->config->scan_binary = FALSE; + scanner->config->scan_octal = FALSE; + scanner->config->scan_float = FALSE; + scanner->config->scan_string_sq = TRUE; + scanner->config->scan_string_dq = TRUE; + scanner->config->scan_identifier_1char = TRUE; + scanner->config->identifier_2_string = TRUE; + + scanner->user_data = rec; + scanner->input_name = name; + scanner->msg_handler = (GScannerMsgFunc) config_parse_error_func; +} + +/* Parse configuration file */ +int config_parse(CONFIG_REC *rec) +{ + g_return_val_if_fail(rec != NULL, -1); + g_return_val_if_fail(rec->fname != NULL, -1); + + rec->handle = open(rec->fname, O_RDONLY); + if (rec->handle == -1) + return config_error(rec, g_strerror(errno)); + + config_parse_init(rec, rec->fname); + g_scanner_input_file(rec->scanner, rec->handle); + config_parse_loop(rec, rec->mainnode, G_TOKEN_EOF); + g_scanner_destroy(rec->scanner); + + close(rec->handle); + rec->handle = -1; + + return rec->last_error == NULL ? 0 : -1; +} + +/* Parse configuration found from `data'. `input_name' is specifies the + "configuration name" which is displayed in error messages. */ +int config_parse_data(CONFIG_REC *rec, const char *data, const char *input_name) +{ + config_parse_init(rec, input_name); + g_scanner_input_text(rec->scanner, data, strlen(data)); + config_parse_loop(rec, rec->mainnode, G_TOKEN_EOF); + g_scanner_destroy(rec->scanner); + + return rec->last_error == NULL ? 0 : -1; +} + +/* Open configuration. The file is created if it doesn't exist, unless + `create_mode' is -1. `fname' can be NULL if you just want to use + config_parse_data() */ +CONFIG_REC *config_open(const char *fname, int create_mode) +{ + CONFIG_REC *rec; + int f; + + if (fname != NULL) { + f = open(fname, O_RDONLY | (create_mode != -1 ? O_CREAT : 0), create_mode); + if (f == -1) return NULL; + close(f); + } + + rec = g_new0(CONFIG_REC, 1); + rec->fname = fname == NULL ? NULL : g_strdup(fname); + rec->handle = -1; + rec->create_mode = create_mode; + rec->mainnode = g_new0(CONFIG_NODE, 1); + rec->mainnode->type = NODE_TYPE_BLOCK; + rec->cache = g_hash_table_new((GHashFunc) g_istr_hash, (GCompareFunc) g_istr_equal); + rec->cache_nodes = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal); + + return rec; +} + +/* Release all memory used by configuration */ +void config_close(CONFIG_REC *rec) +{ + g_return_if_fail(rec != NULL); + + config_nodes_remove_all(rec); + g_free(rec->mainnode); + + if (rec->handle != -1) close(rec->handle); + g_hash_table_foreach(rec->cache, (GHFunc) g_free, NULL); + g_hash_table_destroy(rec->cache); + g_hash_table_destroy(rec->cache_nodes); + g_free_not_null(rec->last_error); + g_free_not_null(rec->fname); + g_free(rec); +} + +/* Change file name of config file */ +void config_change_file_name(CONFIG_REC *rec, const char *fname, int create_mode) +{ + g_return_if_fail(rec != NULL); + g_return_if_fail(fname != NULL); + + g_free_not_null(rec->fname); + rec->fname = g_strdup(fname); + + if (create_mode != -1) + rec->create_mode = create_mode; +} diff --git a/apps/irssi/src/lib-config/set.c b/apps/irssi/src/lib-config/set.c new file mode 100644 index 00000000..fdb358f9 --- /dev/null +++ b/apps/irssi/src/lib-config/set.c @@ -0,0 +1,171 @@ +/* + set.c : irssi configuration - change settings in memory + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" + +static void cache_remove(CONFIG_REC *rec, CONFIG_NODE *node) +{ + char *path; + + path = g_hash_table_lookup(rec->cache_nodes, node); + if (path != NULL) { + g_hash_table_remove(rec->cache, path); + g_hash_table_remove(rec->cache_nodes, node); + g_free(path); + } +} + +void config_node_remove(CONFIG_REC *rec, CONFIG_NODE *parent, CONFIG_NODE *node) +{ + g_return_if_fail(node != NULL); + + if (parent == NULL) + parent = rec->mainnode; + + rec->modifycounter++; + cache_remove(rec, node); + parent->value = g_slist_remove(parent->value, node); + + switch (node->type) { + case NODE_TYPE_KEY: + case NODE_TYPE_VALUE: + case NODE_TYPE_COMMENT: + g_free_not_null(node->value); + break; + case NODE_TYPE_BLOCK: + case NODE_TYPE_LIST: + while (node->value != NULL) + config_node_remove(rec, node, ((GSList *) node->value)->data); + break; + } + g_free_not_null(node->key); + g_free(node); +} + +/* Remove n'th node from a list */ +void config_node_list_remove(CONFIG_REC *rec, CONFIG_NODE *node, int index) +{ + CONFIG_NODE *child; + + g_return_if_fail(node != NULL); + g_return_if_fail(is_node_list(node)); + + child = config_node_index(node, index); + if (child != NULL) config_node_remove(rec, node, child); +} + +/* Clear all data inside node, but leave the node */ +void config_node_clear(CONFIG_REC *rec, CONFIG_NODE *node) +{ + g_return_if_fail(node != NULL); + g_return_if_fail(is_node_list(node)); + + while (node->value != NULL) + config_node_remove(rec, node, ((GSList *) node->value)->data); +} + +void config_nodes_remove_all(CONFIG_REC *rec) +{ + g_return_if_fail(rec != NULL); + + while (rec->mainnode->value != NULL) + config_node_remove(rec, rec->mainnode, ((GSList *) rec->mainnode->value)->data); +} + +void config_node_set_str(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, const char *value) +{ + CONFIG_NODE *node; + int no_key; + + g_return_if_fail(rec != NULL); + g_return_if_fail(parent != NULL); + + no_key = key == NULL; + node = no_key ? NULL : config_node_find(parent, key); + + if (value == NULL) { + /* remove the key */ + if (node != NULL) config_node_remove(rec, parent, node); + return; + } + + if (node != NULL) { + if (strcmp(node->value, value) == 0) + return; + g_free(node->value); + } else { + node = g_new0(CONFIG_NODE, 1); + parent->value = g_slist_append(parent->value, node); + + node->type = no_key ? NODE_TYPE_VALUE : NODE_TYPE_KEY; + node->key = no_key ? NULL : g_strdup(key); + } + + node->value = g_strdup(value); + rec->modifycounter++; +} + +void config_node_set_int(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int value) +{ + char str[MAX_INT_STRLEN]; + + g_snprintf(str, sizeof(str), "%d", value); + config_node_set_str(rec, parent, key, str); +} + +void config_node_set_bool(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int value) +{ + config_node_set_str(rec, parent, key, value ? "yes" : "no"); +} + +int config_set_str(CONFIG_REC *rec, const char *section, const char *key, const char *value) +{ + CONFIG_NODE *parent; + + g_return_val_if_fail(rec != NULL, -1); + + parent = config_node_traverse(rec, section, TRUE); + if (parent == NULL) return -1; + + config_node_set_str(rec, parent, key, value); + return 0; +} + +int config_set_int(CONFIG_REC *rec, const char *section, const char *key, int value) +{ + char str[MAX_INT_STRLEN]; + + g_snprintf(str, sizeof(str), "%d", value); + return config_set_str(rec, section, key, str); +} + +int config_set_bool(CONFIG_REC *rec, const char *section, const char *key, int value) +{ + return config_set_str(rec, section, key, value ? "yes" : "no"); +} + +/* Add all values in `array' to `node' */ +void config_node_add_list(CONFIG_REC *rec, CONFIG_NODE *node, char **array) +{ + char **tmp; + + for (tmp = array; *tmp != NULL; tmp++) + config_node_set_str(rec, node, NULL, *tmp); +} diff --git a/apps/irssi/src/lib-config/write.c b/apps/irssi/src/lib-config/write.c new file mode 100644 index 00000000..446735cd --- /dev/null +++ b/apps/irssi/src/lib-config/write.c @@ -0,0 +1,341 @@ +/* + write.c : irssi configuration - write configuration file + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" + +/* maximum length of lines in config file before splitting them to multiple lines */ +#define MAX_CHARS_IN_LINE 70 + +#define CONFIG_INDENT_SIZE 2 +static const char *indent_block = " "; /* needs to be the same size as CONFIG_INDENT_SIZE! */ + +/* write needed amount of indentation to the start of the line */ +static int config_write_indent(CONFIG_REC *rec) +{ + int n; + + for (n = 0; n < rec->tmp_indent_level/CONFIG_INDENT_SIZE; n++) { + if (write(rec->handle, indent_block, CONFIG_INDENT_SIZE) == -1) + return -1; + } + + return 0; +} + +static int config_write_str(CONFIG_REC *rec, const char *str) +{ + const char *strpos, *p; + + g_return_val_if_fail(rec != NULL, -1); + g_return_val_if_fail(str != NULL, -1); + + strpos = str; + while (*strpos != '\0') { + /* fill the indentation */ + if (rec->tmp_last_lf && rec->tmp_indent_level > 0 && + *str != '\n') { + if (config_write_indent(rec) == -1) + return -1; + } + + p = strchr(strpos, '\n'); + if (p == NULL) { + if (write(rec->handle, strpos, strlen(strpos)) == -1) + return -1; + strpos = ""; + rec->tmp_last_lf = FALSE; + } else { + if (write(rec->handle, strpos, (int) (p-strpos)+1) == -1) + return -1; + strpos = p+1; + rec->tmp_last_lf = TRUE; + } + } + + return 0; +} + +static int config_has_specials(const char *text) +{ + g_return_val_if_fail(text != NULL, FALSE); + + while (*text != '\0') { + if (!isalnum((int) *text) && *text != '_') + return TRUE; + text++; + } + + return FALSE; +} + +static int get_octal(int decimal) +{ + int octal, pos; + + octal = 0; pos = 0; + while (decimal > 0) { + octal += (decimal & 7)*(pos == 0 ? 1 : pos); + decimal /= 8; + pos += 10; + } + + return octal; +} + +static char *config_escape_string(const char *text) +{ + GString *str; + char *ret; + + g_return_val_if_fail(text != NULL, NULL); + + str = g_string_new("\""); + while (*text != '\0') { + if (*text == '\\' || *text == '"') + g_string_sprintfa(str, "\\%c", *text); + else if ((unsigned char) *text < 32) + g_string_sprintfa(str, "\\%03d", get_octal(*text)); + else + g_string_append_c(str, *text); + text++; + } + + g_string_append_c(str, '"'); + + ret = str->str; + g_string_free(str, FALSE); + return ret; +} + +static int config_write_word(CONFIG_REC *rec, const char *word, int string) +{ + char *str; + int ret; + + g_return_val_if_fail(rec != NULL, -1); + g_return_val_if_fail(word != NULL, -1); + + if (!string && !config_has_specials(word)) + return config_write_str(rec, word); + + str = config_escape_string(word); + ret = config_write_str(rec, str); + g_free(str); + + return ret; +} + +static int config_write_block(CONFIG_REC *rec, CONFIG_NODE *node, int list, int line_feeds); + +static int config_write_node(CONFIG_REC *rec, CONFIG_NODE *node, int line_feeds) +{ + g_return_val_if_fail(rec != NULL, -1); + g_return_val_if_fail(node != NULL, -1); + + switch (node->type) { + case NODE_TYPE_KEY: + if (config_write_word(rec, node->key, FALSE) == -1 || + config_write_str(rec, " = ") == -1 || + config_write_word(rec, node->value, TRUE) == -1) + return -1; + break; + case NODE_TYPE_VALUE: + if (config_write_word(rec, node->value, TRUE) == -1) + return -1; + break; + case NODE_TYPE_BLOCK: + /* key = { */ + if (node->key != NULL) { + if (config_write_word(rec, node->key, FALSE) == -1 || + config_write_str(rec, " = ") == -1) + return -1; + } + if (config_write_str(rec, line_feeds ? "{\n" : "{ ") == -1) + return -1; + + /* ..block.. */ + rec->tmp_indent_level += CONFIG_INDENT_SIZE; + if (config_write_block(rec, node, FALSE, line_feeds) == -1) + return -1; + rec->tmp_indent_level -= CONFIG_INDENT_SIZE; + + /* }; */ + if (config_write_str(rec, "}") == -1) + return -1; + break; + case NODE_TYPE_LIST: + /* key = ( */ + if (node->key != NULL) { + if (config_write_word(rec, node->key, FALSE) == -1 || + config_write_str(rec, " = ") == -1) + return -1; + } + if (config_write_str(rec, line_feeds ? "(\n" : "( ") == -1) + return -1; + + /* ..list.. */ + rec->tmp_indent_level += CONFIG_INDENT_SIZE; + if (config_write_block(rec, node, TRUE, line_feeds) == -1) + return -1; + rec->tmp_indent_level -= CONFIG_INDENT_SIZE; + + /* ); */ + if (config_write_str(rec, ")") == -1) + return -1; + break; + case NODE_TYPE_COMMENT: + if (node->value == NULL) + break; + + if (config_write_str(rec, "#") == -1 || + config_write_str(rec, node->value) == -1) + return -1; + break; + } + + return 0; +} + +static int config_block_get_length(CONFIG_REC *rec, CONFIG_NODE *node); + +static int config_node_get_length(CONFIG_REC *rec, CONFIG_NODE *node) +{ + int len; + + switch (node->type) { + case NODE_TYPE_KEY: + /* "key = value; " */ + len = 5 + strlen(node->key) + strlen(node->value); + break; + case NODE_TYPE_VALUE: + /* "value, " */ + len = 2 + strlen(node->value); + break; + case NODE_TYPE_BLOCK: + case NODE_TYPE_LIST: + /* "{ list }; " */ + len = 6; + if (node->key != NULL) len += strlen(node->key); + len += config_block_get_length(rec, node); + break; + default: + /* comments always split the line */ + len = 1000; + break; + } + + return len; +} + +/* return the number of characters `node' and it's subnodes take + if written to file */ +static int config_block_get_length(CONFIG_REC *rec, CONFIG_NODE *node) +{ + GSList *tmp; + int len; + + len = 0; + for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + CONFIG_NODE *subnode = tmp->data; + + len += config_node_get_length(rec, subnode); + if (len > MAX_CHARS_IN_LINE) return len; + } + + return len; +} + +/* check if `node' and it's subnodes fit in one line in the config file */ +static int config_block_fit_one_line(CONFIG_REC *rec, CONFIG_NODE *node) +{ + g_return_val_if_fail(rec != NULL, 0); + g_return_val_if_fail(node != NULL, 0); + + return rec->tmp_indent_level + + config_node_get_length(rec, node) <= MAX_CHARS_IN_LINE; +} + +static int config_write_block(CONFIG_REC *rec, CONFIG_NODE *node, int list, int line_feeds) +{ + GSList *tmp; + int list_line_feeds, node_line_feeds; + + g_return_val_if_fail(rec != NULL, -1); + g_return_val_if_fail(node != NULL, -1); + g_return_val_if_fail(is_node_list(node), -1); + + list_line_feeds = !config_block_fit_one_line(rec, node); + + if (!line_feeds && list_line_feeds) + config_write_str(rec, "\n"); + + for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + CONFIG_NODE *subnode = tmp->data; + + node_line_feeds = !line_feeds ? FALSE : !config_block_fit_one_line(rec, subnode); + if (config_write_node(rec, subnode, node_line_feeds) == -1) + return -1; + + if (subnode->type == NODE_TYPE_COMMENT) + config_write_str(rec, "\n"); + else if (list) { + if (tmp->next != NULL) + config_write_str(rec, list_line_feeds ? ",\n" : ", "); + else + config_write_str(rec, list_line_feeds ? "\n" : " "); + } else { + config_write_str(rec, list_line_feeds ? ";\n" : "; "); + } + } + + return 0; +} + +/* Write configuration file. Write to `fname' if it's not NULL. */ +int config_write(CONFIG_REC *rec, const char *fname, int create_mode) +{ + int ret; + + g_return_val_if_fail(rec != NULL, -1); + g_return_val_if_fail(fname != NULL || rec->fname != NULL, -1); + g_return_val_if_fail(create_mode != -1 || rec->create_mode != -1, -1); + + if (rec->handle != -1) + close(rec->handle); + + rec->handle = open(fname != NULL ? fname : rec->fname, + O_WRONLY | O_TRUNC | O_CREAT, + create_mode != -1 ? create_mode : rec->create_mode); + if (rec->handle == -1) + return config_error(rec, g_strerror(errno)); + + rec->tmp_indent_level = 0; + rec->tmp_last_lf = TRUE; + ret = config_write_block(rec, rec->mainnode, FALSE, TRUE); + if (ret == -1) { + /* write error */ + config_error(rec, errno == 0 ? "bug" : g_strerror(errno)); + } + + close(rec->handle); + rec->handle = -1; + + return ret; +} diff --git a/apps/irssi/src/lib-popt/.cvsignore b/apps/irssi/src/lib-popt/.cvsignore new file mode 100644 index 00000000..577aa172 --- /dev/null +++ b/apps/irssi/src/lib-popt/.cvsignore @@ -0,0 +1,7 @@ +*.la +*.lo +.deps +.libs +Makefile +Makefile.in +so_locations diff --git a/apps/irssi/src/lib-popt/Makefile.am b/apps/irssi/src/lib-popt/Makefile.am new file mode 100644 index 00000000..928e5c0b --- /dev/null +++ b/apps/irssi/src/lib-popt/Makefile.am @@ -0,0 +1,11 @@ +noinst_LIBRARIES = libpopt.a + +INCLUDES = \ + $(GLIB_CFLAGS) \ + -I$(top_srcdir)/src + +libpopt_a_SOURCES = \ + findme.c popt.c poptconfig.c popthelp.c poptparse.c + +noinst_HEADERS = \ + findme.h popt.h poptint.h diff --git a/apps/irssi/src/lib-popt/findme.c b/apps/irssi/src/lib-popt/findme.c new file mode 100644 index 00000000..fae8f032 --- /dev/null +++ b/apps/irssi/src/lib-popt/findme.c @@ -0,0 +1,54 @@ +/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING + file accompanying popt source distributions, available from + ftp://ftp.redhat.com/pub/code/popt */ + +#include "common.h" + +#ifdef __NeXT +/* access macros are not declared in non posix mode in unistd.h - + don't try to use posix on NeXTstep 3.3 ! */ +#include +#endif + +#include "findme.h" + +char * findProgramPath(char * argv0) { + char * path = getenv("PATH"); + char * pathbuf; + char * start, * chptr; + char * buf; + + /* If there is a / in the argv[0], it has to be an absolute + path */ + if (strchr(argv0, '/')) + return g_strdup(argv0); + + if (!path) return NULL; + + start = pathbuf = g_strdup(path); + buf = g_malloc(strlen(path) + strlen(argv0) + 2); + + chptr = NULL; + do { + if ((chptr = strchr(start, ':'))) + *chptr = '\0'; + sprintf(buf, "%s/%s", start, argv0); + +#ifndef WIN32 + if (!access(buf, X_OK)) { + g_free(pathbuf); + return buf; + } +#endif + + if (chptr) + start = chptr + 1; + else + start = NULL; + } while (start && *start); + + g_free(pathbuf); + free(buf); + + return NULL; +} diff --git a/apps/irssi/src/lib-popt/findme.h b/apps/irssi/src/lib-popt/findme.h new file mode 100644 index 00000000..fdd01d52 --- /dev/null +++ b/apps/irssi/src/lib-popt/findme.h @@ -0,0 +1,10 @@ +/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING + file accompanying popt source distributions, available from + ftp://ftp.redhat.com/pub/code/popt */ + +#ifndef H_FINDME +#define H_FINDME + +char * findProgramPath(char * argv0); + +#endif diff --git a/apps/irssi/src/lib-popt/popt.c b/apps/irssi/src/lib-popt/popt.c new file mode 100644 index 00000000..c09a0b30 --- /dev/null +++ b/apps/irssi/src/lib-popt/popt.c @@ -0,0 +1,593 @@ +/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING + file accompanying popt source distributions, available from + ftp://ftp.redhat.com/pub/code/popt */ + +#include "common.h" + +#include "findme.h" +#include "popt.h" +#include "poptint.h" + +void poptSetExecPath(poptContext con, const char * path, int allowAbsolute) { + if (con->execPath) free(con->execPath); + con->execPath = g_strdup(path); + con->execAbsolute = allowAbsolute; +} + +static void invokeCallbacks(poptContext con, const struct poptOption * table, + int post) { + const struct poptOption * opt = table; + poptCallbackType cb; + + while (opt->longName || opt->shortName || opt->arg) { + if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) { + invokeCallbacks(con, opt->arg, post); + } else if (((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) && + ((!post && (opt->argInfo & POPT_CBFLAG_PRE)) || + ( post && (opt->argInfo & POPT_CBFLAG_POST)))) { + cb = (poptCallbackType)opt->arg; + cb(con, post ? POPT_CALLBACK_REASON_POST : POPT_CALLBACK_REASON_PRE, + NULL, NULL, opt->descrip); + } + opt++; + } +} + +poptContext poptGetContext(char * name, int argc, char ** argv, + const struct poptOption * options, int flags) { + poptContext con = malloc(sizeof(*con)); + + memset(con, 0, sizeof(*con)); + + con->os = con->optionStack; + con->os->argc = argc; + con->os->argv = argv; + + if (!(flags & POPT_CONTEXT_KEEP_FIRST)) + con->os->next = 1; /* skip argv[0] */ + + con->leftovers = malloc(sizeof(char *) * (argc + 1)); + con->options = options; + con->finalArgv = malloc(sizeof(*con->finalArgv) * (argc * 2)); + con->finalArgvAlloced = argc * 2; + con->flags = flags; + con->execAbsolute = 1; + + if (getenv("POSIXLY_CORRECT") || getenv("POSIX_ME_HARDER")) + con->flags |= POPT_CONTEXT_POSIXMEHARDER; + + if (name) + con->appName = strcpy(malloc(strlen(name) + 1), name); + + invokeCallbacks(con, con->options, 0); + + return con; +} + +void poptResetContext(poptContext con) { + int i; + + con->os = con->optionStack; + con->os->currAlias = NULL; + con->os->nextCharArg = NULL; + con->os->nextArg = NULL; + con->os->next = 1; /* skip argv[0] */ + + con->numLeftovers = 0; + con->nextLeftover = 0; + con->restLeftover = 0; + con->doExec = NULL; + + for (i = 0; i < con->finalArgvCount; i++) + free(con->finalArgv[i]); + + con->finalArgvCount = 0; +} + +/* Only one of longName, shortName may be set at a time */ +static int handleExec(poptContext con, char * longName, char shortName) { + int i; + + i = con->numExecs - 1; + if (longName) { + while (i >= 0 && (!con->execs[i].longName || + strcmp(con->execs[i].longName, longName))) i--; + } else { + while (i >= 0 && + con->execs[i].shortName != shortName) i--; + } + + if (i < 0) return 0; + + if (con->flags & POPT_CONTEXT_NO_EXEC) + return 1; + + if (!con->doExec) { + con->doExec = con->execs + i; + return 1; + } + + /* We already have an exec to do; remember this option for next + time 'round */ + if ((con->finalArgvCount + 1) >= (con->finalArgvAlloced)) { + con->finalArgvAlloced += 10; + con->finalArgv = realloc(con->finalArgv, + sizeof(*con->finalArgv) * con->finalArgvAlloced); + } + + i = con->finalArgvCount++; + con->finalArgv[i] = malloc((longName ? strlen(longName) : 0) + 3); + if (longName) + sprintf(con->finalArgv[i], "--%s", longName); + else + sprintf(con->finalArgv[i], "-%c", shortName); + + return 1; +} + +/* Only one of longName, shortName may be set at a time */ +static int handleAlias(poptContext con, char * longName, char shortName, + char * nextCharArg) { + int i; + + if (con->os->currAlias && con->os->currAlias->longName && longName && + !strcmp(con->os->currAlias->longName, longName)) + return 0; + if (con->os->currAlias && shortName && + shortName == con->os->currAlias->shortName) + return 0; + + i = con->numAliases - 1; + if (longName) { + while (i >= 0 && (!con->aliases[i].longName || + strcmp(con->aliases[i].longName, longName))) i--; + } else { + while (i >= 0 && + con->aliases[i].shortName != shortName) i--; + } + + if (i < 0) return 0; + + if ((con->os - con->optionStack + 1) + == POPT_OPTION_DEPTH) + return POPT_ERROR_OPTSTOODEEP; + + if (nextCharArg && *nextCharArg) + con->os->nextCharArg = nextCharArg; + + con->os++; + con->os->next = 0; + con->os->stuffed = 0; + con->os->nextArg = con->os->nextCharArg = NULL; + con->os->currAlias = con->aliases + i; + con->os->argc = con->os->currAlias->argc; + con->os->argv = con->os->currAlias->argv; + + return 1; +} + +static void execCommand(poptContext con) { + char ** argv; + int pos = 0; + char * script = con->doExec->script; + + argv = malloc(sizeof(*argv) * + (6 + con->numLeftovers + con->finalArgvCount)); + + if (!con->execAbsolute && strchr(script, '/')) return; + + if (!strchr(script, '/') && con->execPath) { + argv[pos] = g_strdup_printf("%s/%s", con->execPath, script); + } else { + argv[pos] = script; + } + pos++; + + argv[pos] = findProgramPath(con->os->argv[0]); + if (argv[pos]) pos++; + argv[pos++] = ";"; + + memcpy(argv + pos, con->finalArgv, sizeof(*argv) * con->finalArgvCount); + pos += con->finalArgvCount; + + if (con->numLeftovers) { + argv[pos++] = "--"; + memcpy(argv + pos, con->leftovers, sizeof(*argv) * con->numLeftovers); + pos += con->numLeftovers; + } + + argv[pos++] = NULL; + +#ifdef __hpux + setresuid(getuid(), getuid(),-1); +#else +/* + * XXX " ... on BSD systems setuid() should be preferred over setreuid()" + * XXX sez' Timur Bakeyev + * XXX from Norbert Warmuth + */ +#if defined(HAVE_SETUID) + setuid(getuid()); +#elif defined (HAVE_SETREUID) + setreuid(getuid(), getuid()); /*hlauer: not portable to hpux9.01 */ +#else + ; /* Can't drop privileges */ +#endif +#endif + + execvp(argv[0], argv); +} + +static const struct poptOption * findOption(const struct poptOption * table, + const char * longName, + char shortName, + poptCallbackType * callback, + void ** callbackData, + int singleDash) { + const struct poptOption * opt = table; + const struct poptOption * opt2; + const struct poptOption * cb = NULL; + + /* This happens when a single - is given */ + if (singleDash && !shortName && !*longName) + shortName = '-'; + + while (opt->longName || opt->shortName || opt->arg) { + if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) { + opt2 = findOption(opt->arg, longName, shortName, callback, + callbackData, singleDash); + if (opt2) { + if (*callback && !*callbackData) + *callbackData = opt->descrip; + return opt2; + } + } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) { + cb = opt; + } else if (longName && opt->longName && + (!singleDash || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) && + !strcmp(longName, opt->longName)) { + break; + } else if (shortName && shortName == opt->shortName) { + break; + } + opt++; + } + + if (!opt->longName && !opt->shortName) return NULL; + *callbackData = NULL; + *callback = NULL; + if (cb) { + *callback = (poptCallbackType)cb->arg; + if (!(cb->argInfo & POPT_CBFLAG_INC_DATA)) + *callbackData = cb->descrip; + } + + return opt; +} + +/* returns 'val' element, -1 on last item, POPT_ERROR_* on error */ +int poptGetNextOpt(poptContext con) { + char * optString, * chptr, * localOptString; + char * longArg = NULL; + char * origOptString, *dup; + long aLong; + char * end; + const struct poptOption * opt = NULL; + int done = 0; + int i; + poptCallbackType cb; + void * cbData; + int singleDash; + + dup = NULL; + while (!done) { + if (dup) { + g_free(dup); + dup = NULL; + } + while (!con->os->nextCharArg && con->os->next == con->os->argc + && con->os > con->optionStack) + con->os--; + if (!con->os->nextCharArg && con->os->next == con->os->argc) { + invokeCallbacks(con, con->options, 1); + if (con->doExec) execCommand(con); + return -1; + } + + if (!con->os->nextCharArg) { + + origOptString = con->os->argv[con->os->next++]; + + if (con->restLeftover || *origOptString != '-') { + con->leftovers[con->numLeftovers++] = origOptString; + if (con->flags & POPT_CONTEXT_POSIXMEHARDER) + con->restLeftover = 1; + continue; + } + + /* Make a copy we can hack at */ + dup = localOptString = optString = + g_strdup(origOptString); + + if (!optString[0]) { + g_free(dup); + return POPT_ERROR_BADOPT; + } + + if (optString[1] == '-' && !optString[2]) { + con->restLeftover = 1; + continue; + } else { + optString++; + if (*optString == '-') + singleDash = 0, optString++; + else + singleDash = 1; + + if (handleAlias(con, optString, '\0', NULL)) + continue; + if (handleExec(con, optString, '\0')) + continue; + + chptr = optString; + while (*chptr && *chptr != '=') chptr++; + if (*chptr == '=') { + longArg = origOptString + (chptr - localOptString) + 1; + *chptr = '\0'; + } + + opt = findOption(con->options, optString, '\0', &cb, &cbData, + singleDash); + if (!opt && !singleDash) { + g_free(dup); + return POPT_ERROR_BADOPT; + } + } + + if (!opt) + con->os->nextCharArg = origOptString + 1; + } + + if (con->os->nextCharArg) { + origOptString = con->os->nextCharArg; + + con->os->nextCharArg = NULL; + + if (handleAlias(con, NULL, *origOptString, + origOptString + 1)) { + origOptString++; + continue; + } + if (handleExec(con, NULL, *origOptString)) + continue; + + opt = findOption(con->options, NULL, *origOptString, &cb, + &cbData, 0); + if (!opt) { + g_free(dup); + return POPT_ERROR_BADOPT; + } + origOptString++; + if (*origOptString) + con->os->nextCharArg = origOptString; + } + + if (opt->arg && (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE) { + *((int *)opt->arg) = 1; + } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL) { + if (opt->arg) *((int *) opt->arg) = opt->val; + } else if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) { + if (longArg) { + con->os->nextArg = longArg; + } else if (con->os->nextCharArg) { + con->os->nextArg = con->os->nextCharArg; + con->os->nextCharArg = NULL; + } else { + while (con->os->next == con->os->argc && + con->os > con->optionStack) + con->os--; + if (con->os->next == con->os->argc) { + g_free(dup); + return POPT_ERROR_NOARG; + } + + con->os->nextArg = con->os->argv[con->os->next++]; + } + + if (opt->arg) { + switch (opt->argInfo & POPT_ARG_MASK) { + case POPT_ARG_STRING: + *((char **) opt->arg) = con->os->nextArg; + break; + + case POPT_ARG_INT: + case POPT_ARG_LONG: + aLong = strtol(con->os->nextArg, &end, 0); + if (!(end && *end == '\0')) { + g_free(dup); + return POPT_ERROR_BADNUMBER; + } + + if (aLong == LONG_MIN || aLong == LONG_MAX) { + g_free(dup); + return POPT_ERROR_OVERFLOW; + } + if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_LONG) { + *((long *) opt->arg) = aLong; + } else { + if (aLong > INT_MAX || aLong < INT_MIN) { + g_free(dup); + return POPT_ERROR_OVERFLOW; + } + *((int *) opt->arg) =aLong; + } + break; + + default: + fprintf(stdout, POPT_("option type (%d) not implemented in popt\n"), + opt->argInfo & POPT_ARG_MASK); + exit(1); + } + } + } + + if (cb) + cb(con, POPT_CALLBACK_REASON_OPTION, opt, con->os->nextArg, cbData); + else if (opt->val && ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL)) + done = 1; + + if ((con->finalArgvCount + 2) >= (con->finalArgvAlloced)) { + con->finalArgvAlloced += 10; + con->finalArgv = realloc(con->finalArgv, + sizeof(*con->finalArgv) * con->finalArgvAlloced); + } + + i = con->finalArgvCount++; + con->finalArgv[i] = + malloc((opt->longName ? strlen(opt->longName) : 0) + 3); + if (opt->longName) + sprintf(con->finalArgv[i], "--%s", opt->longName); + else + sprintf(con->finalArgv[i], "-%c", opt->shortName); + + if (opt->arg && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE + && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL) + con->finalArgv[con->finalArgvCount++] = g_strdup(con->os->nextArg); + } + + if (dup) g_free(dup); + return opt->val; +} + +char * poptGetOptArg(poptContext con) { + char * ret = con->os->nextArg; + con->os->nextArg = NULL; + return ret; +} + +char * poptGetArg(poptContext con) { + if (con->numLeftovers == con->nextLeftover) return NULL; + return (con->leftovers[con->nextLeftover++]); +} + +char * poptPeekArg(poptContext con) { + if (con->numLeftovers == con->nextLeftover) return NULL; + return (con->leftovers[con->nextLeftover]); +} + +char ** poptGetArgs(poptContext con) { + if (con->numLeftovers == con->nextLeftover) return NULL; + + /* some apps like [like RPM ;-) ] need this NULL terminated */ + con->leftovers[con->numLeftovers] = NULL; + + return (con->leftovers + con->nextLeftover); +} + +void poptFreeContext(poptContext con) { + int i; + + for (i = 0; i < con->numAliases; i++) { + if (con->aliases[i].longName) free(con->aliases[i].longName); + free(con->aliases[i].argv); + } + + for (i = 0; i < con->numExecs; i++) { + if (con->execs[i].longName) free(con->execs[i].longName); + free(con->execs[i].script); + } + + for (i = 0; i < con->finalArgvCount; i++) + free(con->finalArgv[i]); + + free(con->leftovers); + free(con->finalArgv); + if (con->appName) free(con->appName); + if (con->aliases) free(con->aliases); + if (con->otherHelp) free(con->otherHelp); + if (con->execPath) free(con->execPath); + free(con); +} + +int poptAddAlias(poptContext con, struct poptAlias newAlias, int flags) { + int aliasNum = con->numAliases++; + struct poptAlias * alias; + + /* SunOS won't realloc(NULL, ...) */ + if (!con->aliases) + con->aliases = malloc(sizeof(newAlias) * con->numAliases); + else + con->aliases = realloc(con->aliases, + sizeof(newAlias) * con->numAliases); + alias = con->aliases + aliasNum; + + *alias = newAlias; + if (alias->longName) + alias->longName = strcpy(malloc(strlen(alias->longName) + 1), + alias->longName); + else + alias->longName = NULL; + + return 0; +} + +char * poptBadOption(poptContext con, int flags) { + struct optionStackEntry * os; + + if (flags & POPT_BADOPTION_NOALIAS) + os = con->optionStack; + else + os = con->os; + + return os->argv[os->next - 1]; +} + +#define POPT_ERROR_NOARG -10 +#define POPT_ERROR_BADOPT -11 +#define POPT_ERROR_OPTSTOODEEP -13 +#define POPT_ERROR_BADQUOTE -15 /* only from poptParseArgString() */ +#define POPT_ERROR_ERRNO -16 /* only from poptParseArgString() */ + +const char * poptStrerror(const int error) { + switch (error) { + case POPT_ERROR_NOARG: + return POPT_("missing argument"); + case POPT_ERROR_BADOPT: + return POPT_("unknown option"); + case POPT_ERROR_OPTSTOODEEP: + return POPT_("aliases nested too deeply"); + case POPT_ERROR_BADQUOTE: + return POPT_("error in paramter quoting"); + case POPT_ERROR_BADNUMBER: + return POPT_("invalid numeric value"); + case POPT_ERROR_OVERFLOW: + return POPT_("number too large or too small"); + case POPT_ERROR_ERRNO: + return strerror(errno); + default: + return POPT_("unknown error"); + } +} + +int poptStuffArgs(poptContext con, char ** argv) { + int i; + + if ((con->os - con->optionStack) == POPT_OPTION_DEPTH) + return POPT_ERROR_OPTSTOODEEP; + + for (i = 0; argv[i]; i++); + + con->os++; + con->os->next = 0; + con->os->nextArg = con->os->nextCharArg = NULL; + con->os->currAlias = NULL; + con->os->argc = i; + con->os->argv = argv; + con->os->stuffed = 1; + + return 0; +} + +const char * poptGetInvocationName(poptContext con) { + return con->os->argv[0]; +} diff --git a/apps/irssi/src/lib-popt/popt.h b/apps/irssi/src/lib-popt/popt.h new file mode 100644 index 00000000..2fea9e80 --- /dev/null +++ b/apps/irssi/src/lib-popt/popt.h @@ -0,0 +1,123 @@ +/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING + file accompanying popt source distributions, available from + ftp://ftp.redhat.com/pub/code/popt */ + +#ifndef H_POPT +#define H_POPT + +#ifdef __cplusplus +extern "C" { +#endif + +#include /* for FILE * */ + +#define POPT_OPTION_DEPTH 10 + +#define POPT_ARG_NONE 0 +#define POPT_ARG_STRING 1 +#define POPT_ARG_INT 2 +#define POPT_ARG_LONG 3 +#define POPT_ARG_INCLUDE_TABLE 4 /* arg points to table */ +#define POPT_ARG_CALLBACK 5 /* table-wide callback... must be + set first in table; arg points + to callback, descrip points to + callback data to pass */ +#define POPT_ARG_INTL_DOMAIN 6 /* set the translation domain + for this table and any + included tables; arg points + to the domain string */ +#define POPT_ARG_VAL 7 /* arg should take value val */ +#define POPT_ARG_MASK 0x0000FFFF +#define POPT_ARGFLAG_ONEDASH 0x80000000 /* allow -longoption */ +#define POPT_ARGFLAG_DOC_HIDDEN 0x40000000 /* don't show in help/usage */ +#define POPT_CBFLAG_PRE 0x80000000 /* call the callback before parse */ +#define POPT_CBFLAG_POST 0x40000000 /* call the callback after parse */ +#define POPT_CBFLAG_INC_DATA 0x20000000 /* use data from the include line, + not the subtable */ + +#define POPT_ERROR_NOARG -10 +#define POPT_ERROR_BADOPT -11 +#define POPT_ERROR_OPTSTOODEEP -13 +#define POPT_ERROR_BADQUOTE -15 /* only from poptParseArgString() */ +#define POPT_ERROR_ERRNO -16 /* only from poptParseArgString() */ +#define POPT_ERROR_BADNUMBER -17 +#define POPT_ERROR_OVERFLOW -18 + +/* poptBadOption() flags */ +#define POPT_BADOPTION_NOALIAS (1 << 0) /* don't go into an alias */ + +/* poptGetContext() flags */ +#define POPT_CONTEXT_NO_EXEC (1 << 0) /* ignore exec expansions */ +#define POPT_CONTEXT_KEEP_FIRST (1 << 1) /* pay attention to argv[0] */ +#define POPT_CONTEXT_POSIXMEHARDER (1 << 2) /* options can't follow args */ + +struct poptOption { + const char * longName; /* may be NULL */ + char shortName; /* may be '\0' */ + int argInfo; + void * arg; /* depends on argInfo */ + int val; /* 0 means don't return, just update flag */ + char * descrip; /* description for autohelp -- may be NULL */ + char * argDescrip; /* argument description for autohelp */ +}; + +struct poptAlias { + char * longName; /* may be NULL */ + char shortName; /* may be '\0' */ + int argc; + char ** argv; /* must be free()able */ +}; + +extern struct poptOption poptHelpOptions[]; +#define POPT_AUTOHELP { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptHelpOptions, \ + 0, "Help options", NULL }, + +typedef struct poptContext_s * poptContext; +#ifndef __cplusplus +typedef struct poptOption * poptOption; +#endif + +enum poptCallbackReason { POPT_CALLBACK_REASON_PRE, + POPT_CALLBACK_REASON_POST, + POPT_CALLBACK_REASON_OPTION }; +typedef void (*poptCallbackType)(poptContext con, + enum poptCallbackReason reason, + const struct poptOption * opt, + const char * arg, void * data); + +poptContext poptGetContext(char * name, int argc, char ** argv, + const struct poptOption * options, int flags); +void poptResetContext(poptContext con); + +/* returns 'val' element, -1 on last item, POPT_ERROR_* on error */ +int poptGetNextOpt(poptContext con); +/* returns NULL if no argument is available */ +char * poptGetOptArg(poptContext con); +/* returns NULL if no more options are available */ +char * poptGetArg(poptContext con); +char * poptPeekArg(poptContext con); +char ** poptGetArgs(poptContext con); +/* returns the option which caused the most recent error */ +char * poptBadOption(poptContext con, int flags); +void poptFreeContext(poptContext con); +int poptStuffArgs(poptContext con, char ** argv); +int poptAddAlias(poptContext con, struct poptAlias alias, int flags); +int poptReadConfigFile(poptContext con, char * fn); +/* like above, but reads /etc/popt and $HOME/.popt along with environment + vars */ +int poptReadDefaultConfig(poptContext con, int useEnv); +/* argv should be freed -- this allows ', ", and \ quoting, but ' is treated + the same as " and both may include \ quotes */ +int poptParseArgvString(const char * s, int * argcPtr, char *** argvPtr); +const char * poptStrerror(const int error); +void poptSetExecPath(poptContext con, const char * path, int allowAbsolute); +void poptPrintHelp(poptContext con, FILE * f, int flags); +void poptPrintUsage(poptContext con, FILE * f, int flags); +void poptSetOtherOptionHelp(poptContext con, const char * text); +const char * poptGetInvocationName(poptContext con); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/apps/irssi/src/lib-popt/poptconfig.c b/apps/irssi/src/lib-popt/poptconfig.c new file mode 100644 index 00000000..d064297c --- /dev/null +++ b/apps/irssi/src/lib-popt/poptconfig.c @@ -0,0 +1,144 @@ +/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING + file accompanying popt source distributions, available from + ftp://ftp.redhat.com/pub/code/popt */ + +#include "common.h" + +#include "popt.h" +#include "poptint.h" + +static void configLine(poptContext con, char * line) { + int nameLength = strlen(con->appName); + char * opt; + struct poptAlias alias; + char * entryType; + char * longName = NULL; + char shortName = '\0'; + + if (strncmp(line, con->appName, nameLength)) return; + line += nameLength; + if (!*line || !isspace(*line)) return; + while (*line && isspace(*line)) line++; + entryType = line; + + while (!*line || !isspace(*line)) line++; + *line++ = '\0'; + while (*line && isspace(*line)) line++; + if (!*line) return; + opt = line; + + while (!*line || !isspace(*line)) line++; + *line++ = '\0'; + while (*line && isspace(*line)) line++; + if (!*line) return; + + if (opt[0] == '-' && opt[1] == '-') + longName = opt + 2; + else if (opt[0] == '-' && !opt[2]) + shortName = opt[1]; + + if (!strcmp(entryType, "alias")) { + if (poptParseArgvString(line, &alias.argc, &alias.argv)) return; + alias.longName = longName, alias.shortName = shortName; + poptAddAlias(con, alias, 0); + } else if (!strcmp(entryType, "exec")) { + con->execs = realloc(con->execs, + sizeof(*con->execs) * (con->numExecs + 1)); + if (longName) + con->execs[con->numExecs].longName = g_strdup(longName); + else + con->execs[con->numExecs].longName = NULL; + + con->execs[con->numExecs].shortName = shortName; + con->execs[con->numExecs].script = g_strdup(line); + + con->numExecs++; + } +} + +int poptReadConfigFile(poptContext con, char * fn) { + char * file, * chptr, * end; + char * buf, * dst; + int fd, rc; + int fileLength; + + fd = open(fn, O_RDONLY); + if (fd < 0) { + if (errno == ENOENT) + return 0; + else + return POPT_ERROR_ERRNO; + } + + fileLength = lseek(fd, 0, SEEK_END); + lseek(fd, 0, 0); + + file = malloc(fileLength + 1); + if (read(fd, file, fileLength) != fileLength) { + rc = errno; + close(fd); + errno = rc; + free(file); + return POPT_ERROR_ERRNO; + } + close(fd); + + dst = buf = malloc(fileLength + 1); + + chptr = file; + end = (file + fileLength); + while (chptr < end) { + switch (*chptr) { + case '\n': + *dst = '\0'; + dst = buf; + while (*dst && isspace(*dst)) dst++; + if (*dst && *dst != '#') { + configLine(con, dst); + } + chptr++; + break; + case '\\': + *dst++ = *chptr++; + if (chptr < end) { + if (*chptr == '\n') + dst--, chptr++; + /* \ at the end of a line does not insert a \n */ + else + *dst++ = *chptr++; + } + break; + default: + *dst++ = *chptr++; + } + } + + free(buf); + free(file); + return 0; +} + +int poptReadDefaultConfig(poptContext con, int useEnv) { + char * fn, * home; + int rc; + + if (!con->appName) return 0; + + rc = poptReadConfigFile(con, "/etc/popt"); + if (rc) return rc; +#ifndef WIN32 + if (getuid() != geteuid()) return 0; +#endif + + if ((home = getenv("HOME"))) { + fn = malloc(strlen(home) + 20); + strcpy(fn, home); + strcat(fn, "/.popt"); + rc = poptReadConfigFile(con, fn); + free(fn); + if (rc) return rc; + } + + return 0; +} + diff --git a/apps/irssi/src/lib-popt/popthelp.c b/apps/irssi/src/lib-popt/popthelp.c new file mode 100644 index 00000000..c1876d74 --- /dev/null +++ b/apps/irssi/src/lib-popt/popthelp.c @@ -0,0 +1,301 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING + file accompanying popt source distributions, available from + ftp://ftp.redhat.com/pub/code/popt */ + +#include "common.h" + +#include "popt.h" +#include "poptint.h" + +static void displayArgs(poptContext con, enum poptCallbackReason foo, + struct poptOption * key, + const char * arg, void * data) { + if (key->shortName== '?') + poptPrintHelp(con, stdout, 0); + else + poptPrintUsage(con, stdout, 0); + exit(0); +} + +struct poptOption poptHelpOptions[] = { + { NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL }, + { "help", '?', 0, NULL, '?', "Show this help message" }, + { "usage", '\0', 0, NULL, 'u', "Display brief usage message" }, + { NULL, '\0', 0, NULL, 0 } +} ; + + +static const char * +getTableTranslationDomain(const struct poptOption *table) +{ + const struct poptOption *opt; + + for(opt = table; + opt->longName || opt->shortName || opt->arg; + opt++) { + if(opt->argInfo == POPT_ARG_INTL_DOMAIN) + return opt->arg; + } + + return NULL; +} + +static const char * getArgDescrip(const struct poptOption * opt, + const char *translation_domain) { + if (!(opt->argInfo & POPT_ARG_MASK)) return NULL; + + if (opt == (poptHelpOptions + 1) || opt == (poptHelpOptions + 2)) + if (opt->argDescrip) return POPT_(opt->argDescrip); + + if (opt->argDescrip) return D_(translation_domain, opt->argDescrip); + return POPT_("ARG"); +} + +static void singleOptionHelp(FILE * f, int maxLeftCol, + const struct poptOption * opt, + const char *translation_domain) { + int indentLength = maxLeftCol + 5; + int lineLength = 79 - indentLength; + const char * help = D_(translation_domain, opt->descrip); + int helpLength; + const char * ch; + char format[10]; + char * left = malloc(maxLeftCol + 1); + const char * argDescrip = getArgDescrip(opt, translation_domain); + + *left = '\0'; + if (opt->longName && opt->shortName) + sprintf(left, "-%c, --%s", opt->shortName, opt->longName); + else if (opt->shortName) + sprintf(left, "-%c", opt->shortName); + else if (opt->longName) + sprintf(left, "--%s", opt->longName); + if (!*left) { + free(left); + return; + } + if (argDescrip) { + strcat(left, "="); + strcat(left, argDescrip); + } + + if (help) + fprintf(f," %-*s ", maxLeftCol, left); + else { + fprintf(f," %s\n", left); + free(left); + return; + } + + free(left); + + helpLength = strlen(help); + while (helpLength > lineLength) { + ch = help + lineLength - 1; + while (ch > help && !isspace(*ch)) ch--; + if (ch == help) break; /* give up */ + while (ch > (help + 1) && isspace(*ch)) ch--; + ch++; + + sprintf(format, "%%.%ds\n%%%ds", (int) (ch - help), indentLength); + fprintf(f, format, help, " "); + help = ch; + while (isspace(*help) && *help) help++; + helpLength = strlen(help); + } + + if (helpLength) fprintf(f, "%s\n", help); +} + +static int maxArgWidth(const struct poptOption * opt, + const char * translation_domain) { + int max = 0; + int this; + const char * s; + + while (opt->longName || opt->shortName || opt->arg) { + if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) { + this = maxArgWidth(opt->arg, translation_domain); + if (this > max) max = this; + } else if (!(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) { + this = opt->shortName ? 2 : 0; + if (opt->longName) { + if (this) this += 2; + this += strlen(opt->longName) + 2; + } + + s = getArgDescrip(opt, translation_domain); + if (s) + this += strlen(s) + 1; + if (this > max) max = this; + } + + opt++; + } + + return max; +} + +static void singleTableHelp(FILE * f, const struct poptOption * table, + int left, + const char *translation_domain) { + const struct poptOption * opt; + const char *sub_transdom; + + opt = table; + while (opt->longName || opt->shortName || opt->arg) { + if ((opt->longName || opt->shortName) && + !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) + singleOptionHelp(f, left, opt, translation_domain); + opt++; + } + + opt = table; + while (opt->longName || opt->shortName || opt->arg) { + if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) { + sub_transdom = getTableTranslationDomain(opt->arg); + if(!sub_transdom) + sub_transdom = translation_domain; + + if (opt->descrip) + fprintf(f, "\n%s\n", D_(sub_transdom, opt->descrip)); + + singleTableHelp(f, opt->arg, left, sub_transdom); + } + opt++; + } +} + +static int showHelpIntro(poptContext con, FILE * f) { + int len = 6; + char * fn; + + fprintf(f, POPT_("Usage:")); + if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) { + fn = con->optionStack->argv[0]; + if (strchr(fn, '/')) fn = strchr(fn, '/') + 1; + fprintf(f, " %s", fn); + len += strlen(fn) + 1; + } + + return len; +} + +void poptPrintHelp(poptContext con, FILE * f, int flags) { + int leftColWidth; + + showHelpIntro(con, f); + if (con->otherHelp) + fprintf(f, " %s\n", con->otherHelp); + else + fprintf(f, " %s\n", POPT_("[OPTION...]")); + + leftColWidth = maxArgWidth(con->options, NULL); + singleTableHelp(f, con->options, leftColWidth, NULL); +} + +static int singleOptionUsage(FILE * f, int cursor, + const struct poptOption * opt, + const char *translation_domain) { + int len = 3; + char shortStr[2]; + const char * item = shortStr; + const char * argDescrip = getArgDescrip(opt, translation_domain); + + if (opt->shortName) { + if (!(opt->argInfo & POPT_ARG_MASK)) + return cursor; /* we did these already */ + len++; + *shortStr = opt->shortName; + shortStr[1] = '\0'; + } else if (opt->longName) { + len += 1 + strlen(opt->longName); + item = opt->longName; + } + + if (len == 3) return cursor; + + if (argDescrip) + len += strlen(argDescrip) + 1; + + if ((cursor + len) > 79) { + fprintf(f, "\n "); + cursor = 7; + } + + fprintf(f, " [-%s%s%s%s]", opt->shortName ? "" : "-", item, + argDescrip ? (opt->shortName ? " " : "=") : "", + argDescrip ? argDescrip : ""); + + return cursor + len + 1; +} + +static int singleTableUsage(FILE * f, int cursor, const struct poptOption * table, + const char *translation_domain) { + const struct poptOption * opt; + + opt = table; + while (opt->longName || opt->shortName || opt->arg) { + if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) + translation_domain = (const char *)opt->arg; + else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) + cursor = singleTableUsage(f, cursor, opt->arg, + translation_domain); + else if ((opt->longName || opt->shortName) && + !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) + cursor = singleOptionUsage(f, cursor, opt, translation_domain); + + opt++; + } + + return cursor; +} + +static int showShortOptions(const struct poptOption * opt, FILE * f, + char * str) { + char s[300]; /* this is larger then the ascii set, so + it should do just fine */ + + if (!str) { + str = s; + memset(str, 0, sizeof(s)); + } + + while (opt->longName || opt->shortName || opt->arg) { + if (opt->shortName && !(opt->argInfo & POPT_ARG_MASK)) + str[strlen(str)] = opt->shortName; + else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) + showShortOptions(opt->arg, f, str); + + opt++; + } + + if (s != str || !*s) + return 0; + + fprintf(f, " [-%s]", s); + return strlen(s) + 4; +} + +void poptPrintUsage(poptContext con, FILE * f, int flags) { + int cursor; + + cursor = showHelpIntro(con, f); + cursor += showShortOptions(con->options, f, NULL); + singleTableUsage(f, cursor, con->options, NULL); + + if (con->otherHelp) { + cursor += strlen(con->otherHelp) + 1; + if (cursor > 79) fprintf(f, "\n "); + fprintf(f, " %s", con->otherHelp); + } + + fprintf(f, "\n"); +} + +void poptSetOtherOptionHelp(poptContext con, const char * text) { + if (con->otherHelp) free(con->otherHelp); + con->otherHelp = g_strdup(text); +} diff --git a/apps/irssi/src/lib-popt/poptint.h b/apps/irssi/src/lib-popt/poptint.h new file mode 100644 index 00000000..a1346716 --- /dev/null +++ b/apps/irssi/src/lib-popt/poptint.h @@ -0,0 +1,54 @@ +/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING + file accompanying popt source distributions, available from + ftp://ftp.redhat.com/pub/code/popt */ + +#ifndef H_POPTINT +#define H_POPTINT + +struct optionStackEntry { + int argc; + char ** argv; + int next; + char * nextArg; + char * nextCharArg; + struct poptAlias * currAlias; + int stuffed; +}; + +struct execEntry { + char * longName; + char shortName; + char * script; +}; + +struct poptContext_s { + struct optionStackEntry optionStack[POPT_OPTION_DEPTH], * os; + char ** leftovers; + int numLeftovers; + int nextLeftover; + const struct poptOption * options; + int restLeftover; + char * appName; + struct poptAlias * aliases; + int numAliases; + int flags; + struct execEntry * execs; + int numExecs; + char ** finalArgv; + int finalArgvCount; + int finalArgvAlloced; + struct execEntry * doExec; + char * execPath; + int execAbsolute; + char * otherHelp; +}; + +#ifdef HAVE_DGETTEXT +#define D_(dom, str) dgettext(dom, str) +#define POPT_(foo) D_("popt", foo) +#else +#define POPT_(foo) (foo) +#define D_(dom, str) (str) +#endif + +#endif diff --git a/apps/irssi/src/lib-popt/poptparse.c b/apps/irssi/src/lib-popt/poptparse.c new file mode 100644 index 00000000..eb2a6721 --- /dev/null +++ b/apps/irssi/src/lib-popt/poptparse.c @@ -0,0 +1,98 @@ +/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING + file accompanying popt source distributions, available from + ftp://ftp.redhat.com/pub/code/popt */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "popt.h" + +static const int poptArgvArrayGrowDelta = 5; + +int poptParseArgvString(const char * s, int * argcPtr, char *** argvPtr) { + char * buf, * bufStart, * dst; + const char * src; + char quote = '\0'; + int argvAlloced = poptArgvArrayGrowDelta; + char ** argv = malloc(sizeof(*argv) * argvAlloced); + char ** argv2; + int argc = 0; + int i, buflen; + + buflen = strlen(s) + 1; + bufStart = buf = malloc(buflen); + memset(buf, '\0', buflen); + + src = s; + argv[argc] = buf; + + while (*src) { + if (quote == *src) { + quote = '\0'; + } else if (quote) { + if (*src == '\\') { + src++; + if (!*src) { + free(argv); + free(bufStart); + return POPT_ERROR_BADQUOTE; + } + if (*src != quote) *buf++ = '\\'; + } + *buf++ = *src; + } else if (isspace(*src)) { + if (*argv[argc]) { + buf++, argc++; + if (argc == argvAlloced) { + argvAlloced += poptArgvArrayGrowDelta; + argv = realloc(argv, sizeof(*argv) * argvAlloced); + } + argv[argc] = buf; + } + } else switch (*src) { + case '"': + case '\'': + quote = *src; + break; + case '\\': + src++; + if (!*src) { + free(argv); + free(bufStart); + return POPT_ERROR_BADQUOTE; + } + /* fallthrough */ + default: + *buf++ = *src; + } + + src++; + } + + if (strlen(argv[argc])) { + argc++, buf++; + } + + dst = malloc(argc * sizeof(*argv) + (buf - bufStart)); + argv2 = (void *) dst; + dst += argc * sizeof(*argv); + memcpy(argv2, argv, argc * sizeof(*argv)); + memcpy(dst, bufStart, buf - bufStart); + + for (i = 0; i < argc; i++) { + argv2[i] = dst + (argv[i] - bufStart); + } + + free(argv); + + *argvPtr = argv2; + *argcPtr = argc; + + free(bufStart); + return 0; +} diff --git a/apps/irssi/src/silc/Makefile.am b/apps/irssi/src/silc/Makefile.am new file mode 100644 index 00000000..05b9a00a --- /dev/null +++ b/apps/irssi/src/silc/Makefile.am @@ -0,0 +1,5 @@ +SUBDIRS = core + +noinst_LIBRARIES = libsilc.a + +libsilc_a_SOURCES = silc.c diff --git a/apps/irssi/src/silc/silc.c b/apps/irssi/src/silc/silc.c new file mode 100644 index 00000000..de2bde07 --- /dev/null +++ b/apps/irssi/src/silc/silc.c @@ -0,0 +1,4 @@ +/* this file is automatically generated by configure - don't change */ +void silc_core_init(void); void silc_core_deinit(void); +void silc_init(void) { silc_core_init(); } +void silc_deinit(void) { silc_core_deinit(); } diff --git a/apps/irssi/stamp.h.in b/apps/irssi/stamp.h.in new file mode 100644 index 00000000..e69de29b diff --git a/apps/irssi/syntax.pl b/apps/irssi/syntax.pl new file mode 100755 index 00000000..f0ec779b --- /dev/null +++ b/apps/irssi/syntax.pl @@ -0,0 +1,57 @@ +#!/usr/bin/perl +# +# This script reads the syntaces of commands from irssi source tree. +# Then it browses through all '.in' files in the current directory and +# substitutes '@SYNTAX:foo@' tags with real syntaces found. This data +# is written into the corresponding files without the '.in' extension. +# For example: help.in -> ../help +# +# This path has to be changed. It should point to your irssi/src directory +# Remember to include the asterisk ('*'). +$SRC_PATH='src'; + +$FOO = `find src -name '*.c' -exec perl findsyntax.pl \{\} \\; | sed 's/.*SYNTAX: //' > irssi_syntax`; + +while () { + next if (/Makefile/); + + open (FILE, "$_"); + @data = ; + close (FILE); + $count = 0; + foreach $DATARIVI (@data) { + if ($DATARIVI =~ /\@SYNTAX\:(.+)\@/) { + $etsittava = "\U$1 "; + $SYNTAX = `grep \'^$etsittava\' irssi_syntax`; + $SYNTAX =~ s/\*\///g; + $SYNTAX =~ s/ *$//; $SYNTAX =~ s/ *\n/\n/g; + + # add %| after "COMMAND SUB " so parameters will indent correctly + $SYNTAX =~ s/^([A-Z ]+)/\1%|/; + $SYNTAX =~ s/(\n[A-Z ]+)/\1%|/g; + # no need for this if there's no parameters + $SYNTAX =~ s/%\|$//; + $DATARIVI = $SYNTAX; + } elsif ($DATARIVI =~ /^\S+/) { + if ($data[$count+1] =~ /^\S+/) { + chomp $DATARIVI; + $DATARIVI =~ s/ *$//g; + $DATARIVI .= " "; + } + } else { + $DATARIVI =~ s/^\t/ / while ($DATARIVI =~ /^\t/); + } + $count++; + } + + # must always end with empty line + push @data, "\n" if ($data[@data-1] ne "\n"); + push @data, "\n" if ($data[@data-2] !~ /\n$/); + + $newfilename = $_; $newfilename =~ s/\.in$//; + $newfilename =~ s/\/in\//\//; + open (NEWFILE, ">$newfilename"); + print NEWFILE @data; + close (NEWFILE); +} +unlink "irssi_syntax"; -- 2.24.0