source: functions.c @ 259e60a8

release-1.8release-1.9
Last change on this file since 259e60a8 was 259e60a8, checked in by Anders Kaseorg <andersk@mit.edu>, 12 years ago
Remove the length limit on field values in :info
  • Property mode set to 100644
File size: 95.7 KB
RevLine 
[7d4fbcd]1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <signal.h>
[789462a]5#include <netinet/in.h>
[7d4fbcd]6#include <string.h>
7#include <time.h>
[2adaf1d]8#include <sys/types.h>
9#include <sys/stat.h>
[8f44c6b]10#include <sys/wait.h>
11#include <errno.h>
[d09e5a1]12#include <signal.h>
[7d4fbcd]13#include "owl.h"
[d564c3d]14#include "filterproc.h"
[7d4fbcd]15
[e19eb97]16char *owl_function_command(const char *cmdbuff)
[d54838d]17{
[7d4fbcd]18  owl_function_debugmsg("executing command: %s", cmdbuff);
19  return owl_cmddict_execute(owl_global_get_cmddict(&g), 
20                             owl_global_get_context(&g), cmdbuff);
21}
22
[e19eb97]23char *owl_function_command_argv(const char *const *argv, int argc)
[c4ba74d]24{
25  return owl_cmddict_execute_argv(owl_global_get_cmddict(&g),
26                                  owl_global_get_context(&g),
27                                  argv, argc);
28}
29
[e19eb97]30void owl_function_command_norv(const char *cmdbuff)
[d54838d]31{
[7d4fbcd]32  char *rv;
[4b464a4]33  rv=owl_function_command(cmdbuff);
[3b8a563]34  g_free(rv);
[7d4fbcd]35}
36
[e19eb97]37void owl_function_command_alias(const char *alias_from, const char *alias_to)
[d54838d]38{
[7d4fbcd]39  owl_cmddict_add_alias(owl_global_get_cmddict(&g), alias_from, alias_to);
40}
41
[0a0fb74]42const owl_cmd *owl_function_get_cmd(const char *name)
[d54838d]43{
[7d4fbcd]44  return owl_cmddict_find(owl_global_get_cmddict(&g), name);
45}
46
[c79a047]47void owl_function_show_commands(void)
[d54838d]48{
[7d4fbcd]49  owl_list l;
50  owl_fmtext fm;
51
52  owl_fmtext_init_null(&fm);
53  owl_fmtext_append_bold(&fm, "Commands:  ");
54  owl_fmtext_append_normal(&fm, "(use 'show command <name>' for details)\n");
[f25df21]55  owl_list_create(&l);
[7d4fbcd]56  owl_cmddict_get_names(owl_global_get_cmddict(&g), &l);
57  owl_fmtext_append_list(&fm, &l, "\n", owl_function_cmd_describe);
58  owl_fmtext_append_normal(&fm, "\n");
59  owl_function_popless_fmtext(&fm);
[f25df21]60  owl_list_cleanup(&l, g_free);
[7ab0020]61  owl_fmtext_cleanup(&fm);
[7d4fbcd]62}
63
[e19eb97]64void owl_function_show_view(const char *viewname)
[ef56a67]65{
[9e5c9f3]66  const owl_view *v;
[ef56a67]67  owl_fmtext fm;
68
69  /* we only have the one view right now */
70  v=owl_global_get_current_view(&g);
71  if (viewname && strcmp(viewname, owl_view_get_name(v))) {
[ec6ff52]72    owl_function_error("No view named '%s'", viewname);
[ef56a67]73    return;
74  }
75
76  owl_fmtext_init_null(&fm);
77  owl_view_to_fmtext(v, &fm);
78  owl_function_popless_fmtext(&fm);
[7ab0020]79  owl_fmtext_cleanup(&fm);
[ef56a67]80}
81
[c79a047]82void owl_function_show_styles(void) {
[f1e629d]83  owl_list l;
84  owl_fmtext fm;
85
86  owl_fmtext_init_null(&fm);
87  owl_fmtext_append_bold(&fm, "Styles:\n");
[f25df21]88  owl_list_create(&l);
[f1e629d]89  owl_global_get_style_names(&g, &l);
90  owl_fmtext_append_list(&fm, &l, "\n", owl_function_style_describe);
91  owl_fmtext_append_normal(&fm, "\n");
92  owl_function_popless_fmtext(&fm);
[ddbbcffa]93  owl_list_cleanup(&l, g_free);
[7ab0020]94  owl_fmtext_cleanup(&fm);
[f1e629d]95}
96
[c6adf17]97static void _owl_function_timer_append_fmtext(gpointer data, gpointer user_data) {
98  owl_fmtext *fm = user_data;
99  owl_timer *timer = data;
[3472845]100  char *str = g_strdup_printf("- %s: in %d seconds",
101                              timer->name ? timer->name : "(unnamed)",
102                              (int)(timer->time - time(NULL)));
[c6adf17]103  owl_fmtext_append_normal(fm, str);
[ddbbcffa]104  g_free(str);
[c6adf17]105  if (timer->interval) {
[3472845]106    str = g_strdup_printf(", repeat every %d seconds", timer->interval);
[c6adf17]107    owl_fmtext_append_normal(fm, str);
[ddbbcffa]108    g_free(str);
[c6adf17]109  }
110  owl_fmtext_append_normal(fm, "\n");
111}
112
113void owl_function_show_timers(void) {
114  owl_fmtext fm;
115  GList **timers;
116
117  owl_fmtext_init_null(&fm);
118  owl_fmtext_append_bold(&fm, "Active timers:\n");
119
120  timers = owl_global_get_timerlist(&g);
121  g_list_foreach(*timers, _owl_function_timer_append_fmtext, &fm);
122
123  owl_function_popless_fmtext(&fm);
124  owl_fmtext_cleanup(&fm);
125}
126
[e19eb97]127char *owl_function_style_describe(const char *name) {
128  const char *desc;
[65b2173]129  char *s;
[1fdab04]130  const owl_style *style;
[f1e629d]131  style = owl_global_get_style_by_name(&g, name);
132  if (style) {
133    desc = owl_style_get_description(style);
134  } else {
135    desc = "???";
136  }
[3472845]137  s = g_strdup_printf("%-20s - %s%s", name,
138                      0 == owl_style_validate(style) ? "" : "[INVALID] ",
139                      desc);
[f1e629d]140  return s;
141}
[ef56a67]142
[e19eb97]143char *owl_function_cmd_describe(const char *name)
[d54838d]144{
[0a0fb74]145  const owl_cmd *cmd = owl_cmddict_find(owl_global_get_cmddict(&g), name);
[7d4fbcd]146  if (cmd) return owl_cmd_describe(cmd);
147  else return(NULL);
148}
149
[e19eb97]150void owl_function_show_command(const char *name)
[d54838d]151{
[7d4fbcd]152  owl_function_help_for_command(name);
153}
154
[c79a047]155void owl_function_show_license(void)
[debb15d]156{
[e19eb97]157  const char *text;
[debb15d]158
159  text=""
[4228f8b]160    "barnowl version " OWL_VERSION_STRING "\n"
[b03c714]161    "Copyright (c) 2006-2011 The BarnOwl Developers. All rights reserved.\n"
[debb15d]162    "Copyright (c) 2004 James Kretchmar. All rights reserved.\n"
163    "\n"
164    "Redistribution and use in source and binary forms, with or without\n"
165    "modification, are permitted provided that the following conditions are\n"
166    "met:\n"
167    "\n"
168    "   * Redistributions of source code must retain the above copyright\n"
169    "     notice, this list of conditions and the following disclaimer.\n"
170    "\n"
171    "   * Redistributions in binary form must reproduce the above copyright\n"
172    "     notice, this list of conditions and the following disclaimer in\n"
173    "     the documentation and/or other materials provided with the\n"
174    "     distribution.\n"
175    "\n"
176    "   * Redistributions in any form must be accompanied by information on\n"
177    "     how to obtain complete source code for the Owl software and any\n"
178    "     accompanying software that uses the Owl software. The source code\n"
179    "     must either be included in the distribution or be available for no\n"
180    "     more than the cost of distribution plus a nominal fee, and must be\n"
181    "     freely redistributable under reasonable conditions. For an\n"
182    "     executable file, complete source code means the source code for\n"
183    "     all modules it contains. It does not include source code for\n"
184    "     modules or files that typically accompany the major components of\n"
185    "     the operating system on which the executable file runs.\n"
186    "\n"
187    "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
188    "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"
189    "WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR\n"
190    "NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE\n"
191    "LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n"
192    "CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n"
193    "SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\n"
194    "BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n"
195    "WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\n"
196    "OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN\n"
197    "IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n";
198  owl_function_popless_text(text);
199}
200
[c79a047]201void owl_function_show_quickstart(void)
[799b60e]202{
[e19eb97]203    const char *message =
[799b60e]204    "Move between messages with the arrow keys, and press 'r' to reply.\n"
205    "For more info, press 'h' or visit http://barnowl.mit.edu/\n\n"
206#ifdef HAVE_LIBZEPHYR
207    "@b(Zephyr:)\n"
208    "To send a message to a user, type ':zwrite @b(username)'. You can also\n"
209    "press 'z' and then type the username. To subscribe to a class, type\n"
210    "':sub @b(class)', and then type ':zwrite -c @b(class)' to send.\n\n"
211#endif
212    "@b(AIM:)\n"
213    "Log in to AIM with ':aimlogin @b(screenname)'. Use ':aimwrite @b(screenname)',\n"
214    "or 'a' and then the screen name, to send someone a message.\n\n"
215    ;
216
217    if (owl_perlconfig_is_function("BarnOwl::Hooks::_get_quickstart")) {
218        char *perlquickstart = owl_perlconfig_execute("BarnOwl::Hooks::_get_quickstart()");
219        if (perlquickstart) {
[3472845]220            char *result = g_strdup_printf("%s%s", message, perlquickstart);
[799b60e]221            owl_function_adminmsg("BarnOwl Quickstart", result);
[ddbbcffa]222            g_free(result);
223            g_free(perlquickstart);
[799b60e]224            return;
225        }
226    }
227    owl_function_adminmsg("BarnOwl Quickstart", message);
228}
229
230
[15b34fd]231/* Create an admin message, append it to the global list of messages
232 * and redisplay if necessary.
233 */
[e19eb97]234void owl_function_adminmsg(const char *header, const char *body)
[d54838d]235{
[7d4fbcd]236  owl_message *m;
[4b464a4]237
[96828e4]238  m=g_new(owl_message, 1);
[15b34fd]239  owl_message_create_admin(m, header, body);
240 
[8fec514]241  /* add it to the global list and current view */
[7d4fbcd]242  owl_messagelist_append_element(owl_global_get_msglist(&g), m);
243  owl_view_consider_message(owl_global_get_current_view(&g), m);
244
[15b34fd]245  /* do followlast if necessary */
246  if (owl_global_should_followlast(&g)) owl_function_lastmsg_noredisplay();
[7d4fbcd]247
[15b34fd]248  /* redisplay etc. */
[7d4fbcd]249  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
250}
251
[e5da3fe]252/* Queues outgoing zephyrs; if z sends to n people, queue n messages
253 * (except in case of cc). If there are no recipients queues 1
254 * message.
[15b34fd]255 */
[e5da3fe]256void owl_function_add_outgoing_zephyrs(const owl_zwrite *z)
[d09e5a1]257{
[e5da3fe]258  if (z->cc || owl_zwrite_get_numrecips(z) == 0) {
259    /* create the message */
260    owl_message *m = g_new(owl_message, 1);
261    owl_message_create_from_zwrite(m, z, owl_zwrite_get_message(z), 0);
[d09e5a1]262
[e5da3fe]263    owl_global_messagequeue_addmsg(&g, m);
264  } else {
265    int i;
266    for (i = 0; i < owl_zwrite_get_numrecips(z); i++) {
267      /* create the message */
268      owl_message *m = g_new(owl_message, 1);
269      owl_message_create_from_zwrite(m, z, owl_zwrite_get_message(z), i);
[15b34fd]270
[e5da3fe]271      owl_global_messagequeue_addmsg(&g, m);
272    }
273  }
[15b34fd]274}
275
276/* Create an outgoing AIM message, returns a pointer to the created
277 * message or NULL if we're not logged into AIM (and thus unable to
278 * create the message).  Does not put it on the global queue.  Use
[e5da3fe]279 * owl_global_messagequeue_addmsg() for that.
[15b34fd]280 */
[e19eb97]281owl_message *owl_function_make_outgoing_aim(const char *body, const char *to)
[15b34fd]282{
283  owl_message *m;
284
285  /* error if we're not logged into aim */
286  if (!owl_global_is_aimloggedin(&g)) return(NULL);
287 
[96828e4]288  m=g_new(owl_message, 1);
[d559df9]289  owl_message_create_aim(m,
290                         owl_global_get_aim_screenname(&g),
291                         to,
292                         body,
293                         OWL_MESSAGE_DIRECTION_OUT,
294                         0);
[15b34fd]295  return(m);
[d09e5a1]296}
297
[15b34fd]298/* Create an outgoing loopback message and return a pointer to it.
299 * Does not append it to the global queue, use
[13fe062]300 * owl_global_messagequeue_addmsg() for that.
[15b34fd]301 */
[e19eb97]302owl_message *owl_function_make_outgoing_loopback(const char *body)
[37eab7f]303{
304  owl_message *m;
305
306  /* create the message */
[96828e4]307  m=g_new(owl_message, 1);
[37eab7f]308  owl_message_create_loopback(m, body);
309  owl_message_set_direction_out(m);
310
[15b34fd]311  return(m);
[37eab7f]312}
313
[1b1cd2c]314void owl_function_start_edit_win(const char *line, void (*callback)(owl_editwin *), void *data, void (*cleanup)(void *))
[d54838d]315{
[7d4fbcd]316  owl_editwin *e;
[c394de8]317  owl_context *ctx;
[bdbec0a]318  char *s;
319
320  /* create and setup the editwin */
[58d47ca]321  e = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_MULTILINE,
322                                   owl_global_get_msg_history(&g));
[bdbec0a]323  owl_editwin_set_dotsend(e);
[3472845]324  s = g_strdup_printf("----> %s\n", line);
[bdbec0a]325  owl_editwin_set_locktext(e, s);
[ddbbcffa]326  g_free(s);
[bdbec0a]327
[38cc669]328  owl_editwin_set_cbdata(e, data, cleanup);
329  owl_editwin_set_callback(e, callback);
[4a41f16]330  ctx = owl_editcontext_new(OWL_CTX_EDITMULTI, e, "editmulti",
331                            owl_global_deactivate_editcontext, &g);
[c394de8]332  owl_global_push_context_obj(&g, ctx);
333
[bdbec0a]334}
335
[987cf3f]336static void owl_function_write_setup(const char *noun)
[bdbec0a]337{
338
339  if (!owl_global_get_lockout_ctrld(&g))
340    owl_function_makemsg("Type your %s below.  "
341                         "End with ^D or a dot on a line by itself."
342                         "  ^C will quit.", noun);
343  else
344    owl_function_makemsg("Type your %s below.  "
345                         "End with a dot on a line by itself.  ^C will quit.",
346                         noun);
347}
348
[987cf3f]349void owl_function_zwrite_setup(owl_zwrite *z)
[bdbec0a]350{
[7d4fbcd]351  /* send a ping if necessary */
352  if (owl_global_is_txping(&g)) {
[987cf3f]353    owl_zwrite_send_ping(z);
[7d4fbcd]354  }
355
[987cf3f]356
357  owl_function_write_setup("zephyr");
358  owl_function_start_edit_win(z->zwriteline,
359                              &owl_callback_zwrite,
360                              z, (void(*)(void*))owl_zwrite_delete);
[7d4fbcd]361}
362
[3f82515]363void owl_function_aimwrite_setup(const char *to)
[d09e5a1]364{
[3f82515]365  /* TODO: We probably actually want an owl_aimwrite object like
366   * owl_zwrite. */
[3472845]367  char *line = g_strdup_printf("aimwrite %s", to);
[987cf3f]368  owl_function_write_setup("message");
369  owl_function_start_edit_win(line,
370                              &owl_callback_aimwrite,
[d4927a7]371                              g_strdup(to),
[ddbbcffa]372                              g_free);
373  g_free(line);
[d09e5a1]374}
375
[c79a047]376void owl_function_loopwrite_setup(void)
[37eab7f]377{
[987cf3f]378  owl_function_write_setup("message");
379  owl_function_start_edit_win("loopwrite",
380                              &owl_callback_loopwrite,
[9359e5a]381                              NULL, NULL);
[1b6b2f3]382}
383
384void owl_callback_zwrite(owl_editwin *e) {
[987cf3f]385  owl_zwrite *z = owl_editwin_get_cbdata(e);
386  owl_function_zwrite(z, owl_editwin_get_text(e));
[37eab7f]387}
388
[e5da3fe]389/* send, log and display outgoing zephyrs.  If 'msg' is NULL the
390 * message is expected to be set from the zwrite line itself
[9ceee9d]391 */
[c23f678]392#ifdef HAVE_LIBZEPHYR
[987cf3f]393void owl_function_zwrite(owl_zwrite *z, const char *msg)
[d54838d]394{
[0743696]395  int ret;
[d309eb3]396
[987cf3f]397  if(strcmp(z->cmd, "zcrypt") == 0) {
398    owl_function_zcrypt(z, msg);
[a5fc448]399    return;
400  }
401
[9ceee9d]402  /* create the zwrite and send the message */
[987cf3f]403  owl_zwrite_populate_zsig(z);
[9ceee9d]404  if (msg) {
[987cf3f]405    owl_zwrite_set_message(z, msg);
[d309eb3]406  }
[0743696]407  ret = owl_zwrite_send_message(z);
408  if (ret != 0) {
409    owl_function_makemsg("Error sending zephyr: %s", error_message(ret));
410    return;
411  }
[9ceee9d]412  owl_function_makemsg("Waiting for ack...");
[d309eb3]413
[15b34fd]414  /* If it's personal */
[987cf3f]415  if (owl_zwrite_is_personal(z)) {
[15b34fd]416    /* create the outgoing message */
[e5da3fe]417    owl_function_add_outgoing_zephyrs(z);
[9ceee9d]418  }
[d309eb3]419}
[c23f678]420#else
421void owl_function_zwrite(owl_zwrite *z, const char *msg) {
422}
423#endif
[d309eb3]424
[e5da3fe]425/* send, log and display outgoing zcrypt zephyrs.  If 'msg' is NULL
[ce7db4d]426 * the message is expected to be set from the zwrite line itself
427 */
[987cf3f]428void owl_function_zcrypt(owl_zwrite *z, const char *msg)
[d54838d]429{
[9ceee9d]430  char *cryptmsg;
[d564c3d]431  const char *argv[7];
[9a7b4f2]432  char *zcrypt;
[d564c3d]433  int rv, status;
[96582d5]434  char *old_msg;
[7d4fbcd]435
436  /* create the zwrite and send the message */
[987cf3f]437  owl_zwrite_populate_zsig(z);
[ce7db4d]438  if (msg) {
[987cf3f]439    owl_zwrite_set_message(z, msg);
[ce7db4d]440  }
[d4927a7]441  old_msg = g_strdup(owl_zwrite_get_message(z));
[d564c3d]442
[3472845]443  zcrypt = g_strdup_printf("%s/zcrypt", owl_get_bindir());
[d564c3d]444  argv[0] = "zcrypt";
445  argv[1] = "-E";
[987cf3f]446  argv[2] = "-c"; argv[3] = owl_zwrite_get_class(z);
447  argv[4] = "-i"; argv[5] = owl_zwrite_get_instance(z);
[d564c3d]448  argv[6] = NULL;
449
[987cf3f]450  rv = call_filter(zcrypt, argv, owl_zwrite_get_message(z), &cryptmsg, &status);
[9a7b4f2]451
[ddbbcffa]452  g_free(zcrypt);
[d564c3d]453
454  if (rv || status) {
[3b8a563]455    g_free(cryptmsg);
[ddbbcffa]456    g_free(old_msg);
[ec6ff52]457    owl_function_error("Error in zcrypt, possibly no key found.  Message not sent.");
[9ceee9d]458    owl_function_beep();
459    return;
460  }
461
[7bfc613]462  owl_zwrite_set_message_raw(z, cryptmsg);
[987cf3f]463  owl_zwrite_set_opcode(z, "crypt");
[7bfc613]464
[987cf3f]465  owl_zwrite_send_message(z);
[7d4fbcd]466  owl_function_makemsg("Waiting for ack...");
467
[15b34fd]468  /* If it's personal */
[987cf3f]469  if (owl_zwrite_is_personal(z)) {
[c43c77b]470    /* Create the outgoing message. Restore the un-crypted message for display. */
[7bfc613]471    owl_zwrite_set_message_raw(z, old_msg);
[e5da3fe]472    owl_function_add_outgoing_zephyrs(z);
[7d4fbcd]473  }
474
475  /* free the zwrite */
[ddbbcffa]476  g_free(cryptmsg);
[7d4fbcd]477}
478
[1b6b2f3]479void owl_callback_aimwrite(owl_editwin *e) {
[3f82515]480  char *to = owl_editwin_get_cbdata(e);
481  owl_function_aimwrite(to, owl_editwin_get_text(e), true);
[1b6b2f3]482}
483
[3f82515]484void owl_function_aimwrite(const char *to, const char *msg, bool unwrap)
[d09e5a1]485{
[ec6ff52]486  int ret;
[65b2173]487  char *format_msg;
[15b34fd]488  owl_message *m;
[f82e233]489
490  /* make a formatted copy of the message */
[d4927a7]491  format_msg = g_strdup(msg);
[3f82515]492  if (unwrap)
493    owl_text_wordunwrap(format_msg);
[ec6ff52]494 
[f82e233]495  /* send the message */
496  ret=owl_aim_send_im(to, format_msg);
[ec6ff52]497  if (!ret) {
498    owl_function_makemsg("AIM message sent.");
499  } else {
500    owl_function_error("Could not send AIM message.");
501  }
[d09e5a1]502
[15b34fd]503  /* create the outgoing message */
504  m=owl_function_make_outgoing_aim(msg, to);
505
[3c7d086a]506  if (m) {
[13a3c1db]507    owl_global_messagequeue_addmsg(&g, m);
[15b34fd]508  } else {
[3c7d086a]509    owl_function_error("Could not create outgoing AIM message");
[d09e5a1]510  }
[f82e233]511
[ddbbcffa]512  g_free(format_msg);
[d09e5a1]513}
514
[e19eb97]515void owl_function_send_aimawymsg(const char *to, const char *msg)
[9854278]516{
517  int ret;
518  char *format_msg;
[15b34fd]519  owl_message *m;
[9854278]520
521  /* make a formatted copy of the message */
[d4927a7]522  format_msg=g_strdup(msg);
[9854278]523  owl_text_wordunwrap(format_msg);
524 
525  /* send the message */
526  ret=owl_aim_send_awaymsg(to, format_msg);
527  if (!ret) {
528    /* owl_function_makemsg("AIM message sent."); */
529  } else {
530    owl_function_error("Could not send AIM message.");
531  }
532
[15b34fd]533  /* create the message */
534  m=owl_function_make_outgoing_aim(msg, to);
535  if (m) {
[13a3c1db]536    owl_global_messagequeue_addmsg(&g, m);
[15b34fd]537  } else {
538    owl_function_error("Could not create AIM message");
[9854278]539  }
[ddbbcffa]540  g_free(format_msg);
[9854278]541}
542
[1b6b2f3]543void owl_callback_loopwrite(owl_editwin *e) {
544  owl_function_loopwrite(owl_editwin_get_text(e));
545}
546
[e19eb97]547void owl_function_loopwrite(const char *msg)
[37eab7f]548{
[15b34fd]549  owl_message *min, *mout;
[37eab7f]550
551  /* create a message and put it on the message queue.  This simulates
552   * an incoming message */
[96828e4]553  min=g_new(owl_message, 1);
[4211f50b]554  mout=owl_function_make_outgoing_loopback(msg);
[13a3c1db]555
[37eab7f]556  if (owl_global_is_displayoutgoing(&g)) {
[13a3c1db]557    owl_global_messagequeue_addmsg(&g, mout);
[15b34fd]558  } else {
[91634ec]559    owl_message_delete(mout);
[37eab7f]560  }
561
[13a3c1db]562  owl_message_create_loopback(min, msg);
563  owl_message_set_direction_in(min);
564  owl_global_messagequeue_addmsg(&g, min);
565
[37eab7f]566  /* fake a makemsg */
567  owl_function_makemsg("loopback message sent");
568}
569
[b950088]570/* If filter is non-null, looks for the next message matching
571 * that filter.  If skip_deleted, skips any deleted messages.
572 * If last_if_none, will stop at the last message in the view
573 * if no matching messages are found.  */
[e19eb97]574void owl_function_nextmsg_full(const char *filter, int skip_deleted, int last_if_none)
[d54838d]575{
[b950088]576  int curmsg, i, viewsize, found;
[9e5c9f3]577  const owl_view *v;
[4542047]578  const owl_filter *f = NULL;
[c08c70a]579  const owl_message *m;
[7d4fbcd]580
581  v=owl_global_get_current_view(&g);
[b950088]582
583  if (filter) {
584    f=owl_global_get_filter(&g, filter);
585    if (!f) {
[ec6ff52]586      owl_function_error("No %s filter defined", filter);
[b950088]587      return;
588    }
[7d4fbcd]589  }
590
[b950088]591  curmsg=owl_global_get_curmsg(&g);
592  viewsize=owl_view_get_size(v);
593  found=0;
[7d4fbcd]594
[b950088]595  /* just check to make sure we're in bounds... */
596  if (curmsg>viewsize-1) curmsg=viewsize-1;
597  if (curmsg<0) curmsg=0;
[7d4fbcd]598
[b950088]599  for (i=curmsg+1; i<viewsize; i++) {
600    m=owl_view_get_element(v, i);
601    if (skip_deleted && owl_message_is_delete(m)) continue;
602    if (f && !owl_filter_message_match(f, m)) continue;
603    found = 1;
604    break;
605  }
606
607  if (i>owl_view_get_size(v)-1) i=owl_view_get_size(v)-1;
[5763474]608  if (i<0) i=0;
[b950088]609
610  if (!found) {
[799b60e]611    owl_function_makemsg("already at last%s message%s%s%s",
[b950088]612                         skip_deleted?" non-deleted":"",
[799b60e]613                         filter?" in ":"", filter?filter:"",
614                         owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g)) ?
615                         ", press Enter to scroll" : "");
[5a6e6b9]616    /* if (!skip_deleted) owl_function_beep(); */
[7d4fbcd]617  }
618
[b950088]619  if (last_if_none || found) {
620    owl_global_set_curmsg(&g, i);
621    owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
622    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
623    owl_global_set_direction_downwards(&g);
624  }
625}
[7d4fbcd]626
[e19eb97]627void owl_function_prevmsg_full(const char *filter, int skip_deleted, int first_if_none)
[d54838d]628{
[402eb16f]629  int curmsg, i, found;
[9e5c9f3]630  const owl_view *v;
[4542047]631  const owl_filter *f = NULL;
[c08c70a]632  const owl_message *m;
[7d4fbcd]633
634  v=owl_global_get_current_view(&g);
635
[b950088]636  if (filter) {
637    f=owl_global_get_filter(&g, filter);
638    if (!f) {
[ec6ff52]639      owl_function_error("No %s filter defined", filter);
[b950088]640      return;
[7d4fbcd]641    }
[b950088]642  }
[7d4fbcd]643
[b950088]644  curmsg=owl_global_get_curmsg(&g);
645  found=0;
646
647  /* just check to make sure we're in bounds... */
648  if (curmsg<0) curmsg=0;
649
650  for (i=curmsg-1; i>=0; i--) {
651    m=owl_view_get_element(v, i);
652    if (skip_deleted && owl_message_is_delete(m)) continue;
653    if (f && !owl_filter_message_match(f, m)) continue;
654    found = 1;
655    break;
656  }
657
658  if (i<0) i=0;
659
660  if (!found) {
[f51bc78]661    owl_function_makemsg("already at first%s message%s%s",
[b950088]662                         skip_deleted?" non-deleted":"",
663                         filter?" in ":"", filter?filter:"");
[5a6e6b9]664    /* if (!skip_deleted) owl_function_beep(); */
[b950088]665  }
666
667  if (first_if_none || found) {
668    owl_global_set_curmsg(&g, i);
669    owl_function_calculate_topmsg(OWL_DIRECTION_UPWARDS);
670    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
671    owl_global_set_direction_upwards(&g);
[7d4fbcd]672  }
673}
674
[c79a047]675void owl_function_nextmsg(void)
[d54838d]676{
[b950088]677  owl_function_nextmsg_full(NULL, 0, 1);
678}
[7d4fbcd]679
[c79a047]680void owl_function_prevmsg(void)
[d54838d]681{
[b950088]682  owl_function_prevmsg_full(NULL, 0, 1);
683}
[7d4fbcd]684
[c79a047]685void owl_function_nextmsg_notdeleted(void)
[d54838d]686{
[b950088]687  owl_function_nextmsg_full(NULL, 1, 1);
688}
[7d4fbcd]689
[c79a047]690void owl_function_prevmsg_notdeleted(void)
[d54838d]691{
[b950088]692  owl_function_prevmsg_full(NULL, 1, 1);
[7d4fbcd]693}
694
[b950088]695/* if move_after is 1, moves after the delete */
[d54838d]696void owl_function_deletecur(int move_after)
697{
[7d4fbcd]698  int curmsg;
699  owl_view *v;
700
701  v=owl_global_get_current_view(&g);
702
703  /* bail if there's no current message */
704  if (owl_view_get_size(v) < 1) {
[ec6ff52]705    owl_function_error("No current message to delete");
[7d4fbcd]706    return;
707  }
708
709  /* mark the message for deletion */
710  curmsg=owl_global_get_curmsg(&g);
711  owl_view_delete_element(v, curmsg);
712
[b950088]713  if (move_after) {
714    /* move the poiner in the appropriate direction
715     * to the next undeleted msg */
716    if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) {
717      owl_function_prevmsg_notdeleted();
718    } else {
719      owl_function_nextmsg_notdeleted();
720    }
[7d4fbcd]721  }
722}
723
[d54838d]724void owl_function_undeletecur(int move_after)
725{
[7d4fbcd]726  int curmsg;
727  owl_view *v;
728
729  v=owl_global_get_current_view(&g);
730 
731  if (owl_view_get_size(v) < 1) {
[ec6ff52]732    owl_function_error("No current message to undelete");
[7d4fbcd]733    return;
734  }
735  curmsg=owl_global_get_curmsg(&g);
736
737  owl_view_undelete_element(v, curmsg);
738
[b950088]739  if (move_after) {
740    if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) {
741      if (curmsg>0) {
742        owl_function_prevmsg();
743      } else {
744        owl_function_nextmsg();
745      }
[7d4fbcd]746    } else {
[b950088]747      owl_function_nextmsg();
[7d4fbcd]748    }
749  }
750
751  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
752}
753
[c79a047]754void owl_function_expunge(void)
[d54838d]755{
[7d4fbcd]756  int curmsg;
[c08c70a]757  const owl_message *m;
[7d4fbcd]758  owl_messagelist *ml;
759  owl_view *v;
[486688f]760  int lastmsgid=0;
[7d4fbcd]761
762  curmsg=owl_global_get_curmsg(&g);
763  v=owl_global_get_current_view(&g);
764  ml=owl_global_get_msglist(&g);
765
766  m=owl_view_get_element(v, curmsg);
[486688f]767  if (m) lastmsgid = owl_message_get_id(m);
[7d4fbcd]768
769  /* expunge the message list */
770  owl_messagelist_expunge(ml);
771
772  /* update all views (we only have one right now) */
773  owl_view_recalculate(v);
774
[486688f]775  /* find where the new position should be
776     (as close as possible to where we last where) */
777  curmsg = owl_view_get_nearest_to_msgid(v, lastmsgid);
778  if (curmsg>owl_view_get_size(v)-1) curmsg = owl_view_get_size(v)-1;
779  if (curmsg<0) curmsg = 0;
780  owl_global_set_curmsg(&g, curmsg);
781  owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
[7d4fbcd]782  /* if there are no messages set the direction to down in case we
783     delete everything upwards */
784  owl_global_set_direction_downwards(&g);
785 
786  owl_function_makemsg("Messages expunged");
787  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
788}
789
[c79a047]790void owl_function_firstmsg(void)
[d54838d]791{
[7d4fbcd]792  owl_global_set_curmsg(&g, 0);
793  owl_global_set_topmsg(&g, 0);
794  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
795  owl_global_set_direction_downwards(&g);
796}
797
[c79a047]798void owl_function_lastmsg_noredisplay(void)
[d54838d]799{
[5eeea3b]800  int oldcurmsg, curmsg;
[9e5c9f3]801  const owl_view *v;
[7d4fbcd]802
803  v=owl_global_get_current_view(&g);
[5eeea3b]804  oldcurmsg=owl_global_get_curmsg(&g);
805  curmsg=owl_view_get_size(v)-1; 
[7d4fbcd]806  if (curmsg<0) curmsg=0;
807  owl_global_set_curmsg(&g, curmsg);
[5eeea3b]808  if (oldcurmsg < curmsg) {
809    owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
810  } else if (curmsg<owl_view_get_size(v)) {
811    /* If already at the end, blank the screen and move curmsg
812     * past the end of the messages. */
813    owl_global_set_topmsg(&g, curmsg+1);
814    owl_global_set_curmsg(&g, curmsg+1);
815  } 
[6b580b0]816  /* owl_mainwin_redisplay(owl_global_get_mainwin(&g)); */
[7d4fbcd]817  owl_global_set_direction_downwards(&g);
818}
819
[c79a047]820void owl_function_lastmsg(void)
[d54838d]821{
[7d4fbcd]822  owl_function_lastmsg_noredisplay();
823  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
824}
825
[c79a047]826void owl_function_shift_right(void)
[d54838d]827{
[7d4fbcd]828  owl_global_set_rightshift(&g, owl_global_get_rightshift(&g)+10);
829}
830
[c79a047]831void owl_function_shift_left(void)
[d54838d]832{
[7d4fbcd]833  int shift;
834
835  shift=owl_global_get_rightshift(&g);
[8c97fa1]836  if (shift > 0) {
837    owl_global_set_rightshift(&g, MAX(shift - 10, 0));
[7d4fbcd]838  } else {
839    owl_function_beep();
[f51bc78]840    owl_function_makemsg("Already full left");
[7d4fbcd]841  }
842}
843
[c79a047]844void owl_function_unsuball(void)
[d54838d]845{
[7d4fbcd]846  unsuball();
847  owl_function_makemsg("Unsubscribed from all messages.");
848}
849
[95474d7]850
851/* Load zephyr subscriptions from the named 'file' and load zephyr's
852 * default subscriptions as well.  An error message is printed if
853 * 'file' can't be opened or if zephyr reports an error in
854 * subscribing.
855 *
856 * If 'file' is NULL, this look for the default filename
857 * $HOME/.zephyr.subs.  If the file can not be opened in this case
858 * only, no error message is printed.
859 */
[e19eb97]860void owl_function_loadsubs(const char *file)
[d54838d]861{
[4357be8]862  int ret, ret2;
[e19eb97]863  const char *foo;
[65b2173]864  char *path;
[ecd5dc5]865
[95474d7]866  if (file==NULL) {
867    ret=owl_zephyr_loadsubs(NULL, 0);
868  } else {
[27d8d83]869    path = owl_util_makepath(file);
870    ret=owl_zephyr_loadsubs(path, 1);
[ddbbcffa]871    g_free(path);
[95474d7]872  }
[ecd5dc5]873
[4357be8]874  /* for backwards compatibility for now */
875  ret2=owl_zephyr_loaddefaultsubs();
876
[ecd5dc5]877  if (!owl_context_is_interactive(owl_global_get_context(&g))) return;
[95474d7]878
879  foo=file?file:"file";
880  if (ret==0 && ret2==0) {
881    if (!file) {
882      owl_function_makemsg("Subscribed to messages.");
883    } else {
884      owl_function_makemsg("Subscribed to messages from %s", file);
885    }
[7d4fbcd]886  } else if (ret==-1) {
[95474d7]887    owl_function_error("Could not read %s", foo);
[7d4fbcd]888  } else {
[95474d7]889    owl_function_error("Error subscribing to messages");
[7d4fbcd]890  }
891}
892
[e19eb97]893void owl_function_loadloginsubs(const char *file)
[d54838d]894{
[7933748]895  int ret;
[ecd5dc5]896
[7933748]897  ret=owl_zephyr_loadloginsubs(file);
[ecd5dc5]898
899  if (!owl_context_is_interactive(owl_global_get_context(&g))) return;
[7933748]900  if (ret==0) {
901  } else if (ret==-1) {
[ec6ff52]902    owl_function_error("Could not open file for login subscriptions.");
[7933748]903  } else {
[ec6ff52]904    owl_function_error("Error subscribing to login messages from file.");
[7933748]905  }
906}
907
[1b6b2f3]908void owl_callback_aimlogin(owl_editwin *e) {
[8dfb59c]909  char *user = owl_editwin_get_cbdata(e);
910  owl_function_aimlogin(user,
[db8b00b]911                        owl_editwin_get_text(e));
[1b6b2f3]912}
913
[e19eb97]914void owl_function_aimlogin(const char *user, const char *passwd) {
[4211f50b]915  int ret;
916
917  /* clear the buddylist */
918  owl_buddylist_clear(owl_global_get_buddylist(&g));
919
920  /* try to login */
921  ret=owl_aim_login(user, passwd);
922  if (ret) owl_function_makemsg("Warning: login for %s failed.\n", user);
923}
924
[c79a047]925void owl_function_suspend(void)
[d54838d]926{
[7d4fbcd]927  endwin();
928  printf("\n");
929  kill(getpid(), SIGSTOP);
930
931  /* resize to reinitialize all the windows when we come back */
932  owl_command_resize();
933}
934
[c79a047]935void owl_function_zaway_toggle(void)
[d54838d]936{
[7d4fbcd]937  if (!owl_global_is_zaway(&g)) {
938    owl_global_set_zaway_msg(&g, owl_global_get_zaway_msg_default(&g));
939    owl_function_zaway_on();
940  } else {
941    owl_function_zaway_off();
942  }
943}
944
[c79a047]945void owl_function_zaway_on(void)
[d54838d]946{
[7d4fbcd]947  owl_global_set_zaway_on(&g);
[4b660cc]948  owl_function_makemsg("zaway set (%s)", owl_global_get_zaway_msg(&g));
[7d4fbcd]949}
950
[c79a047]951void owl_function_zaway_off(void)
[d54838d]952{
[7d4fbcd]953  owl_global_set_zaway_off(&g);
[4b660cc]954  owl_function_makemsg("zaway off");
955}
956
[c79a047]957void owl_function_aaway_toggle(void)
[4b660cc]958{
959  if (!owl_global_is_aaway(&g)) {
960    owl_global_set_aaway_msg(&g, owl_global_get_aaway_msg_default(&g));
961    owl_function_aaway_on();
962  } else {
963    owl_function_aaway_off();
964  }
965}
966
[c79a047]967void owl_function_aaway_on(void)
[4b660cc]968{
969  owl_global_set_aaway_on(&g);
970  /* owl_aim_set_awaymsg(owl_global_get_zaway_msg(&g)); */
971  owl_function_makemsg("AIM away set (%s)", owl_global_get_aaway_msg(&g));
972}
973
[c79a047]974void owl_function_aaway_off(void)
[4b660cc]975{
976  owl_global_set_aaway_off(&g);
977  /* owl_aim_set_awaymsg(""); */
978  owl_function_makemsg("AIM away off");
[7d4fbcd]979}
980
[c79a047]981void owl_function_quit(void)
[d54838d]982{
[7d4fbcd]983  char *ret;
984 
985  /* zlog out if we need to */
[7433402]986  if (owl_global_is_havezephyr(&g) &&
987      owl_global_is_shutdownlogout(&g)) {
[31e48a3]988    owl_zephyr_zlog_out();
[7d4fbcd]989  }
990
991  /* execute the commands in shutdown */
[0337203]992  ret = owl_perlconfig_execute("BarnOwl::Hooks::_shutdown();");
[3b8a563]993  g_free(ret);
[7d4fbcd]994
[d09e5a1]995  /* signal our child process, if any */
996  if (owl_global_get_newmsgproc_pid(&g)) {
997    kill(owl_global_get_newmsgproc_pid(&g), SIGHUP);
998  }
[8c46404]999 
1000  /* Quit AIM */
1001  if (owl_global_is_aimloggedin(&g)) {
1002    owl_aim_logout();
1003  }
1004
[7d4fbcd]1005  owl_function_debugmsg("Quitting Owl");
[3ecd78b]1006  owl_select_quit_loop();
[7d4fbcd]1007}
1008
[d54838d]1009void owl_function_calculate_topmsg(int direction)
1010{
[aa2f33b3]1011  int recwinlines, topmsg, curmsg;
[9e5c9f3]1012  const owl_view *v;
[7d4fbcd]1013
1014  v=owl_global_get_current_view(&g);
[aa2f33b3]1015  curmsg=owl_global_get_curmsg(&g);
1016  topmsg=owl_global_get_topmsg(&g);
[7d4fbcd]1017  recwinlines=owl_global_get_recwin_lines(&g);
1018
[f9c43ae]1019  /*
[7d4fbcd]1020  if (owl_view_get_size(v) < 1) {
1021    return;
1022  }
[f9c43ae]1023  */
[aa2f33b3]1024
1025  switch (owl_global_get_scrollmode(&g)) {
1026  case OWL_SCROLLMODE_TOP:
[f9c43ae]1027    topmsg = owl_function_calculate_topmsg_top(direction, v, curmsg, topmsg, recwinlines);
[aa2f33b3]1028    break;
1029  case OWL_SCROLLMODE_NEARTOP:
[f9c43ae]1030    topmsg = owl_function_calculate_topmsg_neartop(direction, v, curmsg, topmsg, recwinlines);
[aa2f33b3]1031    break;
1032  case OWL_SCROLLMODE_CENTER:
[f9c43ae]1033    topmsg = owl_function_calculate_topmsg_center(direction, v, curmsg, topmsg, recwinlines);
[aa2f33b3]1034    break;
1035  case OWL_SCROLLMODE_PAGED:
[f9c43ae]1036    topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 0);
[aa2f33b3]1037    break;
1038  case OWL_SCROLLMODE_PAGEDCENTER:
[f9c43ae]1039    topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 1);
[aa2f33b3]1040    break;
1041  case OWL_SCROLLMODE_NORMAL:
1042  default:
[f9c43ae]1043    topmsg = owl_function_calculate_topmsg_normal(direction, v, curmsg, topmsg, recwinlines);
[aa2f33b3]1044  }
[3a2daac]1045  owl_function_debugmsg("Calculated a topmsg of %i", topmsg);
[aa2f33b3]1046  owl_global_set_topmsg(&g, topmsg);
1047}
1048
1049/* Returns what the new topmsg should be. 
1050 * Passed the last direction of movement,
1051 * the current view,
1052 * the current message number in the view,
1053 * the top message currently being displayed,
1054 * and the number of lines in the recwin.
1055 */
[9e5c9f3]1056int owl_function_calculate_topmsg_top(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
[d54838d]1057{
[f9c43ae]1058  return(curmsg);
[aa2f33b3]1059}
1060
[9e5c9f3]1061int owl_function_calculate_topmsg_neartop(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
[d54838d]1062{
[aa2f33b3]1063  if (curmsg>0 
1064      && (owl_message_get_numlines(owl_view_get_element(v, curmsg-1))
1065          <  recwinlines/2)) {
[f9c43ae]1066    return(curmsg-1);
[aa2f33b3]1067  } else {
[f9c43ae]1068    return(curmsg);
[aa2f33b3]1069  }
1070}
1071 
[9e5c9f3]1072int owl_function_calculate_topmsg_center(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
[d54838d]1073{
[aa2f33b3]1074  int i, last, lines;
1075
1076  last = curmsg;
1077  lines = 0;
1078  for (i=curmsg-1; i>=0; i--) {
1079    lines += owl_message_get_numlines(owl_view_get_element(v, i));
1080    if (lines > recwinlines/2) break;
1081    last = i;
1082  }
[f9c43ae]1083  return(last);
[aa2f33b3]1084}
1085 
[9e5c9f3]1086int owl_function_calculate_topmsg_paged(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines, int center_on_page)
[d54838d]1087{
[aa2f33b3]1088  int i, last, lines, savey;
1089 
1090  /* If we're off the top of the screen, scroll up such that the
1091   * curmsg is near the botton of the screen. */
1092  if (curmsg < topmsg) {
1093    last = curmsg;
1094    lines = 0;
1095    for (i=curmsg; i>=0; i--) {
1096      lines += owl_message_get_numlines(owl_view_get_element(v, i));
1097      if (lines > recwinlines) break;
1098    last = i;
1099    }
1100    if (center_on_page) {
[f9c43ae]1101      return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines));
[aa2f33b3]1102    } else {
[f9c43ae]1103      return(last);
[aa2f33b3]1104    }
1105  }
1106
1107  /* Find number of lines from top to bottom of curmsg (store in savey) */
1108  savey=0;
1109  for (i=topmsg; i<=curmsg; i++) {
1110    savey+=owl_message_get_numlines(owl_view_get_element(v, i));
1111  }
1112
1113  /* if we're off the bottom of the screen, scroll down */
1114  if (savey > recwinlines) {
1115    if (center_on_page) {
[f9c43ae]1116      return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines));
[aa2f33b3]1117    } else {
[f9c43ae]1118      return(curmsg);
[aa2f33b3]1119    }
1120  }
1121
1122  /* else just stay as we are... */
[f9c43ae]1123  return(topmsg);
[aa2f33b3]1124}
1125
[9e5c9f3]1126int owl_function_calculate_topmsg_normal(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
[d54838d]1127{
[801b7ac]1128  int savey, i, foo, y;
[f9c43ae]1129
[88736cb]1130  if (curmsg<0) return(topmsg);
1131   
[f9c43ae]1132  /* If we're off the top of the screen then center */
1133  if (curmsg<topmsg) {
1134    topmsg=owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines);
1135  }
1136
[801b7ac]1137  /* If curmsg is so far past topmsg that there are more messages than
1138     lines, skip the line counting that follows because we're
1139     certainly off screen.  */
1140  savey=curmsg-topmsg;
1141  if (savey <= recwinlines) {
1142    /* Find number of lines from top to bottom of curmsg (store in savey) */
1143    savey = 0;
1144    for (i=topmsg; i<=curmsg; i++) {
1145      savey+=owl_message_get_numlines(owl_view_get_element(v, i));
1146    }
[7d4fbcd]1147  }
1148
[f9c43ae]1149  /* If we're off the bottom of the screen, set the topmsg to curmsg
1150   * and scroll upwards */
1151  if (savey > recwinlines) {
1152    topmsg=curmsg;
[801b7ac]1153    savey=owl_message_get_numlines(owl_view_get_element(v, curmsg));
[f9c43ae]1154    direction=OWL_DIRECTION_UPWARDS;
[7d4fbcd]1155  }
[f9c43ae]1156 
[7d4fbcd]1157  /* If our bottom line is less than 1/4 down the screen then scroll up */
1158  if (direction == OWL_DIRECTION_UPWARDS || direction == OWL_DIRECTION_NONE) {
1159    if (savey < (recwinlines / 4)) {
1160      y=0;
[801b7ac]1161      for (i=curmsg; i>=0; i--) {
1162        foo=owl_message_get_numlines(owl_view_get_element(v, i));
[7d4fbcd]1163        /* will we run the curmsg off the screen? */
1164        if ((foo+y) >= recwinlines) {
[801b7ac]1165          i++;
1166          if (i>curmsg) i=curmsg;
[7d4fbcd]1167          break;
1168        }
1169        /* have saved 1/2 the screen space? */
1170        y+=foo;
1171        if (y > (recwinlines / 2)) break;
1172      }
[801b7ac]1173      if (i<0) i=0;
1174      return(i);
[7d4fbcd]1175    }
1176  }
1177
1178  if (direction == OWL_DIRECTION_DOWNWARDS || direction == OWL_DIRECTION_NONE) {
1179    /* If curmsg bottom line is more than 3/4 down the screen then scroll down */
1180    if (savey > ((recwinlines * 3)/4)) {
1181      y=0;
1182      /* count lines from the top until we can save 1/2 the screen size */
[801b7ac]1183      for (i=topmsg; i<curmsg; i++) {
1184        y+=owl_message_get_numlines(owl_view_get_element(v, i));
[7d4fbcd]1185        if (y > (recwinlines / 2)) break;
1186      }
[801b7ac]1187      if (i==curmsg) {
1188        i--;
[7d4fbcd]1189      }
[801b7ac]1190      return(i+1);
[7d4fbcd]1191    }
1192  }
[aa2f33b3]1193
[f9c43ae]1194  return(topmsg);
[7d4fbcd]1195}
1196
[c79a047]1197void owl_function_resize(void)
[d54838d]1198{
[7d4fbcd]1199  owl_global_set_resize_pending(&g);
1200}
1201
[4479497]1202void G_GNUC_PRINTF(1, 2) owl_function_debugmsg(const char *fmt, ...)
[d54838d]1203{
[7d4fbcd]1204  FILE *file;
1205  time_t now;
1206  va_list ap;
1207  va_start(ap, fmt);
1208
[52761cc]1209  if (!owl_global_is_debug_fast(&g))
1210    return;
[7d4fbcd]1211
[d12a8c7]1212  file = owl_global_get_debug_file_handle(&g);
[52761cc]1213  if (!file) /* XXX should report this */
1214    return;
[7d4fbcd]1215
[52761cc]1216  now = time(NULL);
[7d4fbcd]1217
[52761cc]1218  fprintf(file, "[%d -  %.24s - %lds]: ",
1219          (int) getpid(), ctime(&now), now - owl_global_get_starttime(&g));
[7d4fbcd]1220  vfprintf(file, fmt, ap);
[52761cc]1221  putc('\n', file);
[d12a8c7]1222  fflush(file);
[7d4fbcd]1223
1224  va_end(ap);
1225}
1226
[c79a047]1227void owl_function_beep(void)
[d54838d]1228{
[7d4fbcd]1229  if (owl_global_is_bell(&g)) {
1230    beep();
1231  }
1232}
1233
[e19eb97]1234int owl_function_subscribe(const char *class, const char *inst, const char *recip)
[d54838d]1235{
[7d4fbcd]1236  int ret;
1237
1238  ret=owl_zephyr_sub(class, inst, recip);
1239  if (ret) {
[ec6ff52]1240    owl_function_error("Error subscribing.");
[7d4fbcd]1241  } else {
1242    owl_function_makemsg("Subscribed.");
1243  }
[3617286]1244  return(ret);
[7d4fbcd]1245}
1246
[e19eb97]1247void owl_function_unsubscribe(const char *class, const char *inst, const char *recip)
[d54838d]1248{
[7d4fbcd]1249  int ret;
1250
1251  ret=owl_zephyr_unsub(class, inst, recip);
1252  if (ret) {
[ec6ff52]1253    owl_function_error("Error subscribing.");
[7d4fbcd]1254  } else {
1255    owl_function_makemsg("Unsubscribed.");
1256  }
1257}
1258
[e8128c5]1259static void _dirty_everything(owl_window *w) {
1260  if (!owl_window_is_realized(w))
1261    return;
1262  owl_window_dirty(w);
1263  owl_window_children_foreach(w, (GFunc)_dirty_everything, NULL);
[7d4fbcd]1264}
1265
[c79a047]1266void owl_function_full_redisplay(void)
[d54838d]1267{
[1d81c51]1268  /* Ask every widget to redraw itself. */
[e8128c5]1269  _dirty_everything(owl_window_get_screen());
[1d81c51]1270  /* Force ncurses to redisplay everything. */
1271  clearok(stdscr, TRUE);
[7d4fbcd]1272}
1273
[e19eb97]1274void owl_function_popless_text(const char *text)
[d54838d]1275{
[7d4fbcd]1276  owl_popwin *pw;
1277  owl_viewwin *v;
1278
[9eb38bb]1279  if (owl_global_get_popwin(&g) || owl_global_get_viewwin(&g)) {
[4cf7b1b]1280    owl_function_error("Popwin already in use.");
1281    return;
1282  }
[03ca005]1283  pw = owl_popwin_new();
1284  owl_global_set_popwin(&g, pw);
1285  owl_popwin_up(pw);
[9eb38bb]1286
1287  v = owl_viewwin_new_text(owl_popwin_get_content(pw), text);
1288  owl_global_set_viewwin(&g, v);
1289
[07b59ea]1290  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless", NULL);
[7d4fbcd]1291}
1292
[075ba92]1293void owl_function_popless_fmtext(const owl_fmtext *fm)
[d54838d]1294{
[7d4fbcd]1295  owl_popwin *pw;
1296  owl_viewwin *v;
1297
[9eb38bb]1298  if (owl_global_get_popwin(&g) || owl_global_get_viewwin(&g)) {
[4cf7b1b]1299    owl_function_error("Popwin already in use.");
1300    return;
1301  }
[03ca005]1302  pw = owl_popwin_new();
1303  owl_global_set_popwin(&g, pw);
1304  owl_popwin_up(pw);
[9eb38bb]1305
1306  v = owl_viewwin_new_fmtext(owl_popwin_get_content(pw), fm);
1307  owl_global_set_viewwin(&g, v);
1308
[07b59ea]1309  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless", NULL);
[f17bff98]1310}
1311
[e19eb97]1312void owl_function_popless_file(const char *filename)
[f17bff98]1313{
1314  owl_fmtext fm;
1315  FILE *file;
[b7ee89b]1316  char *s = NULL;
[f17bff98]1317
1318  file=fopen(filename, "r");
1319  if (!file) {
1320    owl_function_error("Could not open file: %s", filename);
1321    return;
1322  }
1323
1324  owl_fmtext_init_null(&fm);
[b7ee89b]1325  while (owl_getline(&s, file))
1326    owl_fmtext_append_normal(&fm, s);
[ddbbcffa]1327  g_free(s);
[f17bff98]1328
1329  owl_function_popless_fmtext(&fm);
[7ab0020]1330  owl_fmtext_cleanup(&fm);
[f17bff98]1331  fclose(file);
[7d4fbcd]1332}
1333
[c79a047]1334void owl_function_about(void)
[d54838d]1335{
[2101a50]1336  owl_function_popless_text(
1337    "This is barnowl version " OWL_VERSION_STRING ".\n\n"
1338    "barnowl is a fork of the Owl zephyr client, written and\n"
1339    "maintained by Alejandro Sedeno and Nelson Elhage at the\n"
1340    "Massachusetts Institute of Technology. \n"
1341    "\n"
1342    "Owl was written by James Kretchmar. The first version, 0.5, was\n"
1343    "released in March 2002.\n"
1344    "\n"
1345    "The name 'owl' was chosen in reference to the owls in the\n"
1346    "Harry Potter novels, who are tasked with carrying messages\n"
1347    "between Witches and Wizards. The name 'barnowl' was chosen\n"
1348    "because we feel our owls should live closer to our ponies.\n"
1349    "\n"
[b03c714]1350    "Copyright (c) 2006-2011 The BarnOwl Developers. All rights reserved.\n"
[2101a50]1351    "Copyright (c) 2004 James Kretchmar. All rights reserved.\n"
1352    "Copyright 2002 Massachusetts Institute of Technology\n"
1353    "\n"
1354    "This program is free software. You can redistribute it and/or\n"
1355    "modify under the terms of the Sleepycat License. Use the \n"
1356    "':show license' command to display the full license\n"
1357  );
[7d4fbcd]1358}
1359
[c79a047]1360void owl_function_info(void)
[d54838d]1361{
[c08c70a]1362  const owl_message *m;
[5789230]1363  owl_fmtext fm, attrfm;
[9e5c9f3]1364  const owl_view *v;
[09489b89]1365#ifdef HAVE_LIBZEPHYR
[1077891a]1366  const ZNotice_t *n;
[09489b89]1367#endif
[7d4fbcd]1368
[d0d65df]1369  owl_fmtext_init_null(&fm);
1370 
[7d4fbcd]1371  v=owl_global_get_current_view(&g);
[5eeea3b]1372  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1373  if (!m || owl_view_get_size(v)==0) {
[ec6ff52]1374    owl_function_error("No message selected\n");
[7d4fbcd]1375    return;
1376  }
1377
[5789230]1378  owl_fmtext_append_bold(&fm, "General Information:\n");
[57609b3]1379  owl_fmtext_appendf_normal(&fm, "  Msg Id    : %i\n", owl_message_get_id(m));
[df0d93a]1380
[5789230]1381  owl_fmtext_append_normal(&fm, "  Type      : ");
[37eab7f]1382  owl_fmtext_append_bold(&fm, owl_message_get_type(m));
[df0d93a]1383  owl_fmtext_append_normal(&fm, "\n");
1384
[4b464a4]1385  if (owl_message_is_direction_in(m)) {
[5789230]1386    owl_fmtext_append_normal(&fm, "  Direction : in\n");
[4b464a4]1387  } else if (owl_message_is_direction_out(m)) {
[5789230]1388    owl_fmtext_append_normal(&fm, "  Direction : out\n");
[4b464a4]1389  } else if (owl_message_is_direction_none(m)) {
[5789230]1390    owl_fmtext_append_normal(&fm, "  Direction : none\n");
[4b464a4]1391  } else {
[5789230]1392    owl_fmtext_append_normal(&fm, "  Direction : unknown\n");
[4b464a4]1393  }
[df0d93a]1394
[57609b3]1395  owl_fmtext_appendf_normal(&fm, "  Time      : %s\n", owl_message_get_timestr(m));
[4b464a4]1396
[df0d93a]1397  if (!owl_message_is_type_admin(m)) {
[57609b3]1398    owl_fmtext_appendf_normal(&fm, "  Sender    : %s\n", owl_message_get_sender(m));
1399    owl_fmtext_appendf_normal(&fm, "  Recipient : %s\n", owl_message_get_recipient(m));
[df0d93a]1400  }
[57609b3]1401
[0ff8fb57]1402  if (owl_message_is_type_zephyr(m)) {
[5789230]1403    owl_fmtext_append_bold(&fm, "\nZephyr Specific Information:\n");
[0ff8fb57]1404   
[57609b3]1405    owl_fmtext_appendf_normal(&fm, "  Class     : %s\n", owl_message_get_class(m));
1406    owl_fmtext_appendf_normal(&fm, "  Instance  : %s\n", owl_message_get_instance(m));
1407    owl_fmtext_appendf_normal(&fm, "  Opcode    : %s\n", owl_message_get_opcode(m));
[09489b89]1408#ifdef HAVE_LIBZEPHYR
[d0d65df]1409    if (owl_message_is_direction_in(m)) {
[259e60a8]1410      char *tmpbuff;
1411      int i, fields;
[09489b89]1412
[d0d65df]1413      n=owl_message_get_notice(m);
[df0d93a]1414
[5a95b69]1415      if (!owl_message_is_pseudo(m)) {
1416        owl_fmtext_append_normal(&fm, "  Kind      : ");
1417        if (n->z_kind==UNSAFE) {
1418          owl_fmtext_append_normal(&fm, "UNSAFE\n");
1419        } else if (n->z_kind==UNACKED) {
1420          owl_fmtext_append_normal(&fm, "UNACKED\n");
1421        } else if (n->z_kind==ACKED) {
1422          owl_fmtext_append_normal(&fm, "ACKED\n");
1423        } else if (n->z_kind==HMACK) {
1424          owl_fmtext_append_normal(&fm, "HMACK\n");
1425        } else if (n->z_kind==HMCTL) {
1426          owl_fmtext_append_normal(&fm, "HMCTL\n");
1427        } else if (n->z_kind==SERVACK) {
1428          owl_fmtext_append_normal(&fm, "SERVACK\n");
1429        } else if (n->z_kind==SERVNAK) {
1430          owl_fmtext_append_normal(&fm, "SERVNACK\n");
1431        } else if (n->z_kind==CLIENTACK) {
1432          owl_fmtext_append_normal(&fm, "CLIENTACK\n");
1433        } else if (n->z_kind==STAT) {
1434          owl_fmtext_append_normal(&fm, "STAT\n");
1435        } else {
1436          owl_fmtext_append_normal(&fm, "ILLEGAL VALUE\n");
1437        }
[d0d65df]1438      }
[57609b3]1439      owl_fmtext_appendf_normal(&fm, "  Host      : %s\n", owl_message_get_hostname(m));
[5a95b69]1440
1441      if (!owl_message_is_pseudo(m)) {
1442        owl_fmtext_append_normal(&fm, "\n");
[57609b3]1443        owl_fmtext_appendf_normal(&fm, "  Port      : %i\n", ntohs(n->z_port));
[f12d199]1444        owl_fmtext_appendf_normal(&fm, "  Auth      : %s\n", owl_zephyr_get_authstr(n));
[57609b3]1445
1446        /* FIXME make these more descriptive */
[f12d199]1447        owl_fmtext_appendf_normal(&fm, "  Checkd Ath: %i\n", n->z_checked_auth);
[57609b3]1448        owl_fmtext_appendf_normal(&fm, "  Multi notc: %s\n", n->z_multinotice);
1449        owl_fmtext_appendf_normal(&fm, "  Num other : %i\n", n->z_num_other_fields);
1450        owl_fmtext_appendf_normal(&fm, "  Msg Len   : %i\n", n->z_message_len);
[5a95b69]1451
1452        fields=owl_zephyr_get_num_fields(n);
[57609b3]1453        owl_fmtext_appendf_normal(&fm, "  Fields    : %i\n", fields);
1454
[259e60a8]1455        for (i = 0; i < fields; i++) {
1456          tmpbuff = owl_zephyr_get_field_as_utf8(n, i + 1);
1457
1458          g_strdelimit(tmpbuff, "\n", '~');
1459          g_strdelimit(tmpbuff, "\r", '!');
1460
1461          owl_fmtext_appendf_normal(&fm, "  Field %i   : %s\n", i + 1, tmpbuff);
1462          g_free(tmpbuff);
[5a95b69]1463        }
[57609b3]1464        owl_fmtext_appendf_normal(&fm, "  Default Fm: %s\n", n->z_default_format);
[d0d65df]1465      }
[57609b3]1466
[7d4fbcd]1467    }
[57609b3]1468#endif
[7d4fbcd]1469  }
[0ff8fb57]1470
[5789230]1471  owl_fmtext_append_bold(&fm, "\nOwl Message Attributes:\n");
1472  owl_message_attributes_tofmtext(m, &attrfm);
1473  owl_fmtext_append_fmtext(&fm, &attrfm);
[d0d65df]1474 
1475  owl_function_popless_fmtext(&fm);
[7ab0020]1476  owl_fmtext_cleanup(&fm);
1477  owl_fmtext_cleanup(&attrfm);
[7d4fbcd]1478}
1479
[5639bf2]1480/* print the current message in a popup window.
1481 * Use the 'default' style regardless of whatever
1482 * style the user may be using
1483 */
[c79a047]1484void owl_function_curmsg_to_popwin(void)
[d54838d]1485{
[9e5c9f3]1486  const owl_view *v;
[c08c70a]1487  const owl_message *m;
[1fdab04]1488  const owl_style *s;
[5639bf2]1489  owl_fmtext fm;
[7d4fbcd]1490
[5639bf2]1491  v=owl_global_get_current_view(&g);
1492  s=owl_global_get_style_by_name(&g, "default");
[7d4fbcd]1493
[5eeea3b]1494  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1495
1496  if (!m || owl_view_get_size(v)==0) {
[ec6ff52]1497    owl_function_error("No current message");
[7d4fbcd]1498    return;
1499  }
1500
[5639bf2]1501  owl_fmtext_init_null(&fm);
1502  owl_style_get_formattext(s, &fm, m);
1503
1504  owl_function_popless_fmtext(&fm);
[7ab0020]1505  owl_fmtext_cleanup(&fm);
[7d4fbcd]1506}
1507
[d54838d]1508void owl_function_page_curmsg(int step)
1509{
[7d4fbcd]1510  /* scroll down or up within the current message IF the message is truncated */
1511
1512  int offset, curmsg, lines;
[9e5c9f3]1513  const owl_view *v;
[7d4fbcd]1514  owl_message *m;
1515
1516  offset=owl_global_get_curmsg_vert_offset(&g);
1517  v=owl_global_get_current_view(&g);
1518  curmsg=owl_global_get_curmsg(&g);
1519  m=owl_view_get_element(v, curmsg);
[5eeea3b]1520  if (!m || owl_view_get_size(v)==0) return;
[7d4fbcd]1521  lines=owl_message_get_numlines(m);
1522
1523  if (offset==0) {
1524    /* Bail if the curmsg isn't the last one displayed */
1525    if (curmsg != owl_mainwin_get_last_msg(owl_global_get_mainwin(&g))) {
[f51bc78]1526      owl_function_makemsg("The entire message is already displayed");
[7d4fbcd]1527      return;
1528    }
1529   
1530    /* Bail if we're not truncated */
1531    if (!owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
[f51bc78]1532      owl_function_makemsg("The entire message is already displayed");
[7d4fbcd]1533      return;
1534    }
1535  }
1536 
1537 
1538  /* don't scroll past the last line */
1539  if (step>0) {
1540    if (offset+step > lines-1) {
1541      owl_global_set_curmsg_vert_offset(&g, lines-1);
1542    } else {
1543      owl_global_set_curmsg_vert_offset(&g, offset+step);
1544    }
1545  }
1546
1547  /* would we be before the beginning of the message? */
1548  if (step<0) {
1549    if (offset+step<0) {
1550      owl_global_set_curmsg_vert_offset(&g, 0);
1551    } else {
1552      owl_global_set_curmsg_vert_offset(&g, offset+step);
1553    }
1554  }
1555 
1556  /* redisplay */
1557  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1558}
1559
[d54838d]1560void owl_function_resize_typwin(int newsize)
1561{
[7d4fbcd]1562  owl_global_set_typwin_lines(&g, newsize);
[f6fae8d]1563  owl_mainpanel_layout_contents(&g.mainpanel);
[7d4fbcd]1564}
1565
[c79a047]1566void owl_function_mainwin_pagedown(void)
[d54838d]1567{
[7d4fbcd]1568  int i;
1569
1570  i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g));
1571  if (i<0) return;
[f2e36b5]1572  if (owl_mainwin_is_last_msg_truncated(owl_global_get_mainwin(&g))
1573      && (owl_global_get_curmsg(&g) < i)
1574      && (i>0)) {
1575    i--;
1576  }
[7d4fbcd]1577  owl_global_set_curmsg(&g, i);
1578  owl_function_nextmsg();
1579}
1580
[c79a047]1581void owl_function_mainwin_pageup(void)
[d54838d]1582{
[7d4fbcd]1583  owl_global_set_curmsg(&g, owl_global_get_topmsg(&g));
1584  owl_function_prevmsg();
1585}
1586
[c79a047]1587void owl_function_getsubs(void)
[d54838d]1588{
[09489b89]1589  char *buff;
[7d4fbcd]1590
[09489b89]1591  buff=owl_zephyr_getsubs();
[7d4fbcd]1592
[09489b89]1593  if (buff) {
1594    owl_function_popless_text(buff);
1595  } else {
1596    owl_function_popless_text("Error getting subscriptions");
[7d4fbcd]1597  }
[09489b89]1598           
[ddbbcffa]1599  g_free(buff);
[7d4fbcd]1600}
1601
[c79a047]1602void owl_function_printallvars(void)
[d54838d]1603{
[e19eb97]1604  const char *name;
[010a951]1605  char *var;
[7d4fbcd]1606  owl_list varnames;
[b4c270c]1607  int i, numvarnames;
1608  GString *str   = g_string_new("");
[7d4fbcd]1609
[b4c270c]1610  g_string_append_printf(str, "%-20s = %s\n", "VARIABLE", "VALUE");
1611  g_string_append_printf(str, "%-20s   %s\n",  "--------", "-----");
[f25df21]1612  owl_list_create(&varnames);
[7d4fbcd]1613  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
1614  numvarnames = owl_list_get_size(&varnames);
1615  for (i=0; i<numvarnames; i++) {
1616    name = owl_list_get_element(&varnames, i);
1617    if (name && name[0]!='_') {
[b4c270c]1618      g_string_append_printf(str, "\n%-20s = ", name);
[010a951]1619      var = owl_variable_get_tostring(owl_global_get_vardict(&g), name);
1620      if (var) {
1621        g_string_append(str, var);
1622        g_free(var);
1623      }
[7d4fbcd]1624    }
1625  }
[b4c270c]1626  g_string_append(str, "\n");
[f25df21]1627  owl_list_cleanup(&varnames, g_free);
[b4c270c]1628
1629  owl_function_popless_text(str->str);
[d222c44]1630  g_string_free(str, true);
[7d4fbcd]1631}
1632
[c79a047]1633void owl_function_show_variables(void)
[d54838d]1634{
[7d4fbcd]1635  owl_list varnames;
1636  owl_fmtext fm; 
1637  int i, numvarnames;
[e19eb97]1638  const char *varname;
[7d4fbcd]1639
1640  owl_fmtext_init_null(&fm);
1641  owl_fmtext_append_bold(&fm, 
1642      "Variables: (use 'show variable <name>' for details)\n");
[f25df21]1643  owl_list_create(&varnames);
[7d4fbcd]1644  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
1645  numvarnames = owl_list_get_size(&varnames);
1646  for (i=0; i<numvarnames; i++) {
1647    varname = owl_list_get_element(&varnames, i);
1648    if (varname && varname[0]!='_') {
[aa2f33b3]1649      owl_variable_describe(owl_global_get_vardict(&g), varname, &fm);
[7d4fbcd]1650    }
1651  }
[f25df21]1652  owl_list_cleanup(&varnames, g_free);
[7d4fbcd]1653  owl_function_popless_fmtext(&fm);
[7ab0020]1654  owl_fmtext_cleanup(&fm);
[7d4fbcd]1655}
1656
[e19eb97]1657void owl_function_show_variable(const char *name)
[d54838d]1658{
[7d4fbcd]1659  owl_fmtext fm; 
1660
1661  owl_fmtext_init_null(&fm);
1662  owl_variable_get_help(owl_global_get_vardict(&g), name, &fm);
1663  owl_function_popless_fmtext(&fm);
[7ab0020]1664  owl_fmtext_cleanup(&fm);
[7d4fbcd]1665}
1666
1667/* note: this applies to global message list, not to view.
1668 * If flag is 1, deletes.  If flag is 0, undeletes. */
[d54838d]1669void owl_function_delete_by_id(int id, int flag)
1670{
[3eb599d]1671  const owl_messagelist *ml;
[7d4fbcd]1672  owl_message *m;
1673  ml = owl_global_get_msglist(&g);
1674  m = owl_messagelist_get_by_id(ml, id);
1675  if (m) {
1676    if (flag == 1) {
1677      owl_message_mark_delete(m);
1678    } else if (flag == 0) {
1679      owl_message_unmark_delete(m);
1680    }
1681    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1682  } else {
[ec6ff52]1683    owl_function_error("No message with id %d: unable to mark for (un)delete",id);
[7d4fbcd]1684  }
1685}
1686
[c79a047]1687void owl_function_delete_automsgs(void)
[d54838d]1688{
[7d4fbcd]1689  /* mark for deletion all messages in the current view that match the
1690   * 'trash' filter */
1691
1692  int i, j, count;
1693  owl_message *m;
[9e5c9f3]1694  const owl_view *v;
[4542047]1695  const owl_filter *f;
[7d4fbcd]1696
1697  /* get the trash filter */
1698  f=owl_global_get_filter(&g, "trash");
1699  if (!f) {
[ec6ff52]1700    owl_function_error("No trash filter defined");
[7d4fbcd]1701    return;
1702  }
1703
1704  v=owl_global_get_current_view(&g);
1705
1706  count=0;
1707  j=owl_view_get_size(v);
1708  for (i=0; i<j; i++) {
1709    m=owl_view_get_element(v, i);
1710    if (owl_filter_message_match(f, m)) {
1711      count++;
1712      owl_message_mark_delete(m);
1713    }
1714  }
1715  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
[1c6c4d3]1716  owl_function_makemsg("%i messages marked for deletion", count);
[7d4fbcd]1717}
1718
[c79a047]1719void owl_function_status(void)
[d54838d]1720{
[49a8434]1721  char buff[MAXPATHLEN+1];
[7d4fbcd]1722  time_t start;
1723  int up, days, hours, minutes;
[a352335c]1724  owl_fmtext fm;
1725
1726  owl_fmtext_init_null(&fm);
[7d4fbcd]1727
1728  start=owl_global_get_starttime(&g);
1729
[d9b0b972]1730  owl_fmtext_append_normal(&fm, "General Information:\n");
1731
1732  owl_fmtext_append_normal(&fm, "  Version: ");
[a352335c]1733  owl_fmtext_append_normal(&fm, OWL_VERSION_STRING);
1734  owl_fmtext_append_normal(&fm, "\n");
1735
[cdd3959]1736  owl_fmtext_append_normal(&fm, "  Startup Arguments: ");
[a352335c]1737  owl_fmtext_append_normal(&fm, owl_global_get_startupargs(&g));
1738  owl_fmtext_append_normal(&fm, "\n");
[b6a7367]1739
1740  owl_fmtext_append_normal(&fm, "  Current Directory: ");
[49a8434]1741  if(getcwd(buff, MAXPATHLEN) == NULL) {
1742    owl_fmtext_append_normal(&fm, "<Error in getcwd>");
1743  } else {
1744    owl_fmtext_append_normal(&fm, buff);
1745  }
[b6a7367]1746  owl_fmtext_append_normal(&fm, "\n");
1747
[c1d166b]1748  owl_fmtext_appendf_normal(&fm, "  Startup Time: %s", ctime(&start));
[7d4fbcd]1749
1750  up=owl_global_get_runtime(&g);
1751  days=up/86400;
1752  up-=days*86400;
1753  hours=up/3600;
1754  up-=hours*3600;
1755  minutes=up/60;
1756  up-=minutes*60;
[c1d166b]1757  owl_fmtext_appendf_normal(&fm, "  Run Time: %i days %2.2i:%2.2i:%2.2i\n", days, hours, minutes, up);
[7d4fbcd]1758
[d9b0b972]1759  owl_fmtext_append_normal(&fm, "\nProtocol Options:\n");
1760  owl_fmtext_append_normal(&fm, "  Zephyr included    : ");
1761  if (owl_global_is_havezephyr(&g)) {
1762    owl_fmtext_append_normal(&fm, "yes\n");
[7d4fbcd]1763  } else {
[d9b0b972]1764    owl_fmtext_append_normal(&fm, "no\n");
[7d4fbcd]1765  }
[d9b0b972]1766  owl_fmtext_append_normal(&fm, "  AIM included       : yes\n");
1767  owl_fmtext_append_normal(&fm, "  Loopback included  : yes\n");
1768
[8262340]1769
[d9b0b972]1770  owl_fmtext_append_normal(&fm, "\nBuild Options:\n");
1771  owl_fmtext_append_normal(&fm, "  Stderr redirection : ");
1772#if OWL_STDERR_REDIR
1773  owl_fmtext_append_normal(&fm, "yes\n");
1774#else
1775  owl_fmtext_append_normal(&fm, "no\n");
1776#endif
1777 
1778
1779  owl_fmtext_append_normal(&fm, "\nAIM Status:\n");
1780  owl_fmtext_append_normal(&fm, "  Logged in: ");
[a352335c]1781  if (owl_global_is_aimloggedin(&g)) {
1782    owl_fmtext_append_normal(&fm, owl_global_get_aim_screenname(&g));
1783    owl_fmtext_append_normal(&fm, "\n");
1784  } else {
[d9b0b972]1785    owl_fmtext_append_normal(&fm, "(not logged in)\n");
[a352335c]1786  }
[d9b0b972]1787
1788  owl_fmtext_append_normal(&fm, "  Processing events: ");
[a352335c]1789  if (owl_global_is_doaimevents(&g)) {
[d9b0b972]1790    owl_fmtext_append_normal(&fm, "yes\n");
[a352335c]1791  } else {
[d9b0b972]1792    owl_fmtext_append_normal(&fm, "no\n");
[a352335c]1793  }
1794
1795  owl_function_popless_fmtext(&fm);
[7ab0020]1796  owl_fmtext_cleanup(&fm);
[7d4fbcd]1797}
1798
[c79a047]1799void owl_function_show_term(void)
[d54838d]1800{
[7d4fbcd]1801  owl_fmtext fm;
1802
1803  owl_fmtext_init_null(&fm);
[c1d166b]1804  owl_fmtext_appendf_normal(&fm, "Terminal Lines: %i\nTerminal Columns: %i\n",
[7d4fbcd]1805          owl_global_get_lines(&g),
1806          owl_global_get_cols(&g));
1807
[7b4f3be]1808  if (has_colors()) {
[7d4fbcd]1809    owl_fmtext_append_normal(&fm, "Color: Yes\n");
[9efa5bd]1810    owl_fmtext_appendf_normal(&fm, "Number of color pairs: %i\n", owl_util_get_colorpairs());
[c1d166b]1811    owl_fmtext_appendf_normal(&fm, "Can change colors: %s\n", can_change_color() ? "yes" : "no");
[7d4fbcd]1812  } else {
1813    owl_fmtext_append_normal(&fm, "Color: No\n");
1814  }
1815
1816  owl_function_popless_fmtext(&fm);
[7ab0020]1817  owl_fmtext_cleanup(&fm);
[7d4fbcd]1818}
1819
[e7cc1c3]1820/* if type = 0 then normal reply.
1821 * if type = 1 then it's a reply to sender
1822 * if enter = 0 then allow the command to be edited
1823 * if enter = 1 then don't wait for editing
1824 */
[d54838d]1825void owl_function_reply(int type, int enter)
1826{
[740d5f7]1827  char *buff=NULL;
[c08c70a]1828  const owl_message *m;
[4542047]1829  const owl_filter *f;
[7d4fbcd]1830 
1831  if (owl_view_get_size(owl_global_get_current_view(&g))==0) {
[ec6ff52]1832    owl_function_error("No message selected");
[7d4fbcd]1833  } else {
[5ebc202]1834    char *cmd;
[7d4fbcd]1835   
1836    m=owl_view_get_element(owl_global_get_current_view(&g), owl_global_get_curmsg(&g));
[5eeea3b]1837    if (!m) {
[ec6ff52]1838      owl_function_error("No message selected");
[5eeea3b]1839      return;
1840    }
1841
[7d4fbcd]1842    /* first check if we catch the reply-lockout filter */
1843    f=owl_global_get_filter(&g, "reply-lockout");
1844    if (f) {
1845      if (owl_filter_message_match(f, m)) {
[ec6ff52]1846        owl_function_error("Sorry, replies to this message have been disabled by the reply-lockout filter");
[7d4fbcd]1847        return;
1848      }
1849    }
[4b464a4]1850
[2c09826]1851    /* then check if it's a question and just bring up the command prompt */
1852    if (owl_message_is_question(m)) {
1853      owl_function_start_command("");
1854      return;
1855    }
1856
[740d5f7]1857    if((type == 0 &&
1858        (cmd=owl_perlconfig_message_call_method(m, "replycmd", 0, NULL))) ||
1859       (type == 1 &&
1860        (cmd=owl_perlconfig_message_call_method(m, "replysendercmd", 0, NULL)))) {
1861      buff = cmd;
[d09e5a1]1862    }
[1b6b2f3]1863
[e0540e4]1864    if(!buff) {
1865        owl_function_error("I don't know how to reply to that message.");
1866        return;
1867    }
[740d5f7]1868
[d09e5a1]1869    if (enter) {
1870      owl_history *hist = owl_global_get_cmd_history(&g);
1871      owl_history_store(hist, buff);
1872      owl_history_reset(hist);
1873      owl_function_command_norv(buff);
1874    } else {
1875      owl_function_start_command(buff);
[7d4fbcd]1876    }
[ddbbcffa]1877    g_free(buff);
[7d4fbcd]1878  }
1879}
1880
[e19eb97]1881void owl_function_zlocate(int argc, const char *const *argv, int auth)
[d54838d]1882{
[2527615]1883  owl_fmtext fm;
[dca3b27]1884  char *ptr;
1885  char *result;
[2527615]1886  int i;
1887
1888  owl_fmtext_init_null(&fm);
[7d4fbcd]1889
[2527615]1890  for (i=0; i<argc; i++) {
[dca3b27]1891    ptr = long_zuser(argv[i]);
1892    result = owl_zephyr_zlocate(ptr, auth);
1893    owl_fmtext_append_normal(&fm, result);
[ddbbcffa]1894    g_free(result);
1895    g_free(ptr);
[7d4fbcd]1896  }
1897
[2527615]1898  owl_function_popless_fmtext(&fm);
[7ab0020]1899  owl_fmtext_cleanup(&fm);
[7d4fbcd]1900}
1901
[5934b87]1902void owl_callback_command(owl_editwin *e)
1903{
1904  char *rv;
1905  const char *line = owl_editwin_get_text(e);
1906
1907  rv = owl_function_command(line);
1908   if (rv) {
1909    owl_function_makemsg("%s", rv);
[ddbbcffa]1910    g_free(rv);
[5934b87]1911  }
1912}
1913
[e19eb97]1914void owl_function_start_command(const char *line)
[d54838d]1915{
[7d4fbcd]1916  owl_editwin *tw;
[c394de8]1917  owl_context *ctx;
[7d4fbcd]1918
[58d47ca]1919  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
[10b866d]1920
[7d4fbcd]1921  owl_editwin_set_locktext(tw, "command: ");
1922
[47519e1b]1923  owl_editwin_insert_string(tw, line);
[cf83b7a]1924
[4a41f16]1925  ctx = owl_editcontext_new(OWL_CTX_EDITLINE, tw, "editline",
1926                            owl_global_deactivate_editcontext, &g);
[c394de8]1927  owl_global_push_context_obj(&g, ctx);
[5934b87]1928  owl_editwin_set_callback(tw, owl_callback_command);
[cf83b7a]1929}
1930
[9186c75]1931owl_editwin *owl_function_start_question(const char *line)
[cf83b7a]1932{
1933  owl_editwin *tw;
[c394de8]1934  owl_context *ctx;
[cf83b7a]1935
[58d47ca]1936  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
[cf83b7a]1937
1938  owl_editwin_set_locktext(tw, line);
1939
[4a41f16]1940  ctx = owl_editcontext_new(OWL_CTX_EDITRESPONSE, tw, "editresponse",
1941                            owl_global_deactivate_editcontext, &g);
[c394de8]1942  owl_global_push_context_obj(&g, ctx);
[9186c75]1943  return tw;
[7d4fbcd]1944}
1945
[9186c75]1946owl_editwin *owl_function_start_password(const char *line)
[453bd70]1947{
1948  owl_editwin *tw;
[c394de8]1949  owl_context *ctx;
[453bd70]1950
[58d47ca]1951  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, NULL);
1952
[453bd70]1953  owl_editwin_set_echochar(tw, '*');
1954
1955  owl_editwin_set_locktext(tw, line);
1956
[4a41f16]1957  ctx = owl_editcontext_new(OWL_CTX_EDITRESPONSE, tw, "editresponse",
1958                            owl_global_deactivate_editcontext, &g);
[c394de8]1959  owl_global_push_context_obj(&g, ctx);
[9186c75]1960  return tw;
[453bd70]1961}
1962
[e19eb97]1963char *owl_function_exec(int argc, const char *const *argv, const char *buff, int type)
[d54838d]1964{
[7d4fbcd]1965  /* if type == 1 display in a popup
1966   * if type == 2 display an admin messages
1967   * if type == 0 return output
1968   * else display in a popup
1969   */
[e19eb97]1970  const char *redirect = " 2>&1 < /dev/null";
[65b2173]1971  char *newbuff;
[b7ee89b]1972  char *out;
[7d4fbcd]1973  FILE *p;
1974
[2a2bb60]1975#if OWL_STDERR_REDIR
1976  redirect = " < /dev/null";
1977#endif
1978
[7d4fbcd]1979  if (argc<2) {
[ec6ff52]1980    owl_function_error("Wrong number of arguments to the exec command");
[7d4fbcd]1981    return NULL;
1982  }
1983
1984  buff = skiptokens(buff, 1);
[1a30f05]1985  newbuff = g_strdup_printf("exec%s; %s", redirect, buff);
[7d4fbcd]1986
[7ba9e0de]1987  if (type == OWL_OUTPUT_POPUP) {
[afbf668]1988    owl_popexec_new(newbuff);
[7d4fbcd]1989  } else {
[b7ee89b]1990    p = popen(newbuff, "r");
1991    out = owl_slurp(p);
[afbf668]1992    pclose(p);
1993   
[2cfc6d7]1994    if (type == OWL_OUTPUT_RETURN) {
[ddbbcffa]1995      g_free(newbuff);
[afbf668]1996      return out;
[7ba9e0de]1997    } else if (type == OWL_OUTPUT_ADMINMSG) {
[afbf668]1998      owl_function_adminmsg(buff, out);
1999    }
[ddbbcffa]2000    g_free(out);
[7d4fbcd]2001  }
[ddbbcffa]2002  g_free(newbuff);
[7d4fbcd]2003  return NULL;
2004}
2005
[e19eb97]2006char *owl_function_perl(int argc, const char *const *argv, const char *buff, int type)
[d54838d]2007{
[7d4fbcd]2008  /* if type == 1 display in a popup
2009   * if type == 2 display an admin messages
2010   * if type == 0 return output
2011   * else display in a popup
2012   */
2013  char *perlout;
2014
2015  if (argc<2) {
[ec6ff52]2016    owl_function_error("Wrong number of arguments to perl command");
[7d4fbcd]2017    return NULL;
2018  }
2019
2020  /* consume first token (argv[0]) */
2021  buff = skiptokens(buff, 1);
2022
[f1e629d]2023  perlout = owl_perlconfig_execute(buff);
[7d4fbcd]2024  if (perlout) { 
[7ba9e0de]2025    if (type == OWL_OUTPUT_POPUP) {
[7d4fbcd]2026      owl_function_popless_text(perlout);
[7ba9e0de]2027    } else if (type == OWL_OUTPUT_ADMINMSG) {
[7d4fbcd]2028      owl_function_adminmsg(buff, perlout);
[7ba9e0de]2029    } else if (type == OWL_OUTPUT_RETURN) {
[7d4fbcd]2030      return perlout;
2031    }
[ddbbcffa]2032    g_free(perlout);
[7d4fbcd]2033  }
2034  return NULL;
2035}
2036
[5e0b690]2037/* Change the filter associated with the current view.
2038 * This also figures out which message in the new filter
2039 * should have the pointer.
2040 */
[e19eb97]2041void owl_function_change_currentview_filter(const char *filtname)
[c3ab155]2042{
2043  owl_view *v;
2044  owl_filter *f;
2045  int curid=-1, newpos, curmsg;
[c08c70a]2046  const owl_message *curm=NULL;
[c3ab155]2047
2048  v=owl_global_get_current_view(&g);
2049
2050  curmsg=owl_global_get_curmsg(&g);
2051  if (curmsg==-1) {
2052    owl_function_debugmsg("Hit the curmsg==-1 case in change_view");
2053  } else {
2054    curm=owl_view_get_element(v, curmsg);
2055    if (curm) {
2056      curid=owl_message_get_id(curm);
2057      owl_view_save_curmsgid(v, curid);
2058    }
2059  }
2060
2061  f=owl_global_get_filter(&g, filtname);
2062  if (!f) {
[ec6ff52]2063    owl_function_error("Unknown filter %s", filtname);
[c3ab155]2064    return;
2065  }
2066
2067  owl_view_new_filter(v, f);
2068
2069  /* Figure out what to set the current message to.
2070   * - If the view we're leaving has messages in it, go to the closest message
2071   *   to the last message pointed to in that view.
2072   * - If the view we're leaving is empty, try to restore the position
2073   *   from the last time we were in the new view.  */
2074  if (curm) {
2075    newpos = owl_view_get_nearest_to_msgid(v, curid);
2076  } else {
2077    newpos = owl_view_get_nearest_to_saved(v);
2078  }
2079
2080  owl_global_set_curmsg(&g, newpos);
2081  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
2082  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2083  owl_global_set_direction_downwards(&g);
2084}
[7d4fbcd]2085
[5e0b690]2086/* Create a new filter, or replace an existing one
2087 * with a new definition.
2088 */
[e19eb97]2089void owl_function_create_filter(int argc, const char *const *argv)
[d54838d]2090{
[7d4fbcd]2091  owl_filter *f;
[9e5c9f3]2092  const owl_view *v;
[23fddad]2093  int inuse = 0;
[7d4fbcd]2094
2095  if (argc < 2) {
[ec6ff52]2096    owl_function_error("Wrong number of arguments to filter command");
[7d4fbcd]2097    return;
2098  }
2099
[3895e23]2100  owl_function_debugmsg("owl_function_create_filter: starting to create filter named %s", argv[1]);
2101
[7d4fbcd]2102  v=owl_global_get_current_view(&g);
2103
2104  /* don't touch the all filter */
2105  if (!strcmp(argv[1], "all")) {
[ec6ff52]2106    owl_function_error("You may not change the 'all' filter.");
[7d4fbcd]2107    return;
2108  }
2109
2110  /* deal with the case of trying change the filter color */
2111  if (argc==4 && !strcmp(argv[2], "-c")) {
2112    f=owl_global_get_filter(&g, argv[1]);
2113    if (!f) {
[ec6ff52]2114      owl_function_error("The filter '%s' does not exist.", argv[1]);
[7d4fbcd]2115      return;
2116    }
[601733d]2117    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
[12c35df]2118      owl_function_error("The color '%s' is not available.", argv[3]);
2119      return;
2120    }
[8fa9562]2121    owl_filter_set_fgcolor(f, owl_util_string_to_color(argv[3]));
2122    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2123    return;
2124  }
2125  if (argc==4 && !strcmp(argv[2], "-b")) {
2126    f=owl_global_get_filter(&g, argv[1]);
2127    if (!f) {
2128      owl_function_error("The filter '%s' does not exist.", argv[1]);
2129      return;
2130    }
[601733d]2131    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
[8fa9562]2132      owl_function_error("The color '%s' is not available.", argv[3]);
2133      return;
2134    }
2135    owl_filter_set_bgcolor(f, owl_util_string_to_color(argv[3]));
[7d4fbcd]2136    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2137    return;
2138  }
2139
2140  /* create the filter and check for errors */
[23fddad]2141  f = owl_filter_new(argv[1], argc-2, argv+2);
2142  if (f == NULL) {
[40458b9]2143    owl_function_error("Invalid filter");
[7d4fbcd]2144    return;
2145  }
2146
2147  /* if the named filter is in use by the current view, remember it */
2148  if (!strcmp(owl_view_get_filtname(v), argv[1])) {
2149    inuse=1;
2150  }
2151
2152  /* if the named filter already exists, nuke it */
2153  if (owl_global_get_filter(&g, argv[1])) {
2154    owl_global_remove_filter(&g, argv[1]);
2155  }
2156
2157  /* add the filter */
2158  owl_global_add_filter(&g, f);
2159
2160  /* if it was in use by the current view then update */
2161  if (inuse) {
[3895e23]2162    owl_function_change_currentview_filter(argv[1]);
[7d4fbcd]2163  }
2164  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2165}
2166
[3895e23]2167/* If 'filtername' does not start with 'not-' create a filter named
2168 * 'not-<filtername>' defined as "not filter <filtername>".  If the
2169 * filter 'not-<filtername>' already exists, do not overwrite it.  If
2170 * 'filtername' begins with 'not-' and a filter 'filtername' already
2171 * exists, then do nothing.  If the filter 'filtername' does not
2172 * exist, create it and define it as 'not filter <filtername>'
2173 *
2174 * Returns the name of the negated filter, which the caller must free.
2175 */
[e19eb97]2176char *owl_function_create_negative_filter(const char *filtername)
[3895e23]2177{
2178  char *newname;
[4542047]2179  const owl_filter *tmpfilt;
[e19eb97]2180  const char *argv[5];
[3895e23]2181
2182  owl_function_debugmsg("owl_function_create_negative_filter");
2183 
2184  if (!strncmp(filtername, "not-", 4)) {
[d4927a7]2185    newname=g_strdup(filtername+4);
[3895e23]2186  } else {
[3472845]2187    newname=g_strdup_printf("not-%s", filtername);
[3895e23]2188  }
2189
2190  tmpfilt=owl_global_get_filter(&g, newname);
2191  if (!tmpfilt) {
2192    argv[0]="filter"; /* anything is fine here */
2193    argv[1]=newname;
2194    argv[2]="not";
2195    argv[3]="filter";
2196    argv[4]=filtername;
2197    owl_function_create_filter(5, argv);
2198  }
2199
2200  owl_function_debugmsg("owl_function_create_negative_filter: returning with %s", newname);
2201  return(newname);
2202}
2203
[c79a047]2204void owl_function_show_filters(void)
[d54838d]2205{
[4542047]2206  const owl_filter *f;
[129e609]2207  GList *fl;
[7d4fbcd]2208  owl_fmtext fm;
2209
2210  owl_fmtext_init_null(&fm);
2211
2212  owl_fmtext_append_bold(&fm, "Filters:\n");
2213
[129e609]2214  for (fl = g.filterlist; fl; fl = g_list_next(fl)) {
2215    f = fl->data;
[7d4fbcd]2216    owl_fmtext_append_normal(&fm, "   ");
[7b4f3be]2217    owl_fmtext_append_normal_color(&fm, owl_filter_get_name(f),
2218                                   owl_filter_get_fgcolor(f),
2219                                   owl_filter_get_bgcolor(f));
[7d4fbcd]2220    owl_fmtext_append_normal(&fm, "\n");
2221  }
2222  owl_function_popless_fmtext(&fm);
[7ab0020]2223  owl_fmtext_cleanup(&fm);
[7d4fbcd]2224}
2225
[e19eb97]2226void owl_function_show_filter(const char *name)
[d54838d]2227{
[4542047]2228  const owl_filter *f;
[cdc6ff1]2229  char *buff, *tmp;
[7d4fbcd]2230
2231  f=owl_global_get_filter(&g, name);
2232  if (!f) {
[ec6ff52]2233    owl_function_error("There is no filter named %s", name);
[7d4fbcd]2234    return;
2235  }
[cdc6ff1]2236  tmp = owl_filter_print(f);
[3472845]2237  buff = g_strdup_printf("%s: %s", owl_filter_get_name(f), tmp);
[7d4fbcd]2238  owl_function_popless_text(buff);
[ddbbcffa]2239  g_free(buff);
2240  g_free(tmp);
[7d4fbcd]2241}
2242
[c79a047]2243void owl_function_show_zpunts(void)
[d54838d]2244{
[4542047]2245  const owl_filter *f;
[77bced3]2246  const owl_list *fl;
[0504f63]2247  char *tmp;
[7d4fbcd]2248  owl_fmtext fm;
2249  int i, j;
2250
2251  owl_fmtext_init_null(&fm);
2252
2253  fl=owl_global_get_puntlist(&g);
2254  j=owl_list_get_size(fl);
2255  owl_fmtext_append_bold(&fm, "Active zpunt filters:\n");
2256
2257  for (i=0; i<j; i++) {
2258    f=owl_list_get_element(fl, i);
[e3a75ed]2259    owl_fmtext_appendf_normal(&fm, "[% 2d] ", i+1);
[0504f63]2260    tmp = owl_filter_print(f);
2261    owl_fmtext_append_normal(&fm, tmp);
[ddbbcffa]2262    g_free(tmp);
[7d4fbcd]2263  }
2264  owl_function_popless_fmtext(&fm);
[7ab0020]2265  owl_fmtext_cleanup(&fm);
[7d4fbcd]2266}
2267
[3abf28b]2268/* Create a filter for a class, instance if one doesn't exist.  If
2269 * instance is NULL then catch all messgaes in the class.  Returns the
[c7fe23e]2270 * name of the filter or null.  The caller must free this name.
[66e409c]2271 * If 'related' is nonzero, encompass unclasses and .d classes as well.
[3abf28b]2272 */
[66e409c]2273char *owl_function_classinstfilt(const char *c, const char *i, int related) 
[d54838d]2274{
[7d4fbcd]2275  owl_filter *f;
[c426bc2]2276  char *filtname;
[d54838d]2277  char *tmpclass, *tmpinstance = NULL;
[7a20e4c]2278  char *class, *instance = NULL;
[c426bc2]2279  GString *buf;
[7d4fbcd]2280
[66e409c]2281  if (related) {
2282    class = owl_util_baseclass(c);
2283    if (i) {
2284      instance = owl_util_baseclass(i);
2285    }
2286  } else {
[d4927a7]2287    class = g_strdup(c);
[66e409c]2288    if (i) {
[d4927a7]2289      instance = g_strdup(i);
[66e409c]2290    }
[7a20e4c]2291  }
2292
[7d4fbcd]2293  /* name for the filter */
2294  if (!instance) {
[3472845]2295    filtname = g_strdup_printf("%sclass-%s", related ? "related-" : "", class);
[7d4fbcd]2296  } else {
[3472845]2297    filtname = g_strdup_printf("%sclass-%s-instance-%s", related ? "related-" : "", class, instance);
[7d4fbcd]2298  }
[ed2412d]2299  /* downcase it */
[28ee32b]2300  {
2301    char *temp = g_utf8_strdown(filtname, -1);
2302    if (temp) {
[ddbbcffa]2303      g_free(filtname);
[28ee32b]2304      filtname = temp;
2305    }
2306  }
[ed2412d]2307 
[7d4fbcd]2308  /* if it already exists then go with it.  This lets users override */
2309  if (owl_global_get_filter(&g, filtname)) {
[ff426f9]2310    goto done;
[7d4fbcd]2311  }
2312
2313  /* create the new filter */
[995eb4b]2314  tmpclass=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
[ed2412d]2315  if (instance) {
[995eb4b]2316    tmpinstance=owl_text_quote(instance, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
[ed2412d]2317  }
[a0e6082]2318
[c426bc2]2319  buf = g_string_new("");
2320  owl_string_appendf_quoted(buf,
2321                            related ? "class ^(un)*%q(\\.d)*$" : "class ^%q$",
2322                            tmpclass);
2323
[d54838d]2324  if (tmpinstance) {
[c426bc2]2325    owl_string_appendf_quoted(buf,
2326                              related ?
2327                              " and ( instance ^(un)*%q(\\.d)*$ )" :
2328                              " and instance ^%q$",
2329                              tmpinstance);
[7d4fbcd]2330  }
[ddbbcffa]2331  g_free(tmpclass);
2332  g_free(tmpinstance);
[7d4fbcd]2333
[c426bc2]2334  f = owl_filter_new_fromstring(filtname, buf->str);
2335  g_string_free(buf, true);
[c7fe23e]2336  if (f == NULL) {
2337    /* Couldn't make a filter for some reason. Return NULL. */
2338    owl_function_error("Error creating filter '%s'", filtname);
[ddbbcffa]2339    g_free(filtname);
[c7fe23e]2340    filtname = NULL;
2341    goto done;
2342  }
[7d4fbcd]2343
2344  /* add it to the global list */
2345  owl_global_add_filter(&g, f);
2346
[ff426f9]2347done:
[ddbbcffa]2348  g_free(class);
[3b8a563]2349  g_free(instance);
[ed2412d]2350  return(filtname);
[7d4fbcd]2351}
2352
[3abf28b]2353/* Create a filter for personal zephyrs to or from the specified
2354 * zephyr user.  Includes login/logout notifications for the user.
[811644f]2355 * The name of the filter will be 'user-<shortuser>'.  If a filter already
[3abf28b]2356 * exists with this name, no new filter will be created.  This allows
2357 * the configuration to override this function.  Returns the name of
2358 * the filter, which the caller must free.
2359 */
[811644f]2360char *owl_function_zuserfilt(const char *longuser)
[d54838d]2361{
[7d4fbcd]2362  owl_filter *f;
[811644f]2363  char *argbuff, *esclonguser, *shortuser, *filtname;
[7d4fbcd]2364
2365  /* name for the filter */
[811644f]2366  shortuser = short_zuser(longuser);
[3472845]2367  filtname = g_strdup_printf("user-%s", shortuser);
[ddbbcffa]2368  g_free(shortuser);
[7d4fbcd]2369
2370  /* if it already exists then go with it.  This lets users override */
2371  if (owl_global_get_filter(&g, filtname)) {
[6cc3306]2372    return filtname;
[7d4fbcd]2373  }
2374
2375  /* create the new-internal filter */
[1d12db24]2376  esclonguser = owl_text_quote(longuser, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2377
[a5f477c]2378  argbuff=owl_string_build_quoted("( type ^zephyr$ and filter personal and "
2379      "( ( direction ^in$ and sender ^%q$ ) or ( direction ^out$ and "
2380      "recipient ^%q$ ) ) ) or ( ( class ^login$ ) and ( sender ^%q$ ) )",
2381      esclonguser, esclonguser, esclonguser);
[ddbbcffa]2382  g_free(esclonguser);
[7d4fbcd]2383
[23fddad]2384  f = owl_filter_new_fromstring(filtname, argbuff);
[ddbbcffa]2385  g_free(argbuff);
[c7fe23e]2386
2387  if (f == NULL) {
2388    /* Couldn't make a filter for some reason. Return NULL. */
2389    owl_function_error("Error creating filter '%s'", filtname);
[ddbbcffa]2390    g_free(filtname);
[c7fe23e]2391    return NULL;
2392  }
[7d4fbcd]2393
2394  /* add it to the global list */
2395  owl_global_add_filter(&g, f);
2396
[ed2412d]2397  return(filtname);
[7d4fbcd]2398}
2399
[3abf28b]2400/* Create a filter for AIM IM messages to or from the specified
2401 * screenname.  The name of the filter will be 'aimuser-<user>'.  If a
2402 * filter already exists with this name, no new filter will be
2403 * created.  This allows the configuration to override this function.
2404 * Returns the name of the filter, which the caller must free.
2405 */
[e19eb97]2406char *owl_function_aimuserfilt(const char *user)
[3abf28b]2407{
2408  owl_filter *f;
2409  char *argbuff, *filtname;
[af9b92e]2410  char *escuser;
[3abf28b]2411
2412  /* name for the filter */
[3472845]2413  filtname=g_strdup_printf("aimuser-%s", user);
[3abf28b]2414
2415  /* if it already exists then go with it.  This lets users override */
2416  if (owl_global_get_filter(&g, filtname)) {
[d4927a7]2417    return(g_strdup(filtname));
[3abf28b]2418  }
2419
2420  /* create the new-internal filter */
[af9b92e]2421  escuser = owl_text_quote(user, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2422
[3472845]2423  argbuff = g_strdup_printf(
[9a2ae6c]2424      "( type ^aim$ and ( ( sender ^%1$s$ and recipient ^%2$s$ ) or "
2425      "( sender ^%2$s$ and recipient ^%1$s$ ) ) )",
2426      escuser, owl_global_get_aim_screenname_for_filters(&g));
[ddbbcffa]2427  g_free(escuser);
[3abf28b]2428
[23fddad]2429  f = owl_filter_new_fromstring(filtname, argbuff);
[ddbbcffa]2430  g_free(argbuff);
[c7fe23e]2431
2432  if (f == NULL) {
2433    owl_function_error("Error creating filter '%s'", filtname);
[ddbbcffa]2434    g_free(filtname);
[c7fe23e]2435    return NULL;
2436  }
[3abf28b]2437
2438  /* add it to the global list */
2439  owl_global_add_filter(&g, f);
2440
2441  return(filtname);
2442}
2443
[e19eb97]2444char *owl_function_typefilt(const char *type)
[d54838d]2445{
[f73e519]2446  owl_filter *f;
[1d12db24]2447  char *argbuff, *filtname, *esctype;
[f73e519]2448
2449  /* name for the filter */
[3472845]2450  filtname=g_strdup_printf("type-%s", type);
[f73e519]2451
2452  /* if it already exists then go with it.  This lets users override */
2453  if (owl_global_get_filter(&g, filtname)) {
2454    return filtname;
2455  }
2456
2457  /* create the new-internal filter */
[1d12db24]2458  esctype = owl_text_quote(type, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2459
[42115bf9]2460  argbuff = owl_string_build_quoted("type ^%q$", esctype);
[ddbbcffa]2461  g_free(esctype);
[f73e519]2462
[23fddad]2463  f = owl_filter_new_fromstring(filtname, argbuff);
[ddbbcffa]2464  g_free(argbuff);
[c7fe23e]2465
2466  if (f == NULL) {
2467    owl_function_error("Error creating filter '%s'", filtname);
[ddbbcffa]2468    g_free(filtname);
[c7fe23e]2469    return NULL;
2470  }
[f73e519]2471
2472  /* add it to the global list */
2473  owl_global_add_filter(&g, f);
2474
2475  return filtname;
2476}
2477
[7d4fbcd]2478/* If flag is 1, marks for deletion.  If flag is 0,
2479 * unmarks for deletion. */
[d54838d]2480void owl_function_delete_curview_msgs(int flag)
2481{
[9e5c9f3]2482  const owl_view *v;
[7d4fbcd]2483  int i, j;
2484
2485  v=owl_global_get_current_view(&g);
2486  j=owl_view_get_size(v);
2487  for (i=0; i<j; i++) {
2488    if (flag == 1) {
2489      owl_message_mark_delete(owl_view_get_element(v, i));
2490    } else if (flag == 0) {
2491      owl_message_unmark_delete(owl_view_get_element(v, i));
2492    }
2493  }
2494
2495  owl_function_makemsg("%i messages marked for %sdeletion", j, flag?"":"un");
2496
2497  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
2498}
2499
[ecaec21]2500static char *owl_function_smartfilter_cc(const owl_message *m) {
2501  const char *ccs;
[d222c44]2502  char *ccs_quoted;
[ecaec21]2503  char *filtname;
2504  owl_filter *f;
[d222c44]2505  GString *buf;
[ecaec21]2506
2507  ccs = owl_message_get_attribute_value(m, "zephyr_ccs");
2508
[3472845]2509  filtname = g_strdup_printf("conversation-%s", ccs);
[7865479]2510  g_strdelimit(filtname, " ", '-');
[ecaec21]2511
2512  if (owl_global_get_filter(&g, filtname)) {
2513    return filtname;
2514  }
2515
[d222c44]2516  buf = g_string_new("type ^zephyr$ and filter personal and "
2517                     "zephyr_ccs ^");
2518  ccs_quoted = owl_text_quote(ccs, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2519  owl_string_append_quoted_arg(buf, ccs_quoted);
2520  g_string_append_c(buf, '$');
[ddbbcffa]2521  g_free(ccs_quoted);
[ecaec21]2522
[d222c44]2523  f = owl_filter_new_fromstring(filtname, buf->str);
2524  g_string_free(buf, true);
[ecaec21]2525
[c7fe23e]2526  if (f == NULL) {
2527    owl_function_error("Error creating filter '%s'", filtname);
[ddbbcffa]2528    g_free(filtname);
[c7fe23e]2529    return NULL;
2530  }
2531
[ecaec21]2532  owl_global_add_filter(&g, f);
2533
2534  return filtname;
2535}
2536
[3abf28b]2537/* Create a filter based on the current message.  Returns the name of
2538 * a filter or null.  The caller must free this name.
2539 *
2540 * if the curmsg is a personal zephyr return a filter name
[e6d989f]2541 *    to the zephyr conversation with that user.
[3abf28b]2542 * If the curmsg is a zephyr class message, instance foo, recip *,
2543 *    return a filter name to the class, inst.
2544 * If the curmsg is a zephyr class message and type==0 then
2545 *    return a filter name for just the class.
2546 * If the curmsg is a zephyr class message and type==1 then
2547 *    return a filter name for the class and instance.
2548 * If the curmsg is a personal AIM message returna  filter
2549 *    name to the AIM conversation with that user
2550 */
[8a5b5a1]2551char *owl_function_smartfilter(int type, int invert_related)
[d54838d]2552{
[9e5c9f3]2553  const owl_view *v;
[c08c70a]2554  const owl_message *m;
[811644f]2555  char *filtname = NULL;
2556  const char *argv[2], *zperson;
[8a5b5a1]2557  int related = owl_global_is_narrow_related(&g) ^ invert_related;
2558
[7d4fbcd]2559  v=owl_global_get_current_view(&g);
2560  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2561
[5eeea3b]2562  if (!m || owl_view_get_size(v)==0) {
[ec6ff52]2563    owl_function_error("No message selected\n");
[4b464a4]2564    return(NULL);
[7d4fbcd]2565  }
2566
[f73e519]2567  /* very simple handling of admin messages for now */
[4b464a4]2568  if (owl_message_is_type_admin(m)) {
[3abf28b]2569    return(owl_function_typefilt("admin"));
2570  }
2571
[995eb4b]2572  /* very simple handling of loopback messages for now */
2573  if (owl_message_is_type_loopback(m)) {
2574    return(owl_function_typefilt("loopback"));
2575  }
2576
[3abf28b]2577  /* aim messages */
2578  if (owl_message_is_type_aim(m)) {
2579    if (owl_message_is_direction_in(m)) {
2580      filtname=owl_function_aimuserfilt(owl_message_get_sender(m));
2581    } else if (owl_message_is_direction_out(m)) {
2582      filtname=owl_function_aimuserfilt(owl_message_get_recipient(m));
2583    }
2584    return(filtname);
[7d4fbcd]2585  }
2586
[4b464a4]2587  /* narrow personal and login messages to the sender or recip as appropriate */
[25729b2]2588  if (owl_message_is_type_zephyr(m)) {
[0ef0e8f]2589    if (owl_message_is_personal(m) || owl_message_is_loginout(m)) {
[ecaec21]2590      if (owl_message_get_attribute_value(m, "zephyr_ccs") != NULL) {
2591        return owl_function_smartfilter_cc(m);
2592      }
2593
[4b464a4]2594      if (owl_message_is_direction_in(m)) {
[811644f]2595        zperson = owl_message_get_sender(m);
[4b464a4]2596      } else {
[811644f]2597        zperson = owl_message_get_recipient(m);
[4b464a4]2598      }
[811644f]2599      filtname = owl_function_zuserfilt(zperson);
2600      return filtname;
[7d4fbcd]2601    }
2602
[25729b2]2603    /* narrow class MESSAGE, instance foo, recip * messages to class, inst */
[ce74deb]2604    if (!strcasecmp(owl_message_get_class(m), "message")) {
[66e409c]2605      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
[25729b2]2606      return(filtname);
2607    }
2608
2609    /* otherwise narrow to the class */
2610    if (type==0) {
[66e409c]2611      filtname=owl_function_classinstfilt(owl_message_get_class(m), NULL, related);
[25729b2]2612    } else if (type==1) {
[66e409c]2613      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
[25729b2]2614    }
[4b464a4]2615    return(filtname);
[7d4fbcd]2616  }
2617
[25729b2]2618  /* pass it off to perl */
[66e409c]2619  argv[0] = type ? "1" : "0";
2620  argv[1] = related ? "1" : "0";
2621  return owl_perlconfig_message_call_method(m, "smartfilter", 2, argv);
[7d4fbcd]2622}
2623
[d54838d]2624void owl_function_smartzpunt(int type)
2625{
[d36f2cb]2626  /* Starts a zpunt command based on the current class,instance pair.
2627   * If type=0, uses just class.  If type=1, uses instance as well. */
[9e5c9f3]2628  const owl_view *v;
[c08c70a]2629  const owl_message *m;
[d222c44]2630  const char *mclass, *minst;
2631  GString *buf;
[d36f2cb]2632 
2633  v=owl_global_get_current_view(&g);
2634  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2635
[5eeea3b]2636  if (!m || owl_view_get_size(v)==0) {
[ec6ff52]2637    owl_function_error("No message selected\n");
[d36f2cb]2638    return;
2639  }
2640
2641  /* for now we skip admin messages. */
[4b464a4]2642  if (owl_message_is_type_admin(m)
[5789230]2643      || owl_message_is_loginout(m)
[4b464a4]2644      || !owl_message_is_type_zephyr(m)) {
[ec6ff52]2645    owl_function_error("smartzpunt doesn't support this message type.");
[d36f2cb]2646    return;
2647  }
2648
[cee1f25]2649  mclass = owl_message_get_class(m);
2650  minst = owl_message_get_instance(m);
[d36f2cb]2651  if (!mclass || !*mclass || *mclass==' '
2652      || (!strcasecmp(mclass, "message") && !strcasecmp(minst, "personal"))
2653      || (type && (!minst || !*minst|| *minst==' '))) {
[ec6ff52]2654    owl_function_error("smartzpunt can't safely do this for <%s,%s>",
[d36f2cb]2655                         mclass, minst);
2656  } else {
[d222c44]2657    buf = g_string_new("start-command zpunt ");
2658    owl_string_append_quoted_arg(buf, mclass);
[d36f2cb]2659    if (type) {
[d222c44]2660      g_string_append_c(buf, ' ');
2661      owl_string_append_quoted_arg(buf, minst);
[d36f2cb]2662    } else {
[d222c44]2663      g_string_append(buf, " *");
[d36f2cb]2664    }
[c809f5e]2665    owl_function_command_norv(buf->str);
[d222c44]2666    g_string_free(buf, true);
[d36f2cb]2667  }
2668}
2669
[5e0b690]2670/* Set the color of the current view's filter to
2671 * be 'color'
2672 */
[e19eb97]2673void owl_function_color_current_filter(const char *fgcolor, const char *bgcolor)
[d54838d]2674{
[e19eb97]2675  const char *name;
[7d4fbcd]2676
2677  name=owl_view_get_filtname(owl_global_get_current_view(&g));
[8fa9562]2678  owl_function_color_filter(name, fgcolor, bgcolor);
[5e0b690]2679}
2680
2681/* Set the color of the filter 'filter' to be 'color'.  If the color
2682 * name does not exist, return -1, if the filter does not exist or is
2683 * the "all" filter, return -2.  Return 0 on success
2684 */
[e19eb97]2685int owl_function_color_filter(const char *filtname, const char *fgcolor, const char *bgcolor)
[5e0b690]2686{
2687  owl_filter *f;
2688
2689  f=owl_global_get_filter(&g, filtname);
[7d4fbcd]2690  if (!f) {
[ec6ff52]2691    owl_function_error("Unknown filter");
[5e0b690]2692    return(-2);
[7d4fbcd]2693  }
2694
2695  /* don't touch the all filter */
[5e0b690]2696  if (!strcmp(filtname, "all")) {
[ec6ff52]2697    owl_function_error("You may not change the 'all' filter.");
[5e0b690]2698    return(-2);
[7d4fbcd]2699  }
2700
[601733d]2701  if (owl_util_string_to_color(fgcolor)==OWL_COLOR_INVALID) {
[8fa9562]2702    owl_function_error("No color named '%s' avilable.", fgcolor);
[5e0b690]2703    return(-1);
[12c35df]2704  }
[8fa9562]2705
2706
2707  if (bgcolor != NULL) {
[601733d]2708    if (owl_util_string_to_color(bgcolor)==OWL_COLOR_INVALID) {
[8fa9562]2709      owl_function_error("No color named '%s' avilable.", bgcolor);
2710      return(-1);
2711    }
2712    owl_filter_set_bgcolor(f, owl_util_string_to_color(bgcolor));
2713  }
2714  owl_filter_set_fgcolor(f, owl_util_string_to_color(fgcolor));
2715 
[7d4fbcd]2716  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
[5e0b690]2717  return(0);
[7d4fbcd]2718}
2719
[c79a047]2720void owl_function_show_colors(void)
[d54838d]2721{
[7d4fbcd]2722  owl_fmtext fm;
[c2c5c77]2723  int i; 
2724 
[7d4fbcd]2725  owl_fmtext_init_null(&fm);
[82b734a]2726  owl_fmtext_append_normal(&fm,"default:  ");
[8fa9562]2727  owl_fmtext_append_normal_color(&fm, "default\n", OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
[ca9142e]2728
[82b734a]2729  owl_fmtext_append_normal(&fm,"black:    ");
2730  owl_fmtext_append_normal_color(&fm, "black\n", OWL_COLOR_BLACK, OWL_COLOR_DEFAULT);
2731
[ca9142e]2732  owl_fmtext_append_normal(&fm,"red:      ");
[8fa9562]2733  owl_fmtext_append_normal_color(&fm, "red\n", OWL_COLOR_RED, OWL_COLOR_DEFAULT);
[ca9142e]2734
2735  owl_fmtext_append_normal(&fm,"green:    ");
[8fa9562]2736  owl_fmtext_append_normal_color(&fm, "green\n", OWL_COLOR_GREEN, OWL_COLOR_DEFAULT);
[ca9142e]2737
2738  owl_fmtext_append_normal(&fm,"yellow:   ");
[8fa9562]2739  owl_fmtext_append_normal_color(&fm, "yellow\n", OWL_COLOR_YELLOW, OWL_COLOR_DEFAULT);
[ca9142e]2740
2741  owl_fmtext_append_normal(&fm,"blue:     ");
[8fa9562]2742  owl_fmtext_append_normal_color(&fm, "blue\n", OWL_COLOR_BLUE, OWL_COLOR_DEFAULT);
[ca9142e]2743
2744  owl_fmtext_append_normal(&fm,"magenta:  ");
[8fa9562]2745  owl_fmtext_append_normal_color(&fm, "magenta\n", OWL_COLOR_MAGENTA, OWL_COLOR_DEFAULT);
[ca9142e]2746
2747  owl_fmtext_append_normal(&fm,"cyan:     ");
[8fa9562]2748  owl_fmtext_append_normal_color(&fm, "cyan\n", OWL_COLOR_CYAN, OWL_COLOR_DEFAULT);
[ca9142e]2749
2750  owl_fmtext_append_normal(&fm,"white:    ");
[8fa9562]2751  owl_fmtext_append_normal_color(&fm, "white\n", OWL_COLOR_WHITE, OWL_COLOR_DEFAULT);
[7d4fbcd]2752
[c2c5c77]2753  for(i = 8; i < COLORS; ++i) {
[3472845]2754    char* str1 = g_strdup_printf("%4i:     ",i);
2755    char* str2 = g_strdup_printf("%i\n",i);
[c2c5c77]2756    owl_fmtext_append_normal(&fm,str1);
2757    owl_fmtext_append_normal_color(&fm, str2, i, OWL_COLOR_DEFAULT);
[ddbbcffa]2758    g_free(str1);
2759     g_free(str2);
[c2c5c77]2760  }
2761 
[7d4fbcd]2762  owl_function_popless_fmtext(&fm);
[7ab0020]2763  owl_fmtext_cleanup(&fm);
[7d4fbcd]2764}
2765
[5bb6c21]2766/* add the given class, inst, recip to the punt list for filtering.
2767 *   if direction==0 then punt
2768 *   if direction==1 then unpunt
2769 */
[e19eb97]2770void owl_function_zpunt(const char *class, const char *inst, const char *recip, int direction)
[d54838d]2771{
[78f6c35]2772  GPtrArray *argv;
[bc08664]2773  char *quoted;
[7d4fbcd]2774
[78f6c35]2775  argv = g_ptr_array_new();
[5bb6c21]2776  if (!strcmp(class, "*")) {
[78f6c35]2777    g_ptr_array_add(argv, g_strdup("class"));
2778    g_ptr_array_add(argv, g_strdup(".*"));
[5bb6c21]2779  } else {
[bc08664]2780    quoted=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
[78f6c35]2781    g_ptr_array_add(argv, g_strdup("class"));
2782    g_ptr_array_add(argv, g_strdup_printf("^(un)*%s(\\.d)*$", quoted));
[ddbbcffa]2783    g_free(quoted);
[5bb6c21]2784  }
2785  if (!strcmp(inst, "*")) {
[78f6c35]2786    g_ptr_array_add(argv, g_strdup("and"));
2787    g_ptr_array_add(argv, g_strdup("instance"));
2788    g_ptr_array_add(argv, g_strdup(".*"));
[7d4fbcd]2789  } else {
[bc08664]2790    quoted=owl_text_quote(inst, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
[78f6c35]2791    g_ptr_array_add(argv, g_strdup("and"));
2792    g_ptr_array_add(argv, g_strdup("instance"));
2793    g_ptr_array_add(argv, g_strdup_printf("^(un)*%s(\\.d)*$", quoted));
[ddbbcffa]2794    g_free(quoted);
[7d4fbcd]2795  }
[c894c15]2796  if (!strcmp(recip, "*")) {
[78f6c35]2797    /* nothing */
[c894c15]2798  } else {
[e6c59ba]2799    if(!strcmp(recip, "%me%")) {
2800      recip = owl_zephyr_get_sender();
2801    }
[bc08664]2802    quoted=owl_text_quote(recip, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
[78f6c35]2803    g_ptr_array_add(argv, g_strdup("and"));
2804    g_ptr_array_add(argv, g_strdup("recipient"));
2805    g_ptr_array_add(argv, g_strdup_printf("^%s$", quoted));
[ddbbcffa]2806    g_free(quoted);
[5bb6c21]2807  }
[ce7b824]2808
[78f6c35]2809  owl_function_punt(argv->len, (const char *const*) argv->pdata, direction);
2810  g_ptr_array_foreach(argv, (GFunc)g_free, NULL);
2811  g_ptr_array_free(argv, true);
[ce7b824]2812}
2813
[78f6c35]2814void owl_function_punt(int argc, const char *const *argv, int direction)
[ce7b824]2815{
2816  owl_filter *f;
2817  owl_list *fl;
[23fddad]2818  int i, j;
[ce7b824]2819  fl=owl_global_get_puntlist(&g);
2820
2821  /* first, create the filter */
[78f6c35]2822  f = owl_filter_new("punt-filter", argc, argv);
[23fddad]2823  if (f == NULL) {
[ec6ff52]2824    owl_function_error("Error creating filter for zpunt");
[7d4fbcd]2825    return;
2826  }
2827
2828  /* Check for an identical filter */
2829  j=owl_list_get_size(fl);
2830  for (i=0; i<j; i++) {
2831    if (owl_filter_equiv(f, owl_list_get_element(fl, i))) {
[ce7b824]2832      owl_function_debugmsg("found an equivalent punt filter");
[7d4fbcd]2833      /* if we're punting, then just silently bow out on this duplicate */
2834      if (direction==0) {
[23fddad]2835        owl_filter_delete(f);
[7d4fbcd]2836        return;
2837      }
2838
2839      /* if we're unpunting, then remove this filter from the puntlist */
2840      if (direction==1) {
[23fddad]2841        owl_filter_delete(owl_list_get_element(fl, i));
[7d4fbcd]2842        owl_list_remove_element(fl, i);
[23fddad]2843        owl_filter_delete(f);
[7d4fbcd]2844        return;
2845      }
2846    }
2847  }
2848
[697221f]2849  if (direction == 0) {
2850    owl_function_debugmsg("punting");
2851    /* If we're punting, add the filter to the global punt list */
[7d4fbcd]2852    owl_list_append_element(fl, f);
[697221f]2853  } else if (direction == 1) {
2854    owl_function_makemsg("No matching punt filter");
2855 }
[7d4fbcd]2856}
2857
[c79a047]2858void owl_function_show_keymaps(void)
[d54838d]2859{
[7d4fbcd]2860  owl_list l;
2861  owl_fmtext fm;
[afa200a]2862  const owl_keymap *km;
[12bc46a]2863  const owl_keyhandler *kh;
[1aee7d9]2864  int i, numkm;
[e19eb97]2865  const char *kmname;
[7d4fbcd]2866
[1aee7d9]2867  kh = owl_global_get_keyhandler(&g);
[7d4fbcd]2868  owl_fmtext_init_null(&fm);
2869  owl_fmtext_append_bold(&fm, "Keymaps:   ");
2870  owl_fmtext_append_normal(&fm, "(use 'show keymap <name>' for details)\n");
[f25df21]2871  owl_list_create(&l);
[1aee7d9]2872  owl_keyhandler_get_keymap_names(kh, &l);
[7d4fbcd]2873  owl_fmtext_append_list(&fm, &l, "\n", owl_function_keymap_summary);
2874  owl_fmtext_append_normal(&fm, "\n");
[1aee7d9]2875
2876  numkm = owl_list_get_size(&l);
2877  for (i=0; i<numkm; i++) {
2878    kmname = owl_list_get_element(&l, i);
2879    km = owl_keyhandler_get_keymap(kh, kmname);
2880    owl_fmtext_append_bold(&fm, "\n\n----------------------------------------------------------------------------------------------------\n\n");
[13ebf92]2881    owl_keymap_get_details(km, &fm, 0);
[1aee7d9]2882  }
2883  owl_fmtext_append_normal(&fm, "\n");
2884 
[7d4fbcd]2885  owl_function_popless_fmtext(&fm);
[f25df21]2886  owl_list_cleanup(&l, g_free);
[7ab0020]2887  owl_fmtext_cleanup(&fm);
[7d4fbcd]2888}
2889
[e19eb97]2890char *owl_function_keymap_summary(const char *name)
[d54838d]2891{
[afa200a]2892  const owl_keymap *km
[7d4fbcd]2893    = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2894  if (km) return owl_keymap_summary(km);