source: functions.c @ 219f52c

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