source: functions.c @ 24bd860

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