source: functions.c @ 0071c88

Last change on this file since 0071c88 was 0071c88, checked in by David Benjamin <davidben@mit.edu>, 12 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.2 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 */
206  if (owl_global_should_followlast(&g)) owl_function_lastmsg_noredisplay();
[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
[c79a047]810void owl_function_lastmsg_noredisplay(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  } 
[6b580b0]828  /* owl_mainwin_redisplay(owl_global_get_mainwin(&g)); */
[7d4fbcd]829  owl_global_set_direction_downwards(&g);
830}
831
[c79a047]832void owl_function_lastmsg(void)
[d54838d]833{
[7d4fbcd]834  owl_function_lastmsg_noredisplay();
835  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
836}
837
[c79a047]838void owl_function_shift_right(void)
[d54838d]839{
[7d4fbcd]840  owl_global_set_rightshift(&g, owl_global_get_rightshift(&g)+10);
841}
842
[c79a047]843void owl_function_shift_left(void)
[d54838d]844{
[7d4fbcd]845  int shift;
846
847  shift=owl_global_get_rightshift(&g);
[8c97fa1]848  if (shift > 0) {
849    owl_global_set_rightshift(&g, MAX(shift - 10, 0));
[7d4fbcd]850  } else {
851    owl_function_beep();
[f51bc78]852    owl_function_makemsg("Already full left");
[7d4fbcd]853  }
854}
855
[c79a047]856void owl_function_unsuball(void)
[d54838d]857{
[7d4fbcd]858  unsuball();
859  owl_function_makemsg("Unsubscribed from all messages.");
860}
861
[95474d7]862
863/* Load zephyr subscriptions from the named 'file' and load zephyr's
864 * default subscriptions as well.  An error message is printed if
865 * 'file' can't be opened or if zephyr reports an error in
866 * subscribing.
867 *
868 * If 'file' is NULL, this look for the default filename
869 * $HOME/.zephyr.subs.  If the file can not be opened in this case
870 * only, no error message is printed.
871 */
[e19eb97]872void owl_function_loadsubs(const char *file)
[d54838d]873{
[4357be8]874  int ret, ret2;
[e19eb97]875  const char *foo;
[65b2173]876  char *path;
[ecd5dc5]877
[95474d7]878  if (file==NULL) {
879    ret=owl_zephyr_loadsubs(NULL, 0);
880  } else {
[27d8d83]881    path = owl_util_makepath(file);
882    ret=owl_zephyr_loadsubs(path, 1);
[ddbbcffa]883    g_free(path);
[95474d7]884  }
[ecd5dc5]885
[4357be8]886  /* for backwards compatibility for now */
887  ret2=owl_zephyr_loaddefaultsubs();
888
[ecd5dc5]889  if (!owl_context_is_interactive(owl_global_get_context(&g))) return;
[95474d7]890
891  foo=file?file:"file";
892  if (ret==0 && ret2==0) {
893    if (!file) {
894      owl_function_makemsg("Subscribed to messages.");
895    } else {
896      owl_function_makemsg("Subscribed to messages from %s", file);
897    }
[7d4fbcd]898  } else if (ret==-1) {
[95474d7]899    owl_function_error("Could not read %s", foo);
[7d4fbcd]900  } else {
[95474d7]901    owl_function_error("Error subscribing to messages");
[7d4fbcd]902  }
903}
904
[e19eb97]905void owl_function_loadloginsubs(const char *file)
[d54838d]906{
[7933748]907  int ret;
[ecd5dc5]908
[7933748]909  ret=owl_zephyr_loadloginsubs(file);
[ecd5dc5]910
911  if (!owl_context_is_interactive(owl_global_get_context(&g))) return;
[7933748]912  if (ret==0) {
913  } else if (ret==-1) {
[ec6ff52]914    owl_function_error("Could not open file for login subscriptions.");
[7933748]915  } else {
[ec6ff52]916    owl_function_error("Error subscribing to login messages from file.");
[7933748]917  }
918}
919
[1b6b2f3]920void owl_callback_aimlogin(owl_editwin *e) {
[8dfb59c]921  char *user = owl_editwin_get_cbdata(e);
922  owl_function_aimlogin(user,
[db8b00b]923                        owl_editwin_get_text(e));
[1b6b2f3]924}
925
[e19eb97]926void owl_function_aimlogin(const char *user, const char *passwd) {
[4211f50b]927  int ret;
928
929  /* clear the buddylist */
930  owl_buddylist_clear(owl_global_get_buddylist(&g));
931
932  /* try to login */
933  ret=owl_aim_login(user, passwd);
934  if (ret) owl_function_makemsg("Warning: login for %s failed.\n", user);
935}
936
[c79a047]937void owl_function_suspend(void)
[d54838d]938{
[7d4fbcd]939  endwin();
940  printf("\n");
941  kill(getpid(), SIGSTOP);
942
943  /* resize to reinitialize all the windows when we come back */
944  owl_command_resize();
945}
946
[c79a047]947void owl_function_zaway_toggle(void)
[d54838d]948{
[7d4fbcd]949  if (!owl_global_is_zaway(&g)) {
950    owl_global_set_zaway_msg(&g, owl_global_get_zaway_msg_default(&g));
951    owl_function_zaway_on();
952  } else {
953    owl_function_zaway_off();
954  }
955}
956
[c79a047]957void owl_function_zaway_on(void)
[d54838d]958{
[7d4fbcd]959  owl_global_set_zaway_on(&g);
[4b660cc]960  owl_function_makemsg("zaway set (%s)", owl_global_get_zaway_msg(&g));
[7d4fbcd]961}
962
[c79a047]963void owl_function_zaway_off(void)
[d54838d]964{
[7d4fbcd]965  owl_global_set_zaway_off(&g);
[4b660cc]966  owl_function_makemsg("zaway off");
967}
968
[c79a047]969void owl_function_aaway_toggle(void)
[4b660cc]970{
971  if (!owl_global_is_aaway(&g)) {
972    owl_global_set_aaway_msg(&g, owl_global_get_aaway_msg_default(&g));
973    owl_function_aaway_on();
974  } else {
975    owl_function_aaway_off();
976  }
977}
978
[c79a047]979void owl_function_aaway_on(void)
[4b660cc]980{
981  owl_global_set_aaway_on(&g);
982  /* owl_aim_set_awaymsg(owl_global_get_zaway_msg(&g)); */
983  owl_function_makemsg("AIM away set (%s)", owl_global_get_aaway_msg(&g));
984}
985
[c79a047]986void owl_function_aaway_off(void)
[4b660cc]987{
988  owl_global_set_aaway_off(&g);
989  /* owl_aim_set_awaymsg(""); */
990  owl_function_makemsg("AIM away off");
[7d4fbcd]991}
992
[c79a047]993void owl_function_quit(void)
[d54838d]994{
[7d4fbcd]995  char *ret;
996 
997  /* zlog out if we need to */
[7433402]998  if (owl_global_is_havezephyr(&g) &&
999      owl_global_is_shutdownlogout(&g)) {
[31e48a3]1000    owl_zephyr_zlog_out();
[7d4fbcd]1001  }
1002
1003  /* execute the commands in shutdown */
[0337203]1004  ret = owl_perlconfig_execute("BarnOwl::Hooks::_shutdown();");
[3b8a563]1005  g_free(ret);
[7d4fbcd]1006
[d09e5a1]1007  /* signal our child process, if any */
1008  if (owl_global_get_newmsgproc_pid(&g)) {
1009    kill(owl_global_get_newmsgproc_pid(&g), SIGHUP);
1010  }
[8c46404]1011 
1012  /* Quit AIM */
1013  if (owl_global_is_aimloggedin(&g)) {
1014    owl_aim_logout();
1015  }
1016
[a16d7e5]1017  owl_function_debugmsg("Quitting BarnOwl");
[3ecd78b]1018  owl_select_quit_loop();
[7d4fbcd]1019}
1020
[d54838d]1021void owl_function_calculate_topmsg(int direction)
1022{
[aa2f33b3]1023  int recwinlines, topmsg, curmsg;
[9e5c9f3]1024  const owl_view *v;
[7d4fbcd]1025
1026  v=owl_global_get_current_view(&g);
[aa2f33b3]1027  curmsg=owl_global_get_curmsg(&g);
1028  topmsg=owl_global_get_topmsg(&g);
[7d4fbcd]1029  recwinlines=owl_global_get_recwin_lines(&g);
1030
[f9c43ae]1031  /*
[7d4fbcd]1032  if (owl_view_get_size(v) < 1) {
1033    return;
1034  }
[f9c43ae]1035  */
[aa2f33b3]1036
1037  switch (owl_global_get_scrollmode(&g)) {
1038  case OWL_SCROLLMODE_TOP:
[f9c43ae]1039    topmsg = owl_function_calculate_topmsg_top(direction, v, curmsg, topmsg, recwinlines);
[aa2f33b3]1040    break;
1041  case OWL_SCROLLMODE_NEARTOP:
[f9c43ae]1042    topmsg = owl_function_calculate_topmsg_neartop(direction, v, curmsg, topmsg, recwinlines);
[aa2f33b3]1043    break;
1044  case OWL_SCROLLMODE_CENTER:
[f9c43ae]1045    topmsg = owl_function_calculate_topmsg_center(direction, v, curmsg, topmsg, recwinlines);
[aa2f33b3]1046    break;
1047  case OWL_SCROLLMODE_PAGED:
[f9c43ae]1048    topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 0);
[aa2f33b3]1049    break;
1050  case OWL_SCROLLMODE_PAGEDCENTER:
[f9c43ae]1051    topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 1);
[aa2f33b3]1052    break;
1053  case OWL_SCROLLMODE_NORMAL:
1054  default:
[f9c43ae]1055    topmsg = owl_function_calculate_topmsg_normal(direction, v, curmsg, topmsg, recwinlines);
[aa2f33b3]1056  }
[3a2daac]1057  owl_function_debugmsg("Calculated a topmsg of %i", topmsg);
[aa2f33b3]1058  owl_global_set_topmsg(&g, topmsg);
1059}
1060
1061/* Returns what the new topmsg should be. 
1062 * Passed the last direction of movement,
1063 * the current view,
1064 * the current message number in the view,
1065 * the top message currently being displayed,
1066 * and the number of lines in the recwin.
1067 */
[9e5c9f3]1068int owl_function_calculate_topmsg_top(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
[d54838d]1069{
[f9c43ae]1070  return(curmsg);
[aa2f33b3]1071}
1072
[9e5c9f3]1073int owl_function_calculate_topmsg_neartop(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
[d54838d]1074{
[aa2f33b3]1075  if (curmsg>0 
1076      && (owl_message_get_numlines(owl_view_get_element(v, curmsg-1))
1077          <  recwinlines/2)) {
[f9c43ae]1078    return(curmsg-1);
[aa2f33b3]1079  } else {
[f9c43ae]1080    return(curmsg);
[aa2f33b3]1081  }
1082}
1083 
[9e5c9f3]1084int owl_function_calculate_topmsg_center(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
[d54838d]1085{
[aa2f33b3]1086  int i, last, lines;
1087
1088  last = curmsg;
1089  lines = 0;
1090  for (i=curmsg-1; i>=0; i--) {
1091    lines += owl_message_get_numlines(owl_view_get_element(v, i));
1092    if (lines > recwinlines/2) break;
1093    last = i;
1094  }
[f9c43ae]1095  return(last);
[aa2f33b3]1096}
1097 
[9e5c9f3]1098int owl_function_calculate_topmsg_paged(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines, int center_on_page)
[d54838d]1099{
[aa2f33b3]1100  int i, last, lines, savey;
1101 
1102  /* If we're off the top of the screen, scroll up such that the
1103   * curmsg is near the botton of the screen. */
1104  if (curmsg < topmsg) {
1105    last = curmsg;
1106    lines = 0;
1107    for (i=curmsg; i>=0; i--) {
1108      lines += owl_message_get_numlines(owl_view_get_element(v, i));
1109      if (lines > recwinlines) break;
1110    last = i;
1111    }
1112    if (center_on_page) {
[f9c43ae]1113      return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines));
[aa2f33b3]1114    } else {
[f9c43ae]1115      return(last);
[aa2f33b3]1116    }
1117  }
1118
1119  /* Find number of lines from top to bottom of curmsg (store in savey) */
1120  savey=0;
1121  for (i=topmsg; i<=curmsg; i++) {
1122    savey+=owl_message_get_numlines(owl_view_get_element(v, i));
1123  }
1124
1125  /* if we're off the bottom of the screen, scroll down */
1126  if (savey > recwinlines) {
1127    if (center_on_page) {
[f9c43ae]1128      return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines));
[aa2f33b3]1129    } else {
[f9c43ae]1130      return(curmsg);
[aa2f33b3]1131    }
1132  }
1133
1134  /* else just stay as we are... */
[f9c43ae]1135  return(topmsg);
[aa2f33b3]1136}
1137
[9e5c9f3]1138int owl_function_calculate_topmsg_normal(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
[d54838d]1139{
[801b7ac]1140  int savey, i, foo, y;
[f9c43ae]1141
[88736cb]1142  if (curmsg<0) return(topmsg);
1143   
[f9c43ae]1144  /* If we're off the top of the screen then center */
1145  if (curmsg<topmsg) {
1146    topmsg=owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines);
1147  }
1148
[801b7ac]1149  /* If curmsg is so far past topmsg that there are more messages than
1150     lines, skip the line counting that follows because we're
1151     certainly off screen.  */
1152  savey=curmsg-topmsg;
1153  if (savey <= recwinlines) {
1154    /* Find number of lines from top to bottom of curmsg (store in savey) */
1155    savey = 0;
1156    for (i=topmsg; i<=curmsg; i++) {
1157      savey+=owl_message_get_numlines(owl_view_get_element(v, i));
1158    }
[7d4fbcd]1159  }
1160
[f9c43ae]1161  /* If we're off the bottom of the screen, set the topmsg to curmsg
1162   * and scroll upwards */
1163  if (savey > recwinlines) {
1164    topmsg=curmsg;
[801b7ac]1165    savey=owl_message_get_numlines(owl_view_get_element(v, curmsg));
[f9c43ae]1166    direction=OWL_DIRECTION_UPWARDS;
[7d4fbcd]1167  }
[f9c43ae]1168 
[7d4fbcd]1169  /* If our bottom line is less than 1/4 down the screen then scroll up */
1170  if (direction == OWL_DIRECTION_UPWARDS || direction == OWL_DIRECTION_NONE) {
1171    if (savey < (recwinlines / 4)) {
1172      y=0;
[801b7ac]1173      for (i=curmsg; i>=0; i--) {
1174        foo=owl_message_get_numlines(owl_view_get_element(v, i));
[7d4fbcd]1175        /* will we run the curmsg off the screen? */
1176        if ((foo+y) >= recwinlines) {
[801b7ac]1177          i++;
1178          if (i>curmsg) i=curmsg;
[7d4fbcd]1179          break;
1180        }
1181        /* have saved 1/2 the screen space? */
1182        y+=foo;
1183        if (y > (recwinlines / 2)) break;
1184      }
[801b7ac]1185      if (i<0) i=0;
1186      return(i);
[7d4fbcd]1187    }
1188  }
1189
1190  if (direction == OWL_DIRECTION_DOWNWARDS || direction == OWL_DIRECTION_NONE) {
1191    /* If curmsg bottom line is more than 3/4 down the screen then scroll down */
1192    if (savey > ((recwinlines * 3)/4)) {
1193      y=0;
1194      /* count lines from the top until we can save 1/2 the screen size */
[801b7ac]1195      for (i=topmsg; i<curmsg; i++) {
1196        y+=owl_message_get_numlines(owl_view_get_element(v, i));
[7d4fbcd]1197        if (y > (recwinlines / 2)) break;
1198      }
[801b7ac]1199      if (i==curmsg) {
1200        i--;
[7d4fbcd]1201      }
[801b7ac]1202      return(i+1);
[7d4fbcd]1203    }
1204  }
[aa2f33b3]1205
[f9c43ae]1206  return(topmsg);
[7d4fbcd]1207}
1208
[c79a047]1209void owl_function_resize(void)
[d54838d]1210{
[7d4fbcd]1211  owl_global_set_resize_pending(&g);
1212}
1213
[4479497]1214void G_GNUC_PRINTF(1, 2) owl_function_debugmsg(const char *fmt, ...)
[d54838d]1215{
[4ebbfbc]1216  char *tmpbuff;
[7d4fbcd]1217  FILE *file;
1218  time_t now;
1219  va_list ap;
1220  va_start(ap, fmt);
1221
[52761cc]1222  if (!owl_global_is_debug_fast(&g))
1223    return;
[7d4fbcd]1224
[d12a8c7]1225  file = owl_global_get_debug_file_handle(&g);
[52761cc]1226  if (!file) /* XXX should report this */
1227    return;
[7d4fbcd]1228
[52761cc]1229  now = time(NULL);
[7d4fbcd]1230
[0071c88]1231  tmpbuff = owl_util_format_time(localtime(&now));
[4ebbfbc]1232  fprintf(file, "[%d -  %s - %lds]: ",
1233          (int) getpid(), tmpbuff, now - owl_global_get_starttime(&g));
1234  g_free(tmpbuff);
[7d4fbcd]1235  vfprintf(file, fmt, ap);
[52761cc]1236  putc('\n', file);
[d12a8c7]1237  fflush(file);
[7d4fbcd]1238
1239  va_end(ap);
1240}
1241
[c79a047]1242void owl_function_beep(void)
[d54838d]1243{
[7d4fbcd]1244  if (owl_global_is_bell(&g)) {
1245    beep();
1246  }
1247}
1248
[e19eb97]1249int owl_function_subscribe(const char *class, const char *inst, const char *recip)
[d54838d]1250{
[7d4fbcd]1251  int ret;
1252
1253  ret=owl_zephyr_sub(class, inst, recip);
1254  if (ret) {
[ec6ff52]1255    owl_function_error("Error subscribing.");
[7d4fbcd]1256  } else {
1257    owl_function_makemsg("Subscribed.");
1258  }
[3617286]1259  return(ret);
[7d4fbcd]1260}
1261
[e19eb97]1262void owl_function_unsubscribe(const char *class, const char *inst, const char *recip)
[d54838d]1263{
[7d4fbcd]1264  int ret;
1265
1266  ret=owl_zephyr_unsub(class, inst, recip);
1267  if (ret) {
[ec6ff52]1268    owl_function_error("Error subscribing.");
[7d4fbcd]1269  } else {
1270    owl_function_makemsg("Unsubscribed.");
1271  }
1272}
1273
[b343c2c]1274static void _dirty_everything(gpointer data, gpointer user_data) {
1275  owl_window *w = data;
[e8128c5]1276  if (!owl_window_is_realized(w))
1277    return;
1278  owl_window_dirty(w);
[b343c2c]1279  owl_window_children_foreach(w, _dirty_everything, NULL);
[7d4fbcd]1280}
1281
[c79a047]1282void owl_function_full_redisplay(void)
[d54838d]1283{
[1d81c51]1284  /* Ask every widget to redraw itself. */
[b343c2c]1285  _dirty_everything(owl_window_get_screen(), NULL);
[1d81c51]1286  /* Force ncurses to redisplay everything. */
1287  clearok(stdscr, TRUE);
[7d4fbcd]1288}
1289
[e19eb97]1290void owl_function_popless_text(const char *text)
[d54838d]1291{
[7d4fbcd]1292  owl_popwin *pw;
1293  owl_viewwin *v;
1294
[9eb38bb]1295  if (owl_global_get_popwin(&g) || owl_global_get_viewwin(&g)) {
[4cf7b1b]1296    owl_function_error("Popwin already in use.");
1297    return;
1298  }
[03ca005]1299  pw = owl_popwin_new();
1300  owl_global_set_popwin(&g, pw);
1301  owl_popwin_up(pw);
[9eb38bb]1302
1303  v = owl_viewwin_new_text(owl_popwin_get_content(pw), text);
1304  owl_global_set_viewwin(&g, v);
1305
[07b59ea]1306  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless", NULL);
[7d4fbcd]1307}
1308
[075ba92]1309void owl_function_popless_fmtext(const owl_fmtext *fm)
[d54838d]1310{
[7d4fbcd]1311  owl_popwin *pw;
1312  owl_viewwin *v;
1313
[9eb38bb]1314  if (owl_global_get_popwin(&g) || owl_global_get_viewwin(&g)) {
[4cf7b1b]1315    owl_function_error("Popwin already in use.");
1316    return;
1317  }
[03ca005]1318  pw = owl_popwin_new();
1319  owl_global_set_popwin(&g, pw);
1320  owl_popwin_up(pw);
[9eb38bb]1321
1322  v = owl_viewwin_new_fmtext(owl_popwin_get_content(pw), fm);
1323  owl_global_set_viewwin(&g, v);
1324
[07b59ea]1325  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless", NULL);
[f17bff98]1326}
1327
[e19eb97]1328void owl_function_popless_file(const char *filename)
[f17bff98]1329{
1330  owl_fmtext fm;
1331  FILE *file;
[b7ee89b]1332  char *s = NULL;
[f17bff98]1333
1334  file=fopen(filename, "r");
1335  if (!file) {
1336    owl_function_error("Could not open file: %s", filename);
1337    return;
1338  }
1339
1340  owl_fmtext_init_null(&fm);
[b7ee89b]1341  while (owl_getline(&s, file))
1342    owl_fmtext_append_normal(&fm, s);
[ddbbcffa]1343  g_free(s);
[f17bff98]1344
1345  owl_function_popless_fmtext(&fm);
[7ab0020]1346  owl_fmtext_cleanup(&fm);
[f17bff98]1347  fclose(file);
[7d4fbcd]1348}
1349
[c79a047]1350void owl_function_about(void)
[d54838d]1351{
[2101a50]1352  owl_function_popless_text(
[b8a3e00]1353    "This is BarnOwl version " OWL_VERSION_STRING ".\n\n"
1354    "BarnOwl is a fork of the Owl zephyr client, written and\n"
[2101a50]1355    "maintained by Alejandro Sedeno and Nelson Elhage at the\n"
1356    "Massachusetts Institute of Technology. \n"
1357    "\n"
1358    "Owl was written by James Kretchmar. The first version, 0.5, was\n"
1359    "released in March 2002.\n"
1360    "\n"
1361    "The name 'owl' was chosen in reference to the owls in the\n"
1362    "Harry Potter novels, who are tasked with carrying messages\n"
[b8a3e00]1363    "between Witches and Wizards. The name 'BarnOwl' was chosen\n"
[2101a50]1364    "because we feel our owls should live closer to our ponies.\n"
1365    "\n"
[b03c714]1366    "Copyright (c) 2006-2011 The BarnOwl Developers. All rights reserved.\n"
[2101a50]1367    "Copyright (c) 2004 James Kretchmar. All rights reserved.\n"
1368    "Copyright 2002 Massachusetts Institute of Technology\n"
1369    "\n"
1370    "This program is free software. You can redistribute it and/or\n"
1371    "modify under the terms of the Sleepycat License. Use the \n"
1372    "':show license' command to display the full license\n"
1373  );
[7d4fbcd]1374}
1375
[c79a047]1376void owl_function_info(void)
[d54838d]1377{
[c08c70a]1378  const owl_message *m;
[5789230]1379  owl_fmtext fm, attrfm;
[9e5c9f3]1380  const owl_view *v;
[0071c88]1381  char *time;
[09489b89]1382#ifdef HAVE_LIBZEPHYR
[1077891a]1383  const ZNotice_t *n;
[09489b89]1384#endif
[7d4fbcd]1385
[d0d65df]1386  owl_fmtext_init_null(&fm);
1387 
[7d4fbcd]1388  v=owl_global_get_current_view(&g);
[5eeea3b]1389  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1390  if (!m || owl_view_get_size(v)==0) {
[ec6ff52]1391    owl_function_error("No message selected\n");
[7d4fbcd]1392    return;
1393  }
1394
[5789230]1395  owl_fmtext_append_bold(&fm, "General Information:\n");
[57609b3]1396  owl_fmtext_appendf_normal(&fm, "  Msg Id    : %i\n", owl_message_get_id(m));
[df0d93a]1397
[5789230]1398  owl_fmtext_append_normal(&fm, "  Type      : ");
[37eab7f]1399  owl_fmtext_append_bold(&fm, owl_message_get_type(m));
[df0d93a]1400  owl_fmtext_append_normal(&fm, "\n");
1401
[4b464a4]1402  if (owl_message_is_direction_in(m)) {
[5789230]1403    owl_fmtext_append_normal(&fm, "  Direction : in\n");
[4b464a4]1404  } else if (owl_message_is_direction_out(m)) {
[5789230]1405    owl_fmtext_append_normal(&fm, "  Direction : out\n");
[4b464a4]1406  } else if (owl_message_is_direction_none(m)) {
[5789230]1407    owl_fmtext_append_normal(&fm, "  Direction : none\n");
[4b464a4]1408  } else {
[5789230]1409    owl_fmtext_append_normal(&fm, "  Direction : unknown\n");
[4b464a4]1410  }
[df0d93a]1411
[0071c88]1412  time = owl_message_format_time(m);
1413  owl_fmtext_appendf_normal(&fm, "  Time      : %s\n", time);
1414  g_free(time);
[4b464a4]1415
[df0d93a]1416  if (!owl_message_is_type_admin(m)) {
[57609b3]1417    owl_fmtext_appendf_normal(&fm, "  Sender    : %s\n", owl_message_get_sender(m));
1418    owl_fmtext_appendf_normal(&fm, "  Recipient : %s\n", owl_message_get_recipient(m));
[df0d93a]1419  }
[57609b3]1420
[0ff8fb57]1421  if (owl_message_is_type_zephyr(m)) {
[5789230]1422    owl_fmtext_append_bold(&fm, "\nZephyr Specific Information:\n");
[0ff8fb57]1423   
[57609b3]1424    owl_fmtext_appendf_normal(&fm, "  Class     : %s\n", owl_message_get_class(m));
1425    owl_fmtext_appendf_normal(&fm, "  Instance  : %s\n", owl_message_get_instance(m));
1426    owl_fmtext_appendf_normal(&fm, "  Opcode    : %s\n", owl_message_get_opcode(m));
[09489b89]1427#ifdef HAVE_LIBZEPHYR
[b9517cf]1428    n = owl_message_get_notice(m);
1429    if (n != NULL) {
[6500907]1430      char *tmpbuff, *tmpbuff2;
[259e60a8]1431      int i, fields;
[09489b89]1432
[5a95b69]1433      if (!owl_message_is_pseudo(m)) {
1434        owl_fmtext_append_normal(&fm, "  Kind      : ");
1435        if (n->z_kind==UNSAFE) {
1436          owl_fmtext_append_normal(&fm, "UNSAFE\n");
1437        } else if (n->z_kind==UNACKED) {
1438          owl_fmtext_append_normal(&fm, "UNACKED\n");
1439        } else if (n->z_kind==ACKED) {
1440          owl_fmtext_append_normal(&fm, "ACKED\n");
1441        } else if (n->z_kind==HMACK) {
1442          owl_fmtext_append_normal(&fm, "HMACK\n");
1443        } else if (n->z_kind==HMCTL) {
1444          owl_fmtext_append_normal(&fm, "HMCTL\n");
1445        } else if (n->z_kind==SERVACK) {
1446          owl_fmtext_append_normal(&fm, "SERVACK\n");
1447        } else if (n->z_kind==SERVNAK) {
1448          owl_fmtext_append_normal(&fm, "SERVNACK\n");
1449        } else if (n->z_kind==CLIENTACK) {
1450          owl_fmtext_append_normal(&fm, "CLIENTACK\n");
1451        } else if (n->z_kind==STAT) {
1452          owl_fmtext_append_normal(&fm, "STAT\n");
1453        } else {
1454          owl_fmtext_append_normal(&fm, "ILLEGAL VALUE\n");
1455        }
[d0d65df]1456      }
[57609b3]1457      owl_fmtext_appendf_normal(&fm, "  Host      : %s\n", owl_message_get_hostname(m));
[5a95b69]1458
1459      if (!owl_message_is_pseudo(m)) {
1460        owl_fmtext_append_normal(&fm, "\n");
[57609b3]1461        owl_fmtext_appendf_normal(&fm, "  Port      : %i\n", ntohs(n->z_port));
[f12d199]1462        owl_fmtext_appendf_normal(&fm, "  Auth      : %s\n", owl_zephyr_get_authstr(n));
[57609b3]1463
1464        /* FIXME make these more descriptive */
[f12d199]1465        owl_fmtext_appendf_normal(&fm, "  Checkd Ath: %i\n", n->z_checked_auth);
[57609b3]1466        owl_fmtext_appendf_normal(&fm, "  Multi notc: %s\n", n->z_multinotice);
1467        owl_fmtext_appendf_normal(&fm, "  Num other : %i\n", n->z_num_other_fields);
1468        owl_fmtext_appendf_normal(&fm, "  Msg Len   : %i\n", n->z_message_len);
[5a95b69]1469
1470        fields=owl_zephyr_get_num_fields(n);
[57609b3]1471        owl_fmtext_appendf_normal(&fm, "  Fields    : %i\n", fields);
1472
[259e60a8]1473        for (i = 0; i < fields; i++) {
[6500907]1474          tmpbuff = owl_zephyr_get_field_as_utf8(n, i + 1);
1475          tmpbuff2 = owl_text_indent(tmpbuff, 14, false);
1476          owl_fmtext_appendf_normal(&fm, "  Field %i   : %s\n", i + 1, tmpbuff2);
1477          g_free(tmpbuff2);
1478          g_free(tmpbuff);
[5a95b69]1479        }
[6500907]1480        tmpbuff = owl_text_indent(n->z_default_format, 14, false);
1481        owl_fmtext_appendf_normal(&fm, "  Default Fm: %s\n", tmpbuff);
1482        g_free(tmpbuff);
[d0d65df]1483      }
[57609b3]1484
[7d4fbcd]1485    }
[57609b3]1486#endif
[7d4fbcd]1487  }
[0ff8fb57]1488
[a16d7e5]1489  owl_fmtext_append_bold(&fm, "\nBarnOwl Message Attributes:\n");
[5789230]1490  owl_message_attributes_tofmtext(m, &attrfm);
1491  owl_fmtext_append_fmtext(&fm, &attrfm);
[d0d65df]1492 
1493  owl_function_popless_fmtext(&fm);
[7ab0020]1494  owl_fmtext_cleanup(&fm);
1495  owl_fmtext_cleanup(&attrfm);
[7d4fbcd]1496}
1497
[5639bf2]1498/* print the current message in a popup window.
1499 * Use the 'default' style regardless of whatever
1500 * style the user may be using
1501 */
[c79a047]1502void owl_function_curmsg_to_popwin(void)
[d54838d]1503{
[9e5c9f3]1504  const owl_view *v;
[c08c70a]1505  const owl_message *m;
[1fdab04]1506  const owl_style *s;
[5639bf2]1507  owl_fmtext fm;
[7d4fbcd]1508
[5639bf2]1509  v=owl_global_get_current_view(&g);
1510  s=owl_global_get_style_by_name(&g, "default");
[7d4fbcd]1511
[5eeea3b]1512  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1513
1514  if (!m || owl_view_get_size(v)==0) {
[ec6ff52]1515    owl_function_error("No current message");
[7d4fbcd]1516    return;
1517  }
1518
[5639bf2]1519  owl_fmtext_init_null(&fm);
1520  owl_style_get_formattext(s, &fm, m);
1521
1522  owl_function_popless_fmtext(&fm);
[7ab0020]1523  owl_fmtext_cleanup(&fm);
[7d4fbcd]1524}
1525
[d54838d]1526void owl_function_page_curmsg(int step)
1527{
[7d4fbcd]1528  /* scroll down or up within the current message IF the message is truncated */
1529
1530  int offset, curmsg, lines;
[9e5c9f3]1531  const owl_view *v;
[7d4fbcd]1532  owl_message *m;
1533
1534  offset=owl_global_get_curmsg_vert_offset(&g);
1535  v=owl_global_get_current_view(&g);
1536  curmsg=owl_global_get_curmsg(&g);
1537  m=owl_view_get_element(v, curmsg);
[5eeea3b]1538  if (!m || owl_view_get_size(v)==0) return;
[7d4fbcd]1539  lines=owl_message_get_numlines(m);
1540
1541  if (offset==0) {
1542    /* Bail if the curmsg isn't the last one displayed */
1543    if (curmsg != owl_mainwin_get_last_msg(owl_global_get_mainwin(&g))) {
[f51bc78]1544      owl_function_makemsg("The entire message is already displayed");
[7d4fbcd]1545      return;
1546    }
1547   
1548    /* Bail if we're not truncated */
1549    if (!owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
[f51bc78]1550      owl_function_makemsg("The entire message is already displayed");
[7d4fbcd]1551      return;
1552    }
1553  }
1554 
1555 
1556  /* don't scroll past the last line */
1557  if (step>0) {
1558    if (offset+step > lines-1) {
1559      owl_global_set_curmsg_vert_offset(&g, lines-1);
1560    } else {
1561      owl_global_set_curmsg_vert_offset(&g, offset+step);
1562    }
1563  }
1564
1565  /* would we be before the beginning of the message? */
1566  if (step<0) {
1567    if (offset+step<0) {
1568      owl_global_set_curmsg_vert_offset(&g, 0);
1569    } else {
1570      owl_global_set_curmsg_vert_offset(&g, offset+step);
1571    }
1572  }
1573 
1574  /* redisplay */
1575  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1576}
1577
[d54838d]1578void owl_function_resize_typwin(int newsize)
1579{
[7d4fbcd]1580  owl_global_set_typwin_lines(&g, newsize);
[f6fae8d]1581  owl_mainpanel_layout_contents(&g.mainpanel);
[7d4fbcd]1582}
1583
[c79a047]1584void owl_function_mainwin_pagedown(void)
[d54838d]1585{
[7d4fbcd]1586  int i;
1587
1588  i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g));
1589  if (i<0) return;
[f2e36b5]1590  if (owl_mainwin_is_last_msg_truncated(owl_global_get_mainwin(&g))
1591      && (owl_global_get_curmsg(&g) < i)
1592      && (i>0)) {
1593    i--;
1594  }
[7d4fbcd]1595  owl_global_set_curmsg(&g, i);
1596  owl_function_nextmsg();
1597}
1598
[c79a047]1599void owl_function_mainwin_pageup(void)
[d54838d]1600{
[7d4fbcd]1601  owl_global_set_curmsg(&g, owl_global_get_topmsg(&g));
1602  owl_function_prevmsg();
1603}
1604
[c79a047]1605void owl_function_getsubs(void)
[d54838d]1606{
[09489b89]1607  char *buff;
[7d4fbcd]1608
[09489b89]1609  buff=owl_zephyr_getsubs();
[7d4fbcd]1610
[09489b89]1611  if (buff) {
1612    owl_function_popless_text(buff);
1613  } else {
1614    owl_function_popless_text("Error getting subscriptions");
[7d4fbcd]1615  }
[09489b89]1616           
[ddbbcffa]1617  g_free(buff);
[7d4fbcd]1618}
1619
[c79a047]1620void owl_function_printallvars(void)
[d54838d]1621{
[ca54fd6]1622  const owl_variable *v;
[e19eb97]1623  const char *name;
[010a951]1624  char *var;
[ce68f23]1625  GPtrArray *varnames;
1626  int i;
[b4c270c]1627  GString *str   = g_string_new("");
[7d4fbcd]1628
[b4c270c]1629  g_string_append_printf(str, "%-20s = %s\n", "VARIABLE", "VALUE");
1630  g_string_append_printf(str, "%-20s   %s\n",  "--------", "-----");
[ce68f23]1631  varnames = owl_variable_dict_get_names(owl_global_get_vardict(&g));
1632  for (i = 0; i < varnames->len; i++) {
1633    name = varnames->pdata[i];
[7d4fbcd]1634    if (name && name[0]!='_') {
[b4c270c]1635      g_string_append_printf(str, "\n%-20s = ", name);
[ca54fd6]1636      v = owl_variable_get_var(owl_global_get_vardict(&g), name);
1637      var = owl_variable_get_tostring(v);
[010a951]1638      if (var) {
[ca749a9]1639        g_string_append(str, var);
1640        g_free(var);
1641      } else {
1642        g_string_append(str, "<null>");
[010a951]1643      }
[7d4fbcd]1644    }
1645  }
[b4c270c]1646  g_string_append(str, "\n");
[ce68f23]1647  owl_ptr_array_free(varnames, g_free);
[b4c270c]1648
1649  owl_function_popless_text(str->str);
[d222c44]1650  g_string_free(str, true);
[7d4fbcd]1651}
1652
[c79a047]1653void owl_function_show_variables(void)
[d54838d]1654{
[ca54fd6]1655  const owl_variable *v;
[ce68f23]1656  GPtrArray *varnames;
[7d4fbcd]1657  owl_fmtext fm; 
[ce68f23]1658  int i;
[e19eb97]1659  const char *varname;
[7d4fbcd]1660
1661  owl_fmtext_init_null(&fm);
1662  owl_fmtext_append_bold(&fm, 
1663      "Variables: (use 'show variable <name>' for details)\n");
[ce68f23]1664  varnames = owl_variable_dict_get_names(owl_global_get_vardict(&g));
1665  for (i = 0; i < varnames->len; i++) {
1666    varname = varnames->pdata[i];
[7d4fbcd]1667    if (varname && varname[0]!='_') {
[ca54fd6]1668      v = owl_variable_get_var(owl_global_get_vardict(&g), varname);
1669      owl_variable_describe(v, &fm);
[7d4fbcd]1670    }
1671  }
[ce68f23]1672  owl_ptr_array_free(varnames, g_free);
[7d4fbcd]1673  owl_function_popless_fmtext(&fm);
[7ab0020]1674  owl_fmtext_cleanup(&fm);
[7d4fbcd]1675}
1676
[e19eb97]1677void owl_function_show_variable(const char *name)
[d54838d]1678{
[ca54fd6]1679  const owl_variable *v;
[7d4fbcd]1680  owl_fmtext fm; 
1681
1682  owl_fmtext_init_null(&fm);
[ca54fd6]1683  v = owl_variable_get_var(owl_global_get_vardict(&g), name);
1684  if (v)
1685    owl_variable_get_help(v, &fm);
1686  else
1687    owl_fmtext_append_normal(&fm, "No such variable...\n");
[7d4fbcd]1688  owl_function_popless_fmtext(&fm);
[7ab0020]1689  owl_fmtext_cleanup(&fm);
[7d4fbcd]1690}
1691
[efeec7f]1692void owl_function_delete_and_expunge_by_id(int id, bool exclaim_success)
1693{
1694  const owl_messagelist *ml = owl_global_get_msglist(&g);
1695  int msg = owl_messagelist_get_index_by_id(ml, id);
1696  if (msg < 0) {
1697    owl_function_error("No message with id %d: unable to delete", id);
1698  } else {
1699    owl_function_delete_and_expunge_message(msg);
1700    if (exclaim_success)
1701      owl_function_makemsg("Message deleted and expunged");
1702  }
1703}
1704
[7d4fbcd]1705/* note: this applies to global message list, not to view.
1706 * If flag is 1, deletes.  If flag is 0, undeletes. */
[d54838d]1707void owl_function_delete_by_id(int id, int flag)
1708{
[3eb599d]1709  const owl_messagelist *ml;
[7d4fbcd]1710  owl_message *m;
1711  ml = owl_global_get_msglist(&g);
1712  m = owl_messagelist_get_by_id(ml, id);
1713  if (m) {
1714    if (flag == 1) {
1715      owl_message_mark_delete(m);
1716    } else if (flag == 0) {
1717      owl_message_unmark_delete(m);
1718    }
1719    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1720  } else {
[ec6ff52]1721    owl_function_error("No message with id %d: unable to mark for (un)delete",id);
[7d4fbcd]1722  }
1723}
1724
[c79a047]1725void owl_function_delete_automsgs(void)
[d54838d]1726{
[7d4fbcd]1727  /* mark for deletion all messages in the current view that match the
1728   * 'trash' filter */
1729
1730  int i, j, count;
1731  owl_message *m;
[9e5c9f3]1732  const owl_view *v;
[4542047]1733  const owl_filter *f;
[7d4fbcd]1734
1735  /* get the trash filter */
1736  f=owl_global_get_filter(&g, "trash");
1737  if (!f) {
[ec6ff52]1738    owl_function_error("No trash filter defined");
[7d4fbcd]1739    return;
1740  }
1741
1742  v=owl_global_get_current_view(&g);
1743
1744  count=0;
1745  j=owl_view_get_size(v);
1746  for (i=0; i<j; i++) {
1747    m=owl_view_get_element(v, i);
1748    if (owl_filter_message_match(f, m)) {
1749      count++;
1750      owl_message_mark_delete(m);
1751    }
1752  }
1753  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
[1c6c4d3]1754  owl_function_makemsg("%i messages marked for deletion", count);
[7d4fbcd]1755}
1756
[c79a047]1757void owl_function_status(void)
[d54838d]1758{
[4ebbfbc]1759  char *tmpbuff;
[49a8434]1760  char buff[MAXPATHLEN+1];
[7d4fbcd]1761  time_t start;
1762  int up, days, hours, minutes;
[a352335c]1763  owl_fmtext fm;
1764
1765  owl_fmtext_init_null(&fm);
[7d4fbcd]1766
1767  start=owl_global_get_starttime(&g);
1768
[d9b0b972]1769  owl_fmtext_append_normal(&fm, "General Information:\n");
1770
1771  owl_fmtext_append_normal(&fm, "  Version: ");
[a352335c]1772  owl_fmtext_append_normal(&fm, OWL_VERSION_STRING);
1773  owl_fmtext_append_normal(&fm, "\n");
1774
[cdd3959]1775  owl_fmtext_append_normal(&fm, "  Startup Arguments: ");
[a352335c]1776  owl_fmtext_append_normal(&fm, owl_global_get_startupargs(&g));
1777  owl_fmtext_append_normal(&fm, "\n");
[b6a7367]1778
1779  owl_fmtext_append_normal(&fm, "  Current Directory: ");
[49a8434]1780  if(getcwd(buff, MAXPATHLEN) == NULL) {
1781    owl_fmtext_append_normal(&fm, "<Error in getcwd>");
1782  } else {
1783    owl_fmtext_append_normal(&fm, buff);
1784  }
[b6a7367]1785  owl_fmtext_append_normal(&fm, "\n");
1786
[0071c88]1787  tmpbuff = owl_util_format_time(localtime(&start));
[4ebbfbc]1788  owl_fmtext_appendf_normal(&fm, "  Startup Time: %s\n", tmpbuff);
1789  g_free(tmpbuff);
[7d4fbcd]1790
1791  up=owl_global_get_runtime(&g);
1792  days=up/86400;
1793  up-=days*86400;
1794  hours=up/3600;
1795  up-=hours*3600;
1796  minutes=up/60;
1797  up-=minutes*60;
[c1d166b]1798  owl_fmtext_appendf_normal(&fm, "  Run Time: %i days %2.2i:%2.2i:%2.2i\n", days, hours, minutes, up);
[7d4fbcd]1799
[d9b0b972]1800  owl_fmtext_append_normal(&fm, "\nProtocol Options:\n");
1801  owl_fmtext_append_normal(&fm, "  Zephyr included    : ");
1802  if (owl_global_is_havezephyr(&g)) {
1803    owl_fmtext_append_normal(&fm, "yes\n");
[7d4fbcd]1804  } else {
[d9b0b972]1805    owl_fmtext_append_normal(&fm, "no\n");
[7d4fbcd]1806  }
[d9b0b972]1807  owl_fmtext_append_normal(&fm, "  AIM included       : yes\n");
1808  owl_fmtext_append_normal(&fm, "  Loopback included  : yes\n");
1809
[8262340]1810
[d9b0b972]1811  owl_fmtext_append_normal(&fm, "\nBuild Options:\n");
1812  owl_fmtext_append_normal(&fm, "  Stderr redirection : ");
1813#if OWL_STDERR_REDIR
1814  owl_fmtext_append_normal(&fm, "yes\n");
1815#else
1816  owl_fmtext_append_normal(&fm, "no\n");
1817#endif
1818 
1819
1820  owl_fmtext_append_normal(&fm, "\nAIM Status:\n");
1821  owl_fmtext_append_normal(&fm, "  Logged in: ");
[a352335c]1822  if (owl_global_is_aimloggedin(&g)) {
1823    owl_fmtext_append_normal(&fm, owl_global_get_aim_screenname(&g));
1824    owl_fmtext_append_normal(&fm, "\n");
1825  } else {
[d9b0b972]1826    owl_fmtext_append_normal(&fm, "(not logged in)\n");
[a352335c]1827  }
[d9b0b972]1828
1829  owl_fmtext_append_normal(&fm, "  Processing events: ");
[a352335c]1830  if (owl_global_is_doaimevents(&g)) {
[d9b0b972]1831    owl_fmtext_append_normal(&fm, "yes\n");
[a352335c]1832  } else {
[d9b0b972]1833    owl_fmtext_append_normal(&fm, "no\n");
[a352335c]1834  }
1835
1836  owl_function_popless_fmtext(&fm);
[7ab0020]1837  owl_fmtext_cleanup(&fm);
[7d4fbcd]1838}
1839
[c79a047]1840void owl_function_show_term(void)
[d54838d]1841{
[7d4fbcd]1842  owl_fmtext fm;
1843
1844  owl_fmtext_init_null(&fm);
[c1d166b]1845  owl_fmtext_appendf_normal(&fm, "Terminal Lines: %i\nTerminal Columns: %i\n",
[7d4fbcd]1846          owl_global_get_lines(&g),
1847          owl_global_get_cols(&g));
1848
[7b4f3be]1849  if (has_colors()) {
[7d4fbcd]1850    owl_fmtext_append_normal(&fm, "Color: Yes\n");
[9efa5bd]1851    owl_fmtext_appendf_normal(&fm, "Number of color pairs: %i\n", owl_util_get_colorpairs());
[c1d166b]1852    owl_fmtext_appendf_normal(&fm, "Can change colors: %s\n", can_change_color() ? "yes" : "no");
[7d4fbcd]1853  } else {
1854    owl_fmtext_append_normal(&fm, "Color: No\n");
1855  }
1856
1857  owl_function_popless_fmtext(&fm);
[7ab0020]1858  owl_fmtext_cleanup(&fm);
[7d4fbcd]1859}
1860
[e7cc1c3]1861/* if type = 0 then normal reply.
1862 * if type = 1 then it's a reply to sender
1863 * if enter = 0 then allow the command to be edited
1864 * if enter = 1 then don't wait for editing
1865 */
[d54838d]1866void owl_function_reply(int type, int enter)
1867{
[740d5f7]1868  char *buff=NULL;
[c08c70a]1869  const owl_message *m;
[4542047]1870  const owl_filter *f;
[7d4fbcd]1871 
1872  if (owl_view_get_size(owl_global_get_current_view(&g))==0) {
[ec6ff52]1873    owl_function_error("No message selected");
[7d4fbcd]1874  } else {
[5ebc202]1875    char *cmd;
[7d4fbcd]1876   
1877    m=owl_view_get_element(owl_global_get_current_view(&g), owl_global_get_curmsg(&g));
[5eeea3b]1878    if (!m) {
[ec6ff52]1879      owl_function_error("No message selected");
[5eeea3b]1880      return;
1881    }
1882
[7d4fbcd]1883    /* first check if we catch the reply-lockout filter */
1884    f=owl_global_get_filter(&g, "reply-lockout");
1885    if (f) {
1886      if (owl_filter_message_match(f, m)) {
[ec6ff52]1887        owl_function_error("Sorry, replies to this message have been disabled by the reply-lockout filter");
[7d4fbcd]1888        return;
1889      }
1890    }
[4b464a4]1891
[2c09826]1892    /* then check if it's a question and just bring up the command prompt */
1893    if (owl_message_is_question(m)) {
1894      owl_function_start_command("");
1895      return;
1896    }
1897
[740d5f7]1898    if((type == 0 &&
1899        (cmd=owl_perlconfig_message_call_method(m, "replycmd", 0, NULL))) ||
1900       (type == 1 &&
1901        (cmd=owl_perlconfig_message_call_method(m, "replysendercmd", 0, NULL)))) {
1902      buff = cmd;
[d09e5a1]1903    }
[1b6b2f3]1904
[e0540e4]1905    if(!buff) {
1906        owl_function_error("I don't know how to reply to that message.");
1907        return;
1908    }
[740d5f7]1909
[d09e5a1]1910    if (enter) {
1911      owl_history *hist = owl_global_get_cmd_history(&g);
[b470451]1912      owl_history_store(hist, buff, false);
[d09e5a1]1913      owl_function_command_norv(buff);
1914    } else {
1915      owl_function_start_command(buff);
[7d4fbcd]1916    }
[ddbbcffa]1917    g_free(buff);
[7d4fbcd]1918  }
1919}
1920
[e19eb97]1921void owl_function_zlocate(int argc, const char *const *argv, int auth)
[d54838d]1922{
[2527615]1923  owl_fmtext fm;
[dca3b27]1924  char *ptr;
1925  char *result;
[2527615]1926  int i;
1927
1928  owl_fmtext_init_null(&fm);
[7d4fbcd]1929
[2527615]1930  for (i=0; i<argc; i++) {
[dca3b27]1931    ptr = long_zuser(argv[i]);
1932    result = owl_zephyr_zlocate(ptr, auth);
1933    owl_fmtext_append_normal(&fm, result);
[ddbbcffa]1934    g_free(result);
1935    g_free(ptr);
[7d4fbcd]1936  }
1937
[2527615]1938  owl_function_popless_fmtext(&fm);
[7ab0020]1939  owl_fmtext_cleanup(&fm);
[7d4fbcd]1940}
1941
[5934b87]1942void owl_callback_command(owl_editwin *e)
1943{
1944  char *rv;
1945  const char *line = owl_editwin_get_text(e);
1946
1947  rv = owl_function_command(line);
1948   if (rv) {
1949    owl_function_makemsg("%s", rv);
[ddbbcffa]1950    g_free(rv);
[5934b87]1951  }
1952}
1953
[e19eb97]1954void owl_function_start_command(const char *line)
[d54838d]1955{
[7d4fbcd]1956  owl_editwin *tw;
[c394de8]1957  owl_context *ctx;
[7d4fbcd]1958
[58d47ca]1959  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
[10b866d]1960
[7d4fbcd]1961  owl_editwin_set_locktext(tw, "command: ");
1962
[47519e1b]1963  owl_editwin_insert_string(tw, line);
[cf83b7a]1964
[4a41f16]1965  ctx = owl_editcontext_new(OWL_CTX_EDITLINE, tw, "editline",
1966                            owl_global_deactivate_editcontext, &g);
[c394de8]1967  owl_global_push_context_obj(&g, ctx);
[5934b87]1968  owl_editwin_set_callback(tw, owl_callback_command);
[cf83b7a]1969}
1970
[6829afc]1971CALLER_OWN owl_editwin *owl_function_start_question(const char *line)
[cf83b7a]1972{
1973  owl_editwin *tw;
[c394de8]1974  owl_context *ctx;
[cf83b7a]1975
[58d47ca]1976  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
[cf83b7a]1977
1978  owl_editwin_set_locktext(tw, line);
1979
[4a41f16]1980  ctx = owl_editcontext_new(OWL_CTX_EDITRESPONSE, tw, "editresponse",
1981                            owl_global_deactivate_editcontext, &g);
[c394de8]1982  owl_global_push_context_obj(&g, ctx);
[9186c75]1983  return tw;
[7d4fbcd]1984}
1985
[6829afc]1986CALLER_OWN owl_editwin *owl_function_start_password(const char *line)
[453bd70]1987{
1988  owl_editwin *tw;
[c394de8]1989  owl_context *ctx;
[453bd70]1990
[58d47ca]1991  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, NULL);
1992
[453bd70]1993  owl_editwin_set_echochar(tw, '*');
1994
1995  owl_editwin_set_locktext(tw, line);
1996
[4a41f16]1997  ctx = owl_editcontext_new(OWL_CTX_EDITRESPONSE, tw, "editresponse",
1998                            owl_global_deactivate_editcontext, &g);
[c394de8]1999  owl_global_push_context_obj(&g, ctx);
[9186c75]2000  return tw;
[453bd70]2001}
2002
[6829afc]2003CALLER_OWN char *owl_function_exec(int argc, const char *const *argv, const char *buff, int type)
[d54838d]2004{
[7d4fbcd]2005  /* if type == 1 display in a popup
2006   * if type == 2 display an admin messages
2007   * if type == 0 return output
2008   * else display in a popup
2009   */
[e19eb97]2010  const char *redirect = " 2>&1 < /dev/null";
[65b2173]2011  char *newbuff;
[b7ee89b]2012  char *out;
[7d4fbcd]2013  FILE *p;
2014
[2a2bb60]2015#if OWL_STDERR_REDIR
2016  redirect = " < /dev/null";
2017#endif
2018
[7d4fbcd]2019  if (argc<2) {
[ec6ff52]2020    owl_function_error("Wrong number of arguments to the exec command");
[7d4fbcd]2021    return NULL;
2022  }
2023
2024  buff = skiptokens(buff, 1);
[1a30f05]2025  newbuff = g_strdup_printf("exec%s; %s", redirect, buff);
[7d4fbcd]2026
[7ba9e0de]2027  if (type == OWL_OUTPUT_POPUP) {
[afbf668]2028    owl_popexec_new(newbuff);
[7d4fbcd]2029  } else {
[b7ee89b]2030    p = popen(newbuff, "r");
2031    out = owl_slurp(p);
[afbf668]2032    pclose(p);
2033   
[2cfc6d7]2034    if (type == OWL_OUTPUT_RETURN) {
[ddbbcffa]2035      g_free(newbuff);
[afbf668]2036      return out;
[7ba9e0de]2037    } else if (type == OWL_OUTPUT_ADMINMSG) {
[afbf668]2038      owl_function_adminmsg(buff, out);
2039    }
[ddbbcffa]2040    g_free(out);
[7d4fbcd]2041  }
[ddbbcffa]2042  g_free(newbuff);
[7d4fbcd]2043  return NULL;
2044}
2045
[6829afc]2046CALLER_OWN char *owl_function_perl(int argc, const char *const *argv, const char *buff, int type)
[d54838d]2047{
[7d4fbcd]2048  /* if type == 1 display in a popup
2049   * if type == 2 display an admin messages
2050   * if type == 0 return output
2051   * else display in a popup
2052   */
2053  char *perlout;
2054
2055  if (argc<2) {
[ec6ff52]2056    owl_function_error("Wrong number of arguments to perl command");
[7d4fbcd]2057    return NULL;
2058  }
2059
2060  /* consume first token (argv[0]) */
2061  buff = skiptokens(buff, 1);
2062
[f1e629d]2063  perlout = owl_perlconfig_execute(buff);
[7d4fbcd]2064  if (perlout) { 
[7ba9e0de]2065    if (type == OWL_OUTPUT_POPUP) {
[7d4fbcd]2066      owl_function_popless_text(perlout);
[7ba9e0de]2067    } else if (type == OWL_OUTPUT_ADMINMSG) {
[7d4fbcd]2068      owl_function_adminmsg(buff, perlout);
[7ba9e0de]2069    } else if (type == OWL_OUTPUT_RETURN) {
[7d4fbcd]2070      return perlout;
2071    }
[ddbbcffa]2072    g_free(perlout);
[7d4fbcd]2073  }
2074  return NULL;
2075}
2076
[5e0b690]2077/* Change the filter associated with the current view.
2078 * This also figures out which message in the new filter
2079 * should have the pointer.
2080 */
[e19eb97]2081void owl_function_change_currentview_filter(const char *filtname)
[c3ab155]2082{
2083  owl_view *v;
2084  owl_filter *f;
2085  int curid=-1, newpos, curmsg;
[c08c70a]2086  const owl_message *curm=NULL;
[c3ab155]2087
2088  v=owl_global_get_current_view(&g);
2089
2090  curmsg=owl_global_get_curmsg(&g);
2091  if (curmsg==-1) {
2092    owl_function_debugmsg("Hit the curmsg==-1 case in change_view");
2093  } else {
2094    curm=owl_view_get_element(v, curmsg);
2095    if (curm) {
2096      curid=owl_message_get_id(curm);
2097      owl_view_save_curmsgid(v, curid);
2098    }
2099  }
2100
2101  f=owl_global_get_filter(&g, filtname);
2102  if (!f) {
[ec6ff52]2103    owl_function_error("Unknown filter %s", filtname);
[c3ab155]2104    return;
2105  }
2106
2107  owl_view_new_filter(v, f);
2108
2109  /* Figure out what to set the current message to.
2110   * - If the view we're leaving has messages in it, go to the closest message
2111   *   to the last message pointed to in that view.
2112   * - If the view we're leaving is empty, try to restore the position
2113   *   from the last time we were in the new view.  */
2114  if (curm) {
2115    newpos = owl_view_get_nearest_to_msgid(v, curid);
2116  } else {
2117    newpos = owl_view_get_nearest_to_saved(v);
2118  }
2119
2120  owl_global_set_curmsg(&g, newpos);
2121  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
2122  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2123  owl_global_set_direction_downwards(&g);
2124}
[7d4fbcd]2125
[5e0b690]2126/* Create a new filter, or replace an existing one
[287c634]2127 * with a new definition. Returns true on success.
[5e0b690]2128 */
[287c634]2129bool owl_function_create_filter(int argc, const char *const *argv)
[d54838d]2130{
[7d4fbcd]2131  owl_filter *f;
[9e5c9f3]2132  const owl_view *v;
[23fddad]2133  int inuse = 0;
[7d4fbcd]2134
2135  if (argc < 2) {
[ec6ff52]2136    owl_function_error("Wrong number of arguments to filter command");
[287c634]2137    return false;
[7d4fbcd]2138  }
2139
[3895e23]2140  owl_function_debugmsg("owl_function_create_filter: starting to create filter named %s", argv[1]);
2141
[7d4fbcd]2142  v=owl_global_get_current_view(&g);
2143
2144  /* don't touch the all filter */
2145  if (!strcmp(argv[1], "all")) {
[ec6ff52]2146    owl_function_error("You may not change the 'all' filter.");
[287c634]2147    return false;
[7d4fbcd]2148  }
2149
2150  /* deal with the case of trying change the filter color */
2151  if (argc==4 && !strcmp(argv[2], "-c")) {
2152    f=owl_global_get_filter(&g, argv[1]);
2153    if (!f) {
[ec6ff52]2154      owl_function_error("The filter '%s' does not exist.", argv[1]);
[287c634]2155      return false;
[7d4fbcd]2156    }
[601733d]2157    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
[12c35df]2158      owl_function_error("The color '%s' is not available.", argv[3]);
[287c634]2159      return false;
[12c35df]2160    }
[8fa9562]2161    owl_filter_set_fgcolor(f, owl_util_string_to_color(argv[3]));
2162    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
[287c634]2163    return false;
[8fa9562]2164  }
2165  if (argc==4 && !strcmp(argv[2], "-b")) {
2166    f=owl_global_get_filter(&g, argv[1]);
2167    if (!f) {
2168      owl_function_error("The filter '%s' does not exist.", argv[1]);
[287c634]2169      return false;
[8fa9562]2170    }
[601733d]2171    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
[8fa9562]2172      owl_function_error("The color '%s' is not available.", argv[3]);
[287c634]2173      return false;
[8fa9562]2174    }
2175    owl_filter_set_bgcolor(f, owl_util_string_to_color(argv[3]));
[7d4fbcd]2176    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
[287c634]2177    return true;
[7d4fbcd]2178  }
2179
2180  /* create the filter and check for errors */
[23fddad]2181  f = owl_filter_new(argv[1], argc-2, argv+2);
2182  if (f == NULL) {
[40458b9]2183    owl_function_error("Invalid filter");
[287c634]2184    return false;
[7d4fbcd]2185  }
2186
2187  /* if the named filter is in use by the current view, remember it */
2188  if (!strcmp(owl_view_get_filtname(v), argv[1])) {
2189    inuse=1;
2190  }
2191
2192  /* if the named filter already exists, nuke it */
2193  if (owl_global_get_filter(&g, argv[1])) {
2194    owl_global_remove_filter(&g, argv[1]);
2195  }
2196
2197  /* add the filter */
2198  owl_global_add_filter(&g, f);
2199
2200  /* if it was in use by the current view then update */
2201  if (inuse) {
[3895e23]2202    owl_function_change_currentview_filter(argv[1]);
[7d4fbcd]2203  }
2204  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
[287c634]2205  return true;
[7d4fbcd]2206}
2207
[3895e23]2208/* If 'filtername' does not start with 'not-' create a filter named
2209 * 'not-<filtername>' defined as "not filter <filtername>".  If the
2210 * filter 'not-<filtername>' already exists, do not overwrite it.  If
2211 * 'filtername' begins with 'not-' and a filter 'filtername' already
2212 * exists, then do nothing.  If the filter 'filtername' does not
2213 * exist, create it and define it as 'not filter <filtername>'
2214 *
2215 * Returns the name of the negated filter, which the caller must free.
2216 */
[6829afc]2217CALLER_OWN char *owl_function_create_negative_filter(const char *filtername)
[3895e23]2218{
2219  char *newname;
[4542047]2220  const owl_filter *tmpfilt;
[e19eb97]2221  const char *argv[5];
[3895e23]2222
2223  owl_function_debugmsg("owl_function_create_negative_filter");
2224 
2225  if (!strncmp(filtername, "not-", 4)) {
[d4927a7]2226    newname=g_strdup(filtername+4);
[3895e23]2227  } else {
[3472845]2228    newname=g_strdup_printf("not-%s", filtername);
[3895e23]2229  }
2230
2231  tmpfilt=owl_global_get_filter(&g, newname);
2232  if (!tmpfilt) {
2233    argv[0]="filter"; /* anything is fine here */
2234    argv[1]=newname;
2235    argv[2]="not";
2236    argv[3]="filter";
2237    argv[4]=filtername;
2238    owl_function_create_filter(5, argv);
2239  }
2240
2241  owl_function_debugmsg("owl_function_create_negative_filter: returning with %s", newname);
2242  return(newname);
2243}
2244
[c79a047]2245void owl_function_show_filters(void)
[d54838d]2246{
[4542047]2247  const owl_filter *f;
[129e609]2248  GList *fl;
[7d4fbcd]2249  owl_fmtext fm;
2250
2251  owl_fmtext_init_null(&fm);
2252
2253  owl_fmtext_append_bold(&fm, "Filters:\n");
2254
[129e609]2255  for (fl = g.filterlist; fl; fl = g_list_next(fl)) {
2256    f = fl->data;
[7d4fbcd]2257    owl_fmtext_append_normal(&fm, "   ");
[7b4f3be]2258    owl_fmtext_append_normal_color(&fm, owl_filter_get_name(f),
2259                                   owl_filter_get_fgcolor(f),
2260                                   owl_filter_get_bgcolor(f));
[7d4fbcd]2261    owl_fmtext_append_normal(&fm, "\n");
2262  }
2263  owl_function_popless_fmtext(&fm);
[7ab0020]2264  owl_fmtext_cleanup(&fm);
[7d4fbcd]2265}
2266
[e19eb97]2267void owl_function_show_filter(const char *name)
[d54838d]2268{
[4542047]2269  const owl_filter *f;
[cdc6ff1]2270  char *buff, *tmp;
[7d4fbcd]2271
2272  f=owl_global_get_filter(&g, name);
2273  if (!f) {
[ec6ff52]2274    owl_function_error("There is no filter named %s", name);
[7d4fbcd]2275    return;
2276  }
[cdc6ff1]2277  tmp = owl_filter_print(f);
[3472845]2278  buff = g_strdup_printf("%s: %s", owl_filter_get_name(f), tmp);
[7d4fbcd]2279  owl_function_popless_text(buff);
[ddbbcffa]2280  g_free(buff);
2281  g_free(tmp);
[7d4fbcd]2282}
2283
[c79a047]2284void owl_function_show_zpunts(void)
[d54838d]2285{
[4542047]2286  const owl_filter *f;
[e6d7e4e]2287  const GPtrArray *fl;
[0504f63]2288  char *tmp;
[7d4fbcd]2289  owl_fmtext fm;
[e6d7e4e]2290  int i;
[7d4fbcd]2291
2292  owl_fmtext_init_null(&fm);
2293
2294  fl=owl_global_get_puntlist(&g);
2295  owl_fmtext_append_bold(&fm, "Active zpunt filters:\n");
2296
[e6d7e4e]2297  for (i = 0; i < fl->len; i++) {
2298    f = fl->pdata[i];
[e3a75ed]2299    owl_fmtext_appendf_normal(&fm, "[% 2d] ", i+1);
[0504f63]2300    tmp = owl_filter_print(f);
2301    owl_fmtext_append_normal(&fm, tmp);
[ddbbcffa]2302    g_free(tmp);
[7d4fbcd]2303  }
2304  owl_function_popless_fmtext(&fm);
[7ab0020]2305  owl_fmtext_cleanup(&fm);
[7d4fbcd]2306}
2307
[3abf28b]2308/* Create a filter for a class, instance if one doesn't exist.  If
2309 * instance is NULL then catch all messgaes in the class.  Returns the
[c7fe23e]2310 * name of the filter or null.  The caller must free this name.
[66e409c]2311 * If 'related' is nonzero, encompass unclasses and .d classes as well.
[3abf28b]2312 */
[6829afc]2313CALLER_OWN char *owl_function_classinstfilt(const char *c, const char *i, int related) 
[d54838d]2314{
[7d4fbcd]2315  owl_filter *f;
[c426bc2]2316  char *filtname;
[d54838d]2317  char *tmpclass, *tmpinstance = NULL;
[7a20e4c]2318  char *class, *instance = NULL;
[c426bc2]2319  GString *buf;
[7d4fbcd]2320
[66e409c]2321  if (related) {
2322    class = owl_util_baseclass(c);
2323    if (i) {
2324      instance = owl_util_baseclass(i);
2325    }
2326  } else {
[d4927a7]2327    class = g_strdup(c);
[66e409c]2328    if (i) {
[d4927a7]2329      instance = g_strdup(i);
[66e409c]2330    }
[7a20e4c]2331  }
2332
[7d4fbcd]2333  /* name for the filter */
2334  if (!instance) {
[3472845]2335    filtname = g_strdup_printf("%sclass-%s", related ? "related-" : "", class);
[7d4fbcd]2336  } else {
[3472845]2337    filtname = g_strdup_printf("%sclass-%s-instance-%s", related ? "related-" : "", class, instance);
[7d4fbcd]2338  }
[ed2412d]2339  /* downcase it */
[28ee32b]2340  {
2341    char *temp = g_utf8_strdown(filtname, -1);
2342    if (temp) {
[ddbbcffa]2343      g_free(filtname);
[28ee32b]2344      filtname = temp;
2345    }
2346  }
[ed2412d]2347 
[7d4fbcd]2348  /* if it already exists then go with it.  This lets users override */
2349  if (owl_global_get_filter(&g, filtname)) {
[ff426f9]2350    goto done;
[7d4fbcd]2351  }
2352
2353  /* create the new filter */
[995eb4b]2354  tmpclass=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
[ed2412d]2355  if (instance) {
[995eb4b]2356    tmpinstance=owl_text_quote(instance, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
[ed2412d]2357  }
[a0e6082]2358
[c426bc2]2359  buf = g_string_new("");
2360  owl_string_appendf_quoted(buf,
2361                            related ? "class ^(un)*%q(\\.d)*$" : "class ^%q$",
2362                            tmpclass);
2363
[d54838d]2364  if (tmpinstance) {
[c426bc2]2365    owl_string_appendf_quoted(buf,
2366                              related ?
2367                              " and ( instance ^(un)*%q(\\.d)*$ )" :
2368                              " and instance ^%q$",
2369                              tmpinstance);
[7d4fbcd]2370  }
[ddbbcffa]2371  g_free(tmpclass);
2372  g_free(tmpinstance);
[7d4fbcd]2373
[c426bc2]2374  f = owl_filter_new_fromstring(filtname, buf->str);
2375  g_string_free(buf, true);
[c7fe23e]2376  if (f == NULL) {
2377    /* Couldn't make a filter for some reason. Return NULL. */
2378    owl_function_error("Error creating filter '%s'", filtname);
[ddbbcffa]2379    g_free(filtname);
[c7fe23e]2380    filtname = NULL;
2381    goto done;
2382  }
[7d4fbcd]2383
2384  /* add it to the global list */
2385  owl_global_add_filter(&g, f);
2386
[ff426f9]2387done:
[ddbbcffa]2388  g_free(class);
[3b8a563]2389  g_free(instance);
[ed2412d]2390  return(filtname);
[7d4fbcd]2391}
2392
[3abf28b]2393/* Create a filter for personal zephyrs to or from the specified
2394 * zephyr user.  Includes login/logout notifications for the user.
[811644f]2395 * The name of the filter will be 'user-<shortuser>'.  If a filter already
[3abf28b]2396 * exists with this name, no new filter will be created.  This allows
2397 * the configuration to override this function.  Returns the name of
2398 * the filter, which the caller must free.
2399 */
[6829afc]2400CALLER_OWN char *owl_function_zuserfilt(const char *longuser)
[d54838d]2401{
[7d4fbcd]2402  owl_filter *f;
[811644f]2403  char *argbuff, *esclonguser, *shortuser, *filtname;
[7d4fbcd]2404
2405  /* name for the filter */
[811644f]2406  shortuser = short_zuser(longuser);
[3472845]2407  filtname = g_strdup_printf("user-%s", shortuser);
[ddbbcffa]2408  g_free(shortuser);
[7d4fbcd]2409
2410  /* if it already exists then go with it.  This lets users override */
2411  if (owl_global_get_filter(&g, filtname)) {
[6cc3306]2412    return filtname;
[7d4fbcd]2413  }
2414
2415  /* create the new-internal filter */
[1d12db24]2416  esclonguser = owl_text_quote(longuser, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2417
[a5f477c]2418  argbuff=owl_string_build_quoted("( type ^zephyr$ and filter personal and "
2419      "( ( direction ^in$ and sender ^%q$ ) or ( direction ^out$ and "
2420      "recipient ^%q$ ) ) ) or ( ( class ^login$ ) and ( sender ^%q$ ) )",
2421      esclonguser, esclonguser, esclonguser);
[ddbbcffa]2422  g_free(esclonguser);
[7d4fbcd]2423
[23fddad]2424  f = owl_filter_new_fromstring(filtname, argbuff);
[ddbbcffa]2425  g_free(argbuff);
[c7fe23e]2426
2427  if (f == NULL) {
2428    /* Couldn't make a filter for some reason. Return NULL. */
2429    owl_function_error("Error creating filter '%s'", filtname);
[ddbbcffa]2430    g_free(filtname);
[c7fe23e]2431    return NULL;
2432  }
[7d4fbcd]2433
2434  /* add it to the global list */
2435  owl_global_add_filter(&g, f);
2436
[ed2412d]2437  return(filtname);
[7d4fbcd]2438}
2439
[3abf28b]2440/* Create a filter for AIM IM messages to or from the specified
2441 * screenname.  The name of the filter will be 'aimuser-<user>'.  If a
2442 * filter already exists with this name, no new filter will be
2443 * created.  This allows the configuration to override this function.
2444 * Returns the name of the filter, which the caller must free.
2445 */
[6829afc]2446CALLER_OWN char *owl_function_aimuserfilt(const char *user)
[3abf28b]2447{
2448  owl_filter *f;
2449  char *argbuff, *filtname;
[af9b92e]2450  char *escuser;
[3abf28b]2451
2452  /* name for the filter */
[3472845]2453  filtname=g_strdup_printf("aimuser-%s", user);
[3abf28b]2454
2455  /* if it already exists then go with it.  This lets users override */
2456  if (owl_global_get_filter(&g, filtname)) {
[837bd81]2457    return filtname;
[3abf28b]2458  }
2459
2460  /* create the new-internal filter */
[af9b92e]2461  escuser = owl_text_quote(user, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2462
[3472845]2463  argbuff = g_strdup_printf(
[9a2ae6c]2464      "( type ^aim$ and ( ( sender ^%1$s$ and recipient ^%2$s$ ) or "
2465      "( sender ^%2$s$ and recipient ^%1$s$ ) ) )",
2466      escuser, owl_global_get_aim_screenname_for_filters(&g));
[ddbbcffa]2467  g_free(escuser);
[3abf28b]2468
[23fddad]2469  f = owl_filter_new_fromstring(filtname, argbuff);
[ddbbcffa]2470  g_free(argbuff);
[c7fe23e]2471
2472  if (f == NULL) {
2473    owl_function_error("Error creating filter '%s'", filtname);
[ddbbcffa]2474    g_free(filtname);
[c7fe23e]2475    return NULL;
2476  }
[3abf28b]2477
2478  /* add it to the global list */
2479  owl_global_add_filter(&g, f);
2480
2481  return(filtname);
2482}
2483
[6829afc]2484CALLER_OWN char *owl_function_typefilt(const char *type)
[d54838d]2485{
[f73e519]2486  owl_filter *f;
[1d12db24]2487  char *argbuff, *filtname, *esctype;
[f73e519]2488
2489  /* name for the filter */
[3472845]2490  filtname=g_strdup_printf("type-%s", type);
[f73e519]2491
2492  /* if it already exists then go with it.  This lets users override */
2493  if (owl_global_get_filter(&g, filtname)) {
2494    return filtname;
2495  }
2496
2497  /* create the new-internal filter */
[1d12db24]2498  esctype = owl_text_quote(type, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2499
[42115bf9]2500  argbuff = owl_string_build_quoted("type ^%q$", esctype);
[ddbbcffa]2501  g_free(esctype);
[f73e519]2502
[23fddad]2503  f = owl_filter_new_fromstring(filtname, argbuff);
[ddbbcffa]2504  g_free(argbuff);
[c7fe23e]2505
2506  if (f == NULL) {
2507    owl_function_error("Error creating filter '%s'", filtname);
[ddbbcffa]2508    g_free(filtname);
[c7fe23e]2509    return NULL;
2510  }
[f73e519]2511
2512  /* add it to the global list */
2513  owl_global_add_filter(&g, f);
2514
2515  return filtname;
2516}
2517
[7d4fbcd]2518/* If flag is 1, marks for deletion.  If flag is 0,
2519 * unmarks for deletion. */
[d54838d]2520void owl_function_delete_curview_msgs(int flag)
2521{
[9e5c9f3]2522  const owl_view *v;
[7d4fbcd]2523  int i, j;
2524
2525  v=owl_global_get_current_view(&g);
2526  j=owl_view_get_size(v);
2527  for (i=0; i<j; i++) {
2528    if (flag == 1) {
2529      owl_message_mark_delete(owl_view_get_element(v, i));
2530    } else if (flag == 0) {
2531      owl_message_unmark_delete(owl_view_get_element(v, i));
2532    }
2533  }
2534
2535  owl_function_makemsg("%i messages marked for %sdeletion", j, flag?"":"un");
2536
2537  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
2538}
2539
[6829afc]2540static CALLER_OWN char *owl_function_smartfilter_cc(const owl_message *m)
[d427f08]2541{
[ecaec21]2542  const char *ccs;
[d222c44]2543  char *ccs_quoted;
[ecaec21]2544  char *filtname;
2545  owl_filter *f;
[d222c44]2546  GString *buf;
[ecaec21]2547
2548  ccs = owl_message_get_attribute_value(m, "zephyr_ccs");
2549
[3472845]2550  filtname = g_strdup_printf("conversation-%s", ccs);
[7865479]2551  g_strdelimit(filtname, " ", '-');
[ecaec21]2552
2553  if (owl_global_get_filter(&g, filtname)) {
2554    return filtname;
2555  }
2556
[d222c44]2557  buf = g_string_new("type ^zephyr$ and filter personal and "
2558                     "zephyr_ccs ^");
2559  ccs_quoted = owl_text_quote(ccs, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2560  owl_string_append_quoted_arg(buf, ccs_quoted);
2561  g_string_append_c(buf, '$');
[ddbbcffa]2562  g_free(ccs_quoted);
[ecaec21]2563
[d222c44]2564  f = owl_filter_new_fromstring(filtname, buf->str);
2565  g_string_free(buf, true);
[ecaec21]2566
[c7fe23e]2567  if (f == NULL) {
2568    owl_function_error("Error creating filter '%s'", filtname);
[ddbbcffa]2569    g_free(filtname);
[c7fe23e]2570    return NULL;
2571  }
2572
[ecaec21]2573  owl_global_add_filter(&g, f);
2574
2575  return filtname;
2576}
2577
[3abf28b]2578/* Create a filter based on the current message.  Returns the name of
2579 * a filter or null.  The caller must free this name.
2580 *
2581 * if the curmsg is a personal zephyr return a filter name
[e6d989f]2582 *    to the zephyr conversation with that user.
[3abf28b]2583 * If the curmsg is a zephyr class message, instance foo, recip *,
2584 *    return a filter name to the class, inst.
2585 * If the curmsg is a zephyr class message and type==0 then
2586 *    return a filter name for just the class.
2587 * If the curmsg is a zephyr class message and type==1 then
2588 *    return a filter name for the class and instance.
2589 * If the curmsg is a personal AIM message returna  filter
2590 *    name to the AIM conversation with that user
2591 */
[6829afc]2592CALLER_OWN char *owl_function_smartfilter(int type, int invert_related)
[d54838d]2593{
[9e5c9f3]2594  const owl_view *v;
[c08c70a]2595  const owl_message *m;
[811644f]2596  char *filtname = NULL;
2597  const char *argv[2], *zperson;
[8a5b5a1]2598  int related = owl_global_is_narrow_related(&g) ^ invert_related;
2599
[7d4fbcd]2600  v=owl_global_get_current_view(&g);
2601  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2602
[5eeea3b]2603  if (!m || owl_view_get_size(v)==0) {
[ec6ff52]2604    owl_function_error("No message selected\n");
[4b464a4]2605    return(NULL);
[7d4fbcd]2606  }
2607
[f73e519]2608  /* very simple handling of admin messages for now */
[4b464a4]2609  if (owl_message_is_type_admin(m)) {
[3abf28b]2610    return(owl_function_typefilt("admin"));
2611  }
2612
[995eb4b]2613  /* very simple handling of loopback messages for now */
2614  if (owl_message_is_type_loopback(m)) {
2615    return(owl_function_typefilt("loopback"));
2616  }
2617
[3abf28b]2618  /* aim messages */
2619  if (owl_message_is_type_aim(m)) {
2620    if (owl_message_is_direction_in(m)) {
2621      filtname=owl_function_aimuserfilt(owl_message_get_sender(m));
2622    } else if (owl_message_is_direction_out(m)) {
2623      filtname=owl_function_aimuserfilt(owl_message_get_recipient(m));
2624    }
2625    return(filtname);
[7d4fbcd]2626  }
2627
[4b464a4]2628  /* narrow personal and login messages to the sender or recip as appropriate */
[25729b2]2629  if (owl_message_is_type_zephyr(m)) {
[0ef0e8f]2630    if (owl_message_is_personal(m) || owl_message_is_loginout(m)) {
[ecaec21]2631      if (owl_message_get_attribute_value(m, "zephyr_ccs") != NULL) {
2632        return owl_function_smartfilter_cc(m);
2633      }
2634
[4b464a4]2635      if (owl_message_is_direction_in(m)) {
[811644f]2636        zperson = owl_message_get_sender(m);
[4b464a4]2637      } else {
[811644f]2638        zperson = owl_message_get_recipient(m);
[4b464a4]2639      }
[811644f]2640      filtname = owl_function_zuserfilt(zperson);
2641      return filtname;
[7d4fbcd]2642    }
2643
[25729b2]2644    /* narrow class MESSAGE, instance foo, recip * messages to class, inst */
[ce74deb]2645    if (!strcasecmp(owl_message_get_class(m), "message")) {
[66e409c]2646      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
[25729b2]2647      return(filtname);
2648    }
2649
2650    /* otherwise narrow to the class */
2651    if (type==0) {
[66e409c]2652      filtname=owl_function_classinstfilt(owl_message_get_class(m), NULL, related);
[25729b2]2653    } else if (type==1) {
[66e409c]2654      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
[25729b2]2655    }
[4b464a4]2656    return(filtname);
[7d4fbcd]2657  }
2658
[25729b2]2659  /* pass it off to perl */
[66e409c]2660  argv[0] = type ? "1" : "0";
2661  argv[1] = related ? "1" : "0";
2662  return owl_perlconfig_message_call_method(m, "smartfilter", 2, argv);
[7d4fbcd]2663}
2664
[d54838d]2665void owl_function_smartzpunt(int type)
2666{
[d36f2cb]2667  /* Starts a zpunt command based on the current class,instance pair.
2668   * If type=0, uses just class.  If type=1, uses instance as well. */
[9e5c9f3]2669  const owl_view *v;
[c08c70a]2670  const owl_message *m;
[d222c44]2671  const char *mclass, *minst;
2672  GString *buf;
[d36f2cb]2673 
2674  v=owl_global_get_current_view(&g);
2675  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2676
[5eeea3b]2677  if (!m || owl_view_get_size(v)==0) {
[ec6ff52]2678    owl_function_error("No message selected\n");
[d36f2cb]2679    return;
2680  }
2681
2682  /* for now we skip admin messages. */
[4b464a4]2683  if (owl_message_is_type_admin(m)
[5789230]2684      || owl_message_is_loginout(m)
[4b464a4]2685      || !owl_message_is_type_zephyr(m)) {
[ec6ff52]2686    owl_function_error("smartzpunt doesn't support this message type.");
[d36f2cb]2687    return;
2688  }
2689
[cee1f25]2690  mclass = owl_message_get_class(m);
2691  minst = owl_message_get_instance(m);
[d36f2cb]2692  if (!mclass || !*mclass || *mclass==' '
2693      || (!strcasecmp(mclass, "message") && !strcasecmp(minst, "personal"))
2694      || (type && (!minst || !*minst|| *minst==' '))) {
[ec6ff52]2695    owl_function_error("smartzpunt can't safely do this for <%s,%s>",
[d36f2cb]2696                         mclass, minst);
2697  } else {
[d222c44]2698    buf = g_string_new("start-command zpunt ");
2699    owl_string_append_quoted_arg(buf, mclass);
[d36f2cb]2700    if (type) {
[d222c44]2701      g_string_append_c(buf, ' ');
2702      owl_string_append_quoted_arg(buf, minst);
[d36f2cb]2703    } else {
[d222c44]2704      g_string_append(buf, " *");
[d36f2cb]2705    }
[c809f5e]2706    owl_function_command_norv(buf->str);
[d222c44]2707    g_string_free(buf, true);
[d36f2cb]2708  }
2709}
2710
[5e0b690]2711/* Set the color of the current view's filter to
2712 * be 'color'
2713 */
[e19eb97]2714void owl_function_color_current_filter(const char *fgcolor, const char *bgcolor)
[d54838d]2715{
[e19eb97]2716  const char *name;
[7d4fbcd]2717
2718  name=owl_view_get_filtname(owl_global_get_current_view(&g));
[8fa9562]2719  owl_function_color_filter(name, fgcolor, bgcolor);
[5e0b690]2720}
2721
2722/* Set the color of the filter 'filter' to be 'color'.  If the color
2723 * name does not exist, return -1, if the filter does not exist or is
2724 * the "all" filter, return -2.  Return 0 on success
2725 */
[e19eb97]2726int owl_function_color_filter(const char *filtname, const char *fgcolor, const char *bgcolor)
[5e0b690]2727{
2728  owl_filter *f;
2729
2730  f=owl_global_get_filter(&g, filtname);
[7d4fbcd]2731  if (!f) {
[ec6ff52]2732    owl_function_error("Unknown filter");
[5e0b690]2733    return(-2);
[7d4fbcd]2734  }
2735
2736  /* don't touch the all filter */
[5e0b690]2737  if (!strcmp(filtname, "all")) {
[ec6ff52]2738    owl_function_error("You may not change the 'all' filter.");
[5e0b690]2739    return(-2);
[7d4fbcd]2740  }
2741
[601733d]2742  if (owl_util_string_to_color(fgcolor)==OWL_COLOR_INVALID) {
[8fa9562]2743    owl_function_error("No color named '%s' avilable.", fgcolor);
[5e0b690]2744    return(-1);
[12c35df]2745  }
[8fa9562]2746
2747
2748  if (bgcolor != NULL) {
[601733d]2749    if (owl_util_string_to_color(bgcolor)==OWL_COLOR_INVALID) {
[8fa9562]2750      owl_function_error("No color named '%s' avilable.", bgcolor);
2751      return(-1);
2752    }
2753    owl_filter_set_bgcolor(f, owl_util_string_to_color(bgcolor));
2754  }
2755  owl_filter_set_fgcolor(f, owl_util_string_to_color(fgcolor));
2756 
[7d4fbcd]2757  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
[5e0b690]2758  return(0);
[7d4fbcd]2759}
2760
[c79a047]2761void owl_function_show_colors(void)
[d54838d]2762{
[7d4fbcd]2763  owl_fmtext fm;
[c2c5c77]2764  int i; 
2765 
[7d4fbcd]2766  owl_fmtext_init_null(&fm);
[82b734a]2767  owl_fmtext_append_normal(&fm,"default:  ");
[8fa9562]2768  owl_fmtext_append_normal_color(&fm, "default\n", OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
[ca9142e]2769
[82b734a]2770  owl_fmtext_append_normal(&fm,"black:    ");
2771  owl_fmtext_append_normal_color(&fm, "black\n", OWL_COLOR_BLACK, OWL_COLOR_DEFAULT);
2772
[ca9142e]2773  owl_fmtext_append_normal(&fm,"red:      ");
[8fa9562]2774  owl_fmtext_append_normal_color(&fm, "red\n", OWL_COLOR_RED, OWL_COLOR_DEFAULT);
[ca9142e]2775
2776  owl_fmtext_append_normal(&fm,"green:    ");
[8fa9562]2777  owl_fmtext_append_normal_color(&fm, "green\n", OWL_COLOR_GREEN, OWL_COLOR_DEFAULT);
[ca9142e]2778
2779  owl_fmtext_append_normal(&fm,"yellow:   ");
[8fa9562]2780  owl_fmtext_append_normal_color(&fm, "yellow\n", OWL_COLOR_YELLOW, OWL_COLOR_DEFAULT);
[ca9142e]2781
2782  owl_fmtext_append_normal(&fm,"blue:     ");
[8fa9562]2783  owl_fmtext_append_normal_color(&fm, "blue\n", OWL_COLOR_BLUE, OWL_COLOR_DEFAULT);
[ca9142e]2784
2785  owl_fmtext_append_normal(&fm,"magenta:  ");
[8fa9562]2786  owl_fmtext_append_normal_color(&fm, "magenta\n", OWL_COLOR_MAGENTA, OWL_COLOR_DEFAULT);
[ca9142e]2787
2788  owl_fmtext_append_normal(&fm,"cyan:     ");
[8fa9562]2789  owl_fmtext_append_normal_color(&fm, "cyan\n", OWL_COLOR_CYAN, OWL_COLOR_DEFAULT);
[ca9142e]2790
2791  owl_fmtext_append_normal(&fm,"white:    ");
[8fa9562]2792  owl_fmtext_append_normal_color(&fm, "white\n", OWL_COLOR_WHITE, OWL_COLOR_DEFAULT);
[7d4fbcd]2793
[c2c5c77]2794  for(i = 8; i < COLORS; ++i) {
[3472845]2795    char* str1 = g_strdup_printf("%4i:     ",i);
2796    char* str2 = g_strdup_printf("%i\n",i);
[c2c5c77]2797    owl_fmtext_append_normal(&fm,str1);
2798    owl_fmtext_append_normal_color(&fm, str2, i, OWL_COLOR_DEFAULT);
[ddbbcffa]2799    g_free(str1);
2800     g_free(str2);
[c2c5c77]2801  }
2802 
[7d4fbcd]2803  owl_function_popless_fmtext(&fm);
[7ab0020]2804  owl_fmtext_cleanup(&fm);
[7d4fbcd]2805}
2806
[5bb6c21]2807/* add the given class, inst, recip to the punt list for filtering.
2808 *   if direction==0 then punt
2809 *   if direction==1 then unpunt
2810 */
[e19eb97]2811void owl_function_zpunt(const char *class, const char *inst, const char *recip, int direction)
[d54838d]2812{
[78f6c35]2813  GPtrArray *argv;
[bc08664]2814  char *quoted;
[7d4fbcd]2815
[78f6c35]2816  argv = g_ptr_array_new();
[5bb6c21]2817  if (!strcmp(class, "*")) {
[78f6c35]2818    g_ptr_array_add(argv, g_strdup("class"));
2819    g_ptr_array_add(argv, g_strdup(".*"));
[5bb6c21]2820  } else {
[bc08664]2821    quoted=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
[78f6c35]2822    g_ptr_array_add(argv, g_strdup("class"));
2823    g_ptr_array_add(argv, g_strdup_printf("^(un)*%s(\\.d)*$", quoted));
[ddbbcffa]2824    g_free(quoted);
[5bb6c21]2825  }
2826  if (!strcmp(inst, "*")) {
[78f6c35]2827    g_ptr_array_add(argv, g_strdup("and"));
2828    g_ptr_array_add(argv, g_strdup("instance"));
2829    g_ptr_array_add(argv, g_strdup(".*"));
[7d4fbcd]2830  } else {
[bc08664]2831    quoted=owl_text_quote(inst, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
[78f6c35]2832    g_ptr_array_add(argv, g_strdup("and"));
2833    g_ptr_array_add(argv, g_strdup("instance"));
2834    g_ptr_array_add(argv, g_strdup_printf("^(un)*%s(\\.d)*$", quoted));
[ddbbcffa]2835    g_free(quoted);
[7d4fbcd]2836  }
[c894c15]2837  if (!strcmp(recip, "*")) {
[78f6c35]2838    /* nothing */
[c894c15]2839  } else {
[e6c59ba]2840    if(!strcmp(recip, "%me%")) {
2841      recip = owl_zephyr_get_sender();
2842    }
[bc08664]2843    quoted=owl_text_quote(recip, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
[78f6c35]2844    g_ptr_array_add(argv, g_strdup("and"));
2845    g_ptr_array_add(argv, g_strdup("recipient"));
2846    g_ptr_array_add(argv, g_strdup_printf("^%s$", quoted));
[ddbbcffa]2847    g_free(quoted);
[5bb6c21]2848  }
[ce7b824]2849
[78f6c35]2850  owl_function_punt(argv->len, (const char *const*) argv->pdata, direction);
[3cdd6d2]2851  owl_ptr_array_free(argv, g_free);
[ce7b824]2852}
2853
[78f6c35]2854void owl_function_punt(int argc, const char *const *argv, int direction)
[ce7b824]2855{
2856  owl_filter *f;
[e6d7e4e]2857  GPtrArray *fl;
2858  int i;
[ce7b824]2859  fl=owl_global_get_puntlist(&g);
2860
2861  /* first, create the filter */
[78f6c35]2862  f = owl_filter_new("punt-filter", argc, argv);
[23fddad]2863  if (f == NULL) {
[ec6ff52]2864    owl_function_error("Error creating filter for zpunt");
[7d4fbcd]2865    return;
2866  }
2867
2868  /* Check for an identical filter */
[e6d7e4e]2869  for (i = 0; i < fl->len; i++) {
2870    if (owl_filter_equiv(f, fl->pdata[i])) {
[ce7b824]2871      owl_function_debugmsg("found an equivalent punt filter");
[7d4fbcd]2872      /* if we're punting, then just silently bow out on this duplicate */
2873      if (direction==0) {
[23fddad]2874        owl_filter_delete(f);
[7d4fbcd]2875        return;
2876      }
2877
2878      /* if we're unpunting, then remove this filter from the puntlist */
2879      if (direction==1) {
[e6d7e4e]2880        owl_filter_delete(g_ptr_array_remove_index(fl, i));
[23fddad]2881        owl_filter_delete(f);
[7d4fbcd]2882        return;
2883      }
2884    }
2885  }
2886