source: functions.c @ 6476c0e

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