source: functions.c @ b61ad80

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